ad6e250319e7b5b457c8a400d2ab0278f728a47c
[mdsal.git] / binding / mdsal-binding-java-api-generator / src / test / java / org / opendaylight / mdsal / binding / java / api / generator / test / CompilationTestUtils.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.binding.java.api.generator.test;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12 import static org.junit.Assert.assertTrue;
13 import static org.junit.Assert.fail;
14
15 import java.io.File;
16 import java.io.FileNotFoundException;
17 import java.lang.reflect.Constructor;
18 import java.lang.reflect.Field;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Modifier;
22 import java.lang.reflect.ParameterizedType;
23 import java.net.URI;
24 import java.net.URISyntaxException;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28 import javax.tools.JavaCompiler;
29 import javax.tools.JavaFileObject;
30 import javax.tools.StandardJavaFileManager;
31 import javax.tools.ToolProvider;
32
33 public class CompilationTestUtils {
34     public static final String FS = File.separator;
35     static final String BASE_PKG = "org.opendaylight.yang.gen.v1";
36
37     static final String TEST_PATH = "target" + FS + "test";
38     static final File TEST_DIR = new File(TEST_PATH);
39
40     static final String GENERATOR_OUTPUT_PATH = TEST_PATH + FS + "src";
41     static final File GENERATOR_OUTPUT_DIR = new File(GENERATOR_OUTPUT_PATH);
42     static final String COMPILER_OUTPUT_PATH = TEST_PATH + FS + "bin";
43     static final File COMPILER_OUTPUT_DIR = new File(COMPILER_OUTPUT_PATH);
44
45     static final String AUGMENTATION = "interface org.opendaylight.yangtools.yang.binding.Augmentation";
46     static final String BASE_PATH = "org" + FS + "opendaylight" + FS + "yang" + FS + "gen" + FS + "v1";
47     static final String NS_TEST = BASE_PATH + FS + "urn" + FS + "opendaylight" + FS + "test" + FS + "rev131008";
48     static final String NS_FOO = BASE_PATH + FS + "urn" + FS + "opendaylight" + FS + "foo" + FS + "rev131008";
49     static final String NS_BAR = BASE_PATH + FS + "urn" + FS + "opendaylight" + FS + "bar" + FS + "rev131008";
50     static final String NS_BAZ = BASE_PATH + FS + "urn" + FS + "opendaylight" + FS + "baz" + FS + "rev131008";
51     static final String NS_BUG5882 = BASE_PATH + FS + "urn" + FS + "yang" + FS + "foo" + FS + "rev160102";
52
53     /**
54      * Method to clean resources. It is manually called at the end of each test
55      * instead of marking it with @After annotation to prevent removing
56      * generated code if test fails.
57      */
58     static void cleanUp(final File... resourceDirs) {
59         for (File resourceDir : resourceDirs) {
60             if (resourceDir.exists()) {
61                 deleteTestDir(resourceDir);
62             }
63         }
64     }
65
66     /**
67      * Asserts that class contains field with fiven name and type.
68      *
69      * @param clazz
70      *            class to test
71      * @param name
72      *            field name
73      * @param type
74      *            field type
75      * @return field with given name if present in class
76      */
77     static Field assertContainsField(final Class<?> clazz, final String name, final Class<?> type) {
78         try {
79             Field f = clazz.getDeclaredField(name);
80             assertEquals(type, f.getType());
81             return f;
82         } catch (NoSuchFieldException e) {
83             throw new AssertionError("Field " + name + " does not exist in class " + clazz.getSimpleName());
84         }
85     }
86
87     /**
88      * Asserts that class contains field with given name and value. Method tries
89      * to create new instance of class and get value of field. If class
90      * constructor contains any arguments, class is instantiated with null
91      * values.
92      *
93      * @param clazz
94      *            class to test
95      * @param name
96      *            name of field
97      * @param returnType
98      *            return type of field
99      * @param expectedValue
100      *            expected value of field
101      * @param constructorArgs
102      *            constructor arguments of class to test
103      */
104     static void assertContainsFieldWithValue(final Class<?> clazz, final String name, final Class<?> returnType, final Object expectedValue,
105             final Class<?>... constructorArgs) {
106         Object[] initargs = null;
107         if (constructorArgs != null && constructorArgs.length > 0) {
108             initargs = new Object[constructorArgs.length];
109             for (int i = 0; i < constructorArgs.length; i++) {
110                 initargs[i] = null;
111             }
112         }
113         assertContainsFieldWithValue(clazz, name, returnType, expectedValue, constructorArgs, initargs);
114     }
115
116     /**
117      * Asserts that class contains field with given name, return type and value.
118      *
119      * @param clazz
120      *            class to test
121      * @param name
122      *            name of field
123      * @param returnType
124      *            return type of field
125      * @param expectedValue
126      *            expected value of field
127      * @param constructorArgs
128      *            array of constructor arguments classes
129      * @param initargs
130      *            array of constructor values
131      */
132     static void assertContainsFieldWithValue(final Class<?> clazz, final String name, final Class<?> returnType, final Object expectedValue,
133             final Class<?>[] constructorArgs, final Object... initargs) {
134         Field f = assertContainsField(clazz, name, returnType);
135         f.setAccessible(true);
136
137         final Object obj;
138         if ((f.getModifiers() & Modifier.STATIC) == 0) {
139             try {
140                 Constructor<?> c = clazz.getDeclaredConstructor(constructorArgs);
141                 obj = c.newInstance(initargs);
142             } catch (Exception e) {
143                 throw new AssertionError("Failed to instantiate object for " + clazz, e);
144             }
145         } else {
146             obj = null;
147         }
148
149         try {
150             assertEquals(expectedValue, f.get(obj));
151         } catch (IllegalArgumentException | IllegalAccessException e) {
152             throw new AssertionError("Failed to get field " + name + " of class " + clazz, e);
153         }
154     }
155
156     /**
157      * Asserts that class contains constructor with parameter types.
158      *
159      * @param clazz
160      *            class to test
161      * @param args
162      *            array of argument classes
163      */
164     static Constructor<?> assertContainsConstructor(final Class<?> clazz, final Class<?>... args) {
165         try {
166             return clazz.getDeclaredConstructor(args);
167         } catch (NoSuchMethodException e) {
168             throw new AssertionError("Constructor with args " + Arrays.toString(args) + " does not exists in class "
169                     + clazz.getSimpleName());
170         }
171     }
172
173     /**
174      * Asserts that class contains method with given name, return type and
175      * parameter types.
176      *
177      * @param clazz
178      *            class to test
179      * @param returnType
180      *            method return type
181      * @param name
182      *            method name
183      * @param args
184      *            array of parameter type classes
185      * @return method with given name, return type and parameter types
186      */
187     static Method assertContainsMethod(final Class<?> clazz, final Class<?> returnType, final String name, final Class<?>... args) {
188         try {
189             Method m = clazz.getDeclaredMethod(name, args);
190             assertEquals(returnType, m.getReturnType());
191             return m;
192         } catch (NoSuchMethodException e) {
193             throw new AssertionError("Method " + name + " with args " + Arrays.toString(args)
194                     + " does not exists in class " + clazz.getSimpleName());
195         }
196     }
197
198     /**
199      * Asserts that class contains method with given name and return type.
200      *
201      * @param clazz
202      *            class to test
203      * @param returnTypeStr
204      *            name of method return type
205      * @param name
206      *            method name
207      * @param loader
208      *            current class loader
209      */
210     static void assertContainsMethod(final Class<?> clazz, final String returnTypeStr, final String name, final ClassLoader loader) {
211         Class<?> returnType;
212         try {
213             returnType = Class.forName(returnTypeStr, true, loader);
214             Method method = clazz.getMethod(name);
215             assertEquals(returnType, method.getReturnType());
216         } catch (ClassNotFoundException e) {
217             throw new AssertionError("Return type of method '" + name + "' not found");
218         } catch (NoSuchMethodException e) {
219             throw new AssertionError("Method " + name + " does not exists in class " + clazz.getSimpleName());
220         }
221     }
222
223     /**
224      * Asserts that class contains hashCode, equals and toString methods.
225      *
226      * @param clazz
227      *            class to test
228      */
229     static void assertContainsDefaultMethods(final Class<?> clazz) {
230         assertContainsMethod(clazz, Integer.TYPE, "hashCode");
231         assertContainsMethod(clazz, Boolean.TYPE, "equals", Object.class);
232         assertContainsMethod(clazz, String.class, "toString");
233     }
234
235     /**
236      * Asserts that constructor contains check for illegal argument.
237      *
238      * @param constructor
239      *            constructor to invoke
240      * @param errorMsg
241      *            expected error message
242      * @param args
243      *            constructor arguments
244      * @throws Exception
245      */
246     static void assertContainsRestrictionCheck(final Constructor<?> constructor, final String errorMsg, final Object... args)
247             throws Exception {
248         try {
249             constructor.newInstance(args);
250             fail("constructor invocation should fail");
251         } catch (InvocationTargetException e) {
252             Throwable cause = e.getCause();
253             assertTrue(cause instanceof IllegalArgumentException);
254             assertEquals(errorMsg, cause.getMessage());
255         }
256     }
257
258     /**
259      * Asserts that method contains check for illegal argument.
260      *
261      * @param obj
262      *            object to test (can be null, if method is static)
263      * @param method
264      *            method to invoke
265      * @param errorMsg
266      *            expected error message
267      * @param args
268      *            constructor arguments
269      * @throws Exception
270      */
271     static void assertContainsRestrictionCheck(final Object obj, final Method method, final String errorMsg, final Object... args)
272             throws Exception {
273         try {
274             method.invoke(obj, args);
275             fail("method invocation should fail");
276         } catch (InvocationTargetException e) {
277             Throwable cause = e.getCause();
278             assertTrue(cause instanceof IllegalArgumentException);
279             assertEquals(errorMsg, cause.getMessage());
280         }
281     }
282
283     /**
284      * Asserts that class implements given interface.
285      *
286      * @param clazz
287      *            source to test
288      * @param ifc
289      *            expected interface
290      */
291     static void assertImplementsIfc(final Class<?> clazz, final Class<?> ifc) {
292         Class<?>[] interfaces = clazz.getInterfaces();
293         List<Class<?>> ifcsList = Arrays.asList(interfaces);
294         if (!ifcsList.contains(ifc)) {
295             throw new AssertionError(clazz + " should implement " + ifc);
296         }
297     }
298
299     /**
300      * Test if interface generated from augment extends Augmentation interface
301      * with correct generic type.
302      *
303      * @param clazz
304      *            interface generated from augment
305      * @param genericTypeName
306      *            fully qualified name of expected parameter type
307      */
308     static void testAugmentation(final Class<?> clazz, final String genericTypeName) {
309         assertImplementsParameterizedIfc(clazz, AUGMENTATION, genericTypeName);
310     }
311
312     /**
313      * Asserts that class implements interface with given name and generic type
314      * parameter.
315      *
316      * @param clazz
317      *            class to test
318      * @param ifcName
319      *            name of interface
320      * @param genericTypeName
321      *            name of generic type
322      */
323     static void assertImplementsParameterizedIfc(final Class<?> clazz, final String ifcName, final String genericTypeName) {
324         ParameterizedType ifcType = null;
325         for (java.lang.reflect.Type ifc : clazz.getGenericInterfaces()) {
326             if (ifc instanceof ParameterizedType) {
327                 ParameterizedType pt = (ParameterizedType) ifc;
328                 if (ifcName.equals(pt.getRawType().toString())) {
329                     ifcType = pt;
330                 }
331             }
332         }
333         assertNotNull(ifcType);
334
335         java.lang.reflect.Type[] typeArguments = ifcType.getActualTypeArguments();
336         assertEquals(1, typeArguments.length);
337         assertEquals("interface " + genericTypeName, typeArguments[0].toString());
338     }
339
340     /**
341      * Test if source code is compilable.
342      *
343      * @param sourcesOutputDir
344      *            directory containing source files
345      * @param compiledOutputDir
346      *            compiler output directory
347      */
348     static void testCompilation(final File sourcesOutputDir, final File compiledOutputDir) {
349         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
350         StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
351         List<File> filesList = getJavaFiles(sourcesOutputDir);
352         Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(filesList);
353         Iterable<String> options = Arrays.asList("-d", compiledOutputDir.getAbsolutePath());
354         boolean compiled = compiler.getTask(null, null, null, options, null, compilationUnits).call();
355         assertTrue("Compilation failed", compiled);
356     }
357
358     /**
359      * Asserts that directory contains exactly given count of files.
360      *
361      * @param dir
362      *            directory to test
363      * @param count
364      *            expected count of files in directory
365      */
366     static void assertFilesCount(final File dir, final int count) {
367         File[] dirContent = dir.listFiles();
368         if (dirContent == null) {
369             throw new AssertionError("File " + dir + " doesn't exists or it's not a directory");
370         } else {
371             assertEquals("Unexpected count of generated files", count, dirContent.length);
372         }
373     }
374
375     /**
376      * Search recursively given directory for *.java files.
377      *
378      * @param directory
379      *            directory to search
380      * @return List of java files found
381      */
382     private static List<File> getJavaFiles(final File directory) {
383         List<File> result = new ArrayList<>();
384         File[] filesToRead = directory.listFiles();
385         if (filesToRead != null) {
386             for (File file : filesToRead) {
387                 if (file.isDirectory()) {
388                     result.addAll(getJavaFiles(file));
389                 } else {
390                     String absPath = file.getAbsolutePath();
391                     if (absPath.endsWith(".java")) {
392                         result.add(file);
393                     }
394                 }
395             }
396         }
397         return result;
398     }
399
400     static List<File> getSourceFiles(final String path) throws FileNotFoundException, URISyntaxException {
401         final URI resPath = BaseCompilationTest.class.getResource(path).toURI();
402         final File sourcesDir = new File(resPath);
403         if (!sourcesDir.exists()) {
404             throw new FileNotFoundException("Testing files were not found(" + sourcesDir.getName() + ")");
405         }
406
407         final List<File> sourceFiles = new ArrayList<>();
408         final File[] fileArray = sourcesDir.listFiles();
409         if (fileArray == null) {
410             throw new IllegalArgumentException("Unable to locate files in " + sourcesDir);
411         }
412         sourceFiles.addAll(Arrays.asList(fileArray));
413         return sourceFiles;
414     }
415
416     static void deleteTestDir(final File file) {
417         if (file.isDirectory()) {
418             File[] filesToDelete = file.listFiles();
419             if (filesToDelete != null) {
420                 for (File f : filesToDelete) {
421                     deleteTestDir(f);
422                 }
423             }
424         }
425         if (!file.delete()) {
426             throw new RuntimeException("Failed to clean up after test");
427         }
428     }
429
430 }