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