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