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