2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.binding.codegen;
10 import static org.junit.jupiter.api.Assertions.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
12 import static org.junit.jupiter.api.Assertions.assertNotNull;
13 import static org.junit.jupiter.api.Assertions.assertThrows;
14 import static org.junit.jupiter.api.Assertions.assertTrue;
15 import static org.junit.jupiter.api.Assertions.fail;
18 import java.io.IOException;
19 import java.lang.reflect.Constructor;
20 import java.lang.reflect.Field;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Modifier;
24 import java.lang.reflect.ParameterizedType;
25 import java.lang.reflect.Type;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Comparator;
31 import java.util.List;
32 import javax.tools.Diagnostic;
33 import javax.tools.ToolProvider;
35 public final class CompilationTestUtils {
36 static final String BASE_PKG = "org.opendaylight.yang.gen.v1";
38 static final Path TEST_DIR = Path.of("target", "test");
39 static final Path GENERATOR_OUTPUT_DIR = TEST_DIR.resolve("src");
40 static final Path COMPILER_OUTPUT_DIR = TEST_DIR.resolve("bin");
42 static final String AUGMENTATION = "interface org.opendaylight.yangtools.binding.Augmentation";
43 static final Path BASE_PATH = Path.of("org", "opendaylight", "yang", "gen", "v1");
44 static final Path BASE_SVC_PATH = Path.of("org", "opendaylight", "yang", "svc", "v1");
45 static final Path NS_TEST = BASE_PATH.resolve(Path.of("urn", "opendaylight", "test", "rev131008"));
46 static final Path NS_SVC_TEST = BASE_SVC_PATH.resolve(Path.of("urn", "opendaylight", "test", "rev131008"));
47 static final Path NS_FOO = BASE_PATH.resolve(Path.of("urn", "opendaylight", "foo", "rev131008"));
48 static final Path NS_SVC_FOO = BASE_SVC_PATH.resolve(Path.of("urn", "opendaylight", "foo", "rev131008"));
49 static final Path NS_BAR = BASE_PATH.resolve(Path.of("urn", "opendaylight", "bar", "rev131008"));
50 static final Path NS_SVC_BAR = BASE_SVC_PATH.resolve(Path.of("urn", "opendaylight", "bar", "rev131008"));
51 static final Path NS_BAZ = BASE_PATH.resolve(Path.of("urn", "opendaylight", "baz", "rev131008"));
52 static final Path NS_SVC_BAZ = BASE_SVC_PATH.resolve(Path.of("urn", "opendaylight", "baz", "rev131008"));
53 static final Path NS_BUG5882 = BASE_PATH.resolve(Path.of("urn", "yang", "foo", "rev160102"));
55 private CompilationTestUtils() {
59 static Path compilerOutput(final String name) {
60 return createDirectory(COMPILER_OUTPUT_DIR.resolve(name));
63 static Path generatorOutput(final String name) {
64 return createDirectory(GENERATOR_OUTPUT_DIR.resolve(name));
67 private static Path createDirectory(final Path path) {
69 Files.createDirectory(path);
70 } catch (IOException e) {
71 throw new AssertionError(e);
77 * Method to clean resources. It is manually called at the end of each test instead of marking it with @After
78 * annotation to prevent removing generated code if test fails.
80 static void cleanUp(final Path... resourceDirs) throws IOException {
81 for (var resourceDir : resourceDirs) {
82 if (Files.exists(resourceDir)) {
83 deleteTestDir(resourceDir);
89 * Asserts that class contains field with given name and type.
91 * @param clazz class to test
92 * @param name field name
93 * @param type field type
94 * @return field with given name if present in class
96 static Field assertContainsField(final Class<?> clazz, final String name, final Class<?> type) {
98 Field field = clazz.getDeclaredField(name);
99 assertEquals(type, field.getType());
101 } catch (NoSuchFieldException e) {
102 throw new AssertionError("Field " + name + " does not exist in class " + clazz.getSimpleName(), e);
107 * Asserts that class contains field with given name and value. Method tries to create new instance of class
108 * and get value of field. If class constructor contains any arguments, class is instantiated with null values.
110 * @param clazz class to test
111 * @param name name of field
112 * @param returnType return type of field
113 * @param expectedValue expected value of field
114 * @param constructorArgs constructor arguments of class to test
116 static void assertContainsFieldWithValue(final Class<?> clazz, final String name, final Class<?> returnType,
117 final Object expectedValue, final Class<?>... constructorArgs) {
118 Object[] initargs = null;
119 if (constructorArgs != null && constructorArgs.length > 0) {
120 initargs = new Object[constructorArgs.length];
121 for (int i = 0; i < constructorArgs.length; i++) {
125 assertContainsFieldWithValue(clazz, name, returnType, expectedValue, constructorArgs, initargs);
129 * Asserts that class contains field with given name, return type and value.
131 * @param clazz class to test
132 * @param name name of field
133 * @param returnType return type of field
134 * @param expectedValue expected value of field
135 * @param constructorArgs array of constructor arguments classes
136 * @param initargs array of constructor values
138 static void assertContainsFieldWithValue(final Class<?> clazz, final String name, final Class<?> returnType,
139 final Object expectedValue, final Class<?>[] constructorArgs, final Object... initargs) {
140 final var field = assertContainsField(clazz, name, returnType);
141 field.setAccessible(true);
144 if ((field.getModifiers() & Modifier.STATIC) == 0) {
146 Constructor<?> cls = clazz.getDeclaredConstructor(constructorArgs);
147 obj = cls.newInstance(initargs);
148 } catch (ReflectiveOperationException e) {
149 throw new AssertionError("Failed to instantiate object for " + clazz, e);
156 assertEquals(expectedValue, field.get(obj));
157 } catch (IllegalArgumentException | IllegalAccessException e) {
158 throw new AssertionError("Failed to get field " + name + " of class " + clazz, e);
163 * Asserts that class contains constructor with parameter types.
165 * @param clazz class to test
166 * @param args array of argument classes
168 static Constructor<?> assertContainsConstructor(final Class<?> clazz, final Class<?>... args) {
170 return clazz.getDeclaredConstructor(args);
171 } catch (NoSuchMethodException e) {
172 throw new AssertionError("Constructor with args " + Arrays.toString(args) + " does not exists in class "
173 + clazz.getSimpleName(), e);
178 * Asserts that class contains method with given name, return type and parameter types.
180 * @param clazz class to test
181 * @param returnType method return type
182 * @param name method name
183 * @param args array of parameter type classes
184 * @return method with given name, return type and parameter types
186 static Method assertContainsMethod(final Class<?> clazz, final Class<?> returnType, final String name,
187 final Class<?>... args) {
189 Method method = clazz.getDeclaredMethod(name, args);
190 assertEquals(returnType, method.getReturnType());
192 } catch (NoSuchMethodException e) {
193 throw new AssertionError("Method " + name + " with args " + Arrays.toString(args)
194 + " does not exists in class " + clazz.getSimpleName(), e);
199 * Asserts that class contains method with given name and return type.
201 * @param clazz class to test
202 * @param returnTypeStr name of method return type
203 * @param name method name
204 * @param loader current class loader
206 static void assertContainsMethod(final Class<?> clazz, final String returnTypeStr, final String name,
207 final ClassLoader loader) {
210 returnType = Class.forName(returnTypeStr, true, loader);
211 Method method = clazz.getMethod(name);
212 assertEquals(returnType, method.getReturnType());
213 } catch (ClassNotFoundException e) {
214 throw new AssertionError("Return type of method '" + name + "' not found", e);
215 } catch (NoSuchMethodException e) {
216 throw new AssertionError("Method " + name + " does not exists in class " + clazz.getSimpleName(), e);
221 * Asserts that class contains hashCode, equals and toString methods.
223 * @param clazz class to test
225 static void assertContainsDefaultMethods(final Class<?> clazz) {
226 assertContainsMethod(clazz, Integer.TYPE, "hashCode");
227 assertContainsMethod(clazz, Boolean.TYPE, "equals", Object.class);
228 assertContainsMethod(clazz, String.class, "toString");
232 * Asserts that constructor contains check for illegal argument.
234 * @param constructor constructor to invoke
235 * @param errorMsg expected error message
236 * @param args constructor arguments
238 static void assertContainsRestrictionCheck(final Constructor<?> constructor, final String errorMsg,
239 final Object... args) {
240 final var cause = assertThrows(InvocationTargetException.class, () -> constructor.newInstance(args)).getCause();
241 assertInstanceOf(IllegalArgumentException.class, cause);
242 assertEquals(errorMsg, cause.getMessage());
246 * Asserts that method contains check for illegal argument.
248 * @param obj object to test (can be null, if method is static)
249 * @param method method to invoke
250 * @param errorMsg expected error message
251 * @param args constructor arguments
253 static void assertContainsRestrictionCheck(final Object obj, final Method method, final String errorMsg,
254 final Object... args) {
255 final var cause = assertThrows(InvocationTargetException.class, () -> method.invoke(obj, args)).getCause();
256 assertInstanceOf(IllegalArgumentException.class, cause);
257 assertEquals(errorMsg, cause.getMessage());
261 * Asserts that class implements given interface.
263 * @param clazz source to test
264 * @param ifc expected interface
266 static void assertImplementsIfc(final Class<?> clazz, final Class<?> ifc) {
267 final var ifcsList = Arrays.asList(clazz.getInterfaces());
268 if (!ifcsList.contains(ifc)) {
269 throw new AssertionError(clazz + " should implement " + ifc);
274 * Test if interface generated from augment extends Augmentation interface with correct generic type.
276 * @param clazz interface generated from augment
277 * @param genericTypeName fully qualified name of expected parameter type
279 static void testAugmentation(final Class<?> clazz, final String genericTypeName) {
280 assertImplementsParameterizedIfc(clazz, AUGMENTATION, genericTypeName);
284 * Asserts that class implements interface with given name and generic type parameter.
286 * @param clazz class to test
287 * @param ifcName name of interface
288 * @param genericTypeName name of generic type
290 static void assertImplementsParameterizedIfc(final Class<?> clazz, final String ifcName,
291 final String genericTypeName) {
292 ParameterizedType ifcType = null;
293 for (var ifc : clazz.getGenericInterfaces()) {
294 if (ifc instanceof ParameterizedType pt && ifcName.equals(pt.getRawType().toString())) {
298 assertNotNull(ifcType);
300 Type[] typeArg = ifcType.getActualTypeArguments();
301 assertEquals(1, typeArg.length);
302 Type typeArgument = typeArg[0];
303 assertInstanceOf(Class.class, typeArgument);
304 Class<?> argClass = (Class<?>) typeArgument;
305 assertEquals(genericTypeName, argClass.getName());
306 assertTrue(argClass.isInterface());
310 * Test if source code is compilable.
312 * @param sourcesOutputDir directory containing source files
313 * @param compiledOutputDir compiler output directory
315 static void testCompilation(final Path sourcesOutputDir, final Path compiledOutputDir) {
316 final var compiler = ToolProvider.getSystemJavaCompiler();
317 final var fileManager = compiler.getStandardFileManager(null, null, null);
318 final var filesList = getJavaFiles(sourcesOutputDir);
319 final var compilationUnits = fileManager.getJavaFileObjectsFromPaths(filesList);
320 final var options = List.of("-d", compiledOutputDir.toAbsolutePath().toString());
322 final var diags = new ArrayList<Diagnostic<?>>();
323 if (!compiler.getTask(null, null, diags::add, options, null, compilationUnits).call()) {
324 fail("Compilation failed with " + diags);
329 * Asserts that directory contains exactly given count of files.
334 * expected count of files in directory
336 static void assertFilesCount(final Path dir, final int count) {
337 try (var files = Files.list(dir)) {
338 assertEquals(count, files.count(), "Unexpected count of generated files");
339 } catch (IOException e) {
340 throw new AssertionError(e);
345 * Search recursively given directory for *.java files.
347 * @param directory directory to search
348 * @return List of java files found
350 private static List<Path> getJavaFiles(final Path directory) {
351 final var result = new ArrayList<Path>();
352 try (var stream = Files.list(directory)) {
353 stream.forEach(file -> {
354 if (Files.isDirectory(file)) {
355 result.addAll(getJavaFiles(file));
356 } else if (file.getFileName().toString().endsWith(".java")) {
360 } catch (IOException e) {
361 throw new AssertionError(e);
366 static void deleteTestDir(final Path file) throws IOException {
367 if (Files.isDirectory(file)) {
368 try (var paths = Files.walk(file)) {
369 paths.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
376 static void assertRegularFile(final Path parent, final String name) {
377 assertTrue(Files.isRegularFile(parent.resolve(name)));