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