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