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