Added support for yang-ext:context-reference extension.
[mdsal.git] / code-generator / binding-java-api-generator / src / test / java / org / opendaylight / yangtools / sal / java / api / generator / test / CompilationTest.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.assertEquals;
11 import static org.junit.Assert.assertTrue;
12
13 import java.io.File;
14 import java.io.FileNotFoundException;
15 import java.lang.annotation.Annotation;
16 import java.lang.reflect.Method;
17 import java.net.URL;
18 import java.net.URLClassLoader;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Set;
24
25 import javax.tools.JavaCompiler;
26 import javax.tools.JavaFileObject;
27 import javax.tools.StandardJavaFileManager;
28 import javax.tools.ToolProvider;
29
30 import org.junit.Before;
31 import org.junit.BeforeClass;
32 import org.junit.Test;
33 import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator;
34 import org.opendaylight.yangtools.sal.binding.generator.impl.BindingGeneratorImpl;
35 import org.opendaylight.yangtools.sal.binding.model.api.Type;
36 import org.opendaylight.yangtools.sal.java.api.generator.GeneratorJavaFile;
37 import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
38 import org.opendaylight.yangtools.yang.model.api.Module;
39 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
40 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
41
42 /**
43  * Test correct code generation.
44  *
45  */
46 public class CompilationTest {
47     private static final String FS = File.separator;
48     private static final String NS_TEST = "org" + FS + "opendaylight" + FS + "yang" + FS + "gen" + FS + "v1" + FS
49             + "urn" + FS + "opendaylight" + FS + "test" + FS + "rev131008";
50     private static final String NS_FOO = "org" + FS + "opendaylight" + FS + "yang" + FS + "gen" + FS + "v1" + FS
51             + "urn" + FS + "opendaylight" + FS + "foo" + FS + "rev131008";
52
53     private static final String TEST_PATH = "target" + FS + "test";
54     private static final File TEST_DIR = new File(TEST_PATH);
55
56     private static final String GENERATOR_OUTPUT_PATH = TEST_PATH + FS + "src";
57     private static final File GENERATOR_OUTPUT_DIR = new File(GENERATOR_OUTPUT_PATH);
58     private static final String COMPILER_OUTPUT_PATH = TEST_PATH + FS + "bin";
59     private static final File COMPILER_OUTPUT_DIR = new File(COMPILER_OUTPUT_PATH);
60
61     private YangParserImpl parser;
62     private BindingGenerator bindingGenerator;
63
64     @BeforeClass
65     public static void createTestDirs() {
66         if (TEST_DIR.exists()) {
67             deleteTestDir(TEST_DIR);
68         }
69         assertTrue(GENERATOR_OUTPUT_DIR.mkdirs());
70         assertTrue(COMPILER_OUTPUT_DIR.mkdirs());
71     }
72
73     @Before
74     public void init() {
75         parser = new YangParserImpl();
76         bindingGenerator = new BindingGeneratorImpl();
77     }
78
79     /**
80      * Method to clean resources. It is manually called at the end of each test
81      * instead of marking it with @After annotation to prevent removing
82      * generated code if test fails.
83      */
84     public void cleanUp(File... resourceDirs) {
85         for (File resourceDir : resourceDirs) {
86             if (resourceDir.exists()) {
87                 deleteTestDir(resourceDir);
88             }
89         }
90     }
91
92     @Test
93     public void testListGeneration() throws Exception {
94         final File sourcesOutputDir = new File(GENERATOR_OUTPUT_PATH + FS + "list-gen");
95         assertTrue("Failed to create test file '" + sourcesOutputDir + "'", sourcesOutputDir.mkdir());
96         final File compiledOutputDir = new File(COMPILER_OUTPUT_PATH + FS + "list-gen");
97         assertTrue("Failed to create test file '" + compiledOutputDir + "'", compiledOutputDir.mkdir());
98
99         final List<File> sourceFiles = getSourceFiles("/compilation/list-gen");
100         final Set<Module> modulesToBuild = parser.parseYangModels(sourceFiles);
101         final SchemaContext context = parser.resolveSchemaContext(modulesToBuild);
102         final List<Type> types = bindingGenerator.generateTypes(context);
103         final GeneratorJavaFile generator = new GeneratorJavaFile(new HashSet<>(types));
104         generator.generateToFile(sourcesOutputDir);
105
106         // Test if all sources are generated
107         File parent = new File(sourcesOutputDir, NS_TEST);
108         File linksKeyFile = new File(parent, "LinksKey.java");
109         assertTrue(new File(parent, "KeyArgs.java").exists());
110         assertTrue(new File(parent, "Links.java").exists());
111         assertTrue(new File(parent, "LinksBuilder.java").exists());
112         assertTrue(linksKeyFile.exists());
113         assertTrue(new File(parent, "TestData.java").exists());
114         assertTrue(new File(parent, "links" + FS + "Level.java").exists());
115         assertTrue(new File(parent, "links" + FS + "LinkGroup.java").exists());
116         assertTrue(new File(parent, "links" + FS + "Node.java").exists());
117         assertTrue(new File(parent, "links" + FS + "NodeBuilder.java").exists());
118         assertTrue(new File(parent, "links" + FS + "NodeList.java").exists());
119         assertTrue(new File(parent, "links" + FS + "NodeListBuilder.java").exists());
120         assertTrue(linksKeyFile.exists());
121
122         // Test if sources are compilable
123         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
124         StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
125         List<File> filesList = getJavaFiles(sourcesOutputDir);
126         Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(filesList);
127         Iterable<String> options = Arrays.asList("-d", compiledOutputDir.getAbsolutePath());
128         boolean compiled = compiler.getTask(null, null, null, options, null, compilationUnits).call();
129         assertTrue(compiled);
130
131         ClassLoader loader = new URLClassLoader(new URL[] { compiledOutputDir.toURI().toURL() });
132         Class<?> linksKeyClass = Class.forName("org.opendaylight.yang.gen.v1.urn.opendaylight.test.rev131008.LinksKey",
133                 true, loader);
134
135         // Test list key constructor arguments ordering
136         try {
137             linksKeyClass.getConstructor(Byte.class, String.class, Integer.class);
138         } catch (NoSuchMethodException e) {
139             throw new AssertionError("Parameters of list key constructor are not properly ordered");
140         }
141
142         cleanUp(sourcesOutputDir, compiledOutputDir);
143     }
144
145     @Test
146     public void testAugmentUnderUsesGeneration() throws Exception {
147         final File sourcesOutputDir = new File(GENERATOR_OUTPUT_PATH + FS + "augment-under-uses");
148         assertTrue("Failed to create test file '" + sourcesOutputDir + "'", sourcesOutputDir.mkdir());
149         final File compiledOutputDir = new File(COMPILER_OUTPUT_PATH + FS + "augment-under-uses");
150         assertTrue("Failed to create test file '" + compiledOutputDir + "'", compiledOutputDir.mkdir());
151
152         final List<File> sourceFiles = getSourceFiles("/compilation/augment-under-uses");
153         final Set<Module> modulesToBuild = parser.parseYangModels(sourceFiles);
154         final SchemaContext context = parser.resolveSchemaContext(modulesToBuild);
155         final List<Type> types = bindingGenerator.generateTypes(context);
156         final GeneratorJavaFile generator = new GeneratorJavaFile(new HashSet<>(types));
157         generator.generateToFile(sourcesOutputDir);
158
159         // Test if all sources are generated
160         File parent = new File(sourcesOutputDir, NS_TEST);
161         assertTrue(new File(parent, "Object.java").exists());
162         assertTrue(new File(parent, "OpenObject.java").exists());
163         assertTrue(new File(parent, "object" + FS + "Nodes.java").exists());
164         assertTrue(new File(parent, "object" + FS + "NodesBuilder.java").exists());
165         assertTrue(new File(parent, "open" + FS + "object" + FS + "Nodes1.java").exists());
166         assertTrue(new File(parent, "open" + FS + "object" + FS + "Nodes1Builder.java").exists());
167         assertTrue(new File(parent, "open" + FS + "object" + FS + "nodes" + FS + "Links.java").exists());
168         assertTrue(new File(parent, "open" + FS + "object" + FS + "nodes" + FS + "LinksBuilder.java").exists());
169
170         // Test if sources are compilable
171         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
172         StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
173
174         List<File> filesList = getJavaFiles(sourcesOutputDir);
175         Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(filesList);
176         Iterable<String> options = Arrays.asList("-d", compiledOutputDir.getAbsolutePath());
177         boolean compiled = compiler.getTask(null, null, null, options, null, compilationUnits).call();
178         assertTrue(compiled);
179
180         cleanUp(sourcesOutputDir, compiledOutputDir);
181     }
182
183     // TODO: add test for return types, equals and hashcode
184     @Test
185     public void testLeafReturnTypes() throws Exception {
186         final File sourcesOutputDir = new File(GENERATOR_OUTPUT_PATH + FS + "leaf-return-types");
187         assertTrue("Failed to create test file '" + sourcesOutputDir + "'", sourcesOutputDir.mkdir());
188         final File compiledOutputDir = new File(COMPILER_OUTPUT_PATH + FS + "leaf-return-types");
189         assertTrue("Failed to create test file '" + compiledOutputDir + "'", compiledOutputDir.mkdir());
190
191         final List<File> sourceFiles = getSourceFiles("/compilation/leaf-return-types");
192         final Set<Module> modulesToBuild = parser.parseYangModels(sourceFiles);
193         final SchemaContext context = parser.resolveSchemaContext(modulesToBuild);
194         final List<Type> types = bindingGenerator.generateTypes(context);
195         final GeneratorJavaFile generator = new GeneratorJavaFile(new HashSet<>(types));
196         generator.generateToFile(sourcesOutputDir);
197
198         File parent = new File(sourcesOutputDir, NS_TEST);
199         assertTrue(new File(parent, "TestData.java").exists());
200         assertTrue(new File(parent, "Nodes.java").exists());
201         assertTrue(new File(parent, "NodesBuilder.java").exists());
202         assertTrue(new File(parent, "Alg.java").exists());
203
204         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
205         StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
206
207         List<File> filesList = getJavaFiles(sourcesOutputDir);
208         Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(filesList);
209         Iterable<String> options = Arrays.asList("-d", compiledOutputDir.getAbsolutePath());
210         boolean compiled = compiler.getTask(null, null, null, options, null, compilationUnits).call();
211         assertTrue(compiled);
212
213         cleanUp(sourcesOutputDir, compiledOutputDir);
214     }
215
216     @Test
217     public void testGenerationContextReferenceExtension() throws Exception {
218         final File sourcesOutputDir = new File(GENERATOR_OUTPUT_PATH + FS + "context-reference");
219         assertTrue("Failed to create test file '" + sourcesOutputDir + "'", sourcesOutputDir.mkdir());
220         final File compiledOutputDir = new File(COMPILER_OUTPUT_PATH + FS + "context-reference");
221         assertTrue("Failed to create test file '" + compiledOutputDir + "'", compiledOutputDir.mkdir());
222
223         final List<File> sourceFiles = getSourceFiles("/compilation/context-reference");
224         final Set<Module> modulesToBuild = parser.parseYangModels(sourceFiles);
225         final SchemaContext context = parser.resolveSchemaContext(modulesToBuild);
226         final List<Type> types = bindingGenerator.generateTypes(context);
227         final GeneratorJavaFile generator = new GeneratorJavaFile(new HashSet<>(types));
228         generator.generateToFile(sourcesOutputDir);
229
230         // Test if all sources are generated
231         File fooParent = new File(sourcesOutputDir, NS_FOO);
232         File nodes = new File(fooParent, "Nodes.java");
233         File nodesBuilder = new File(fooParent, "NodesBuilder.java");
234         assertTrue(new File(fooParent, "FooData.java").exists());
235         assertTrue(nodes.exists());
236         assertTrue(nodesBuilder.exists());
237
238         // Test if sources are compilable
239         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
240         StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
241
242         List<File> filesList = getJavaFiles(sourcesOutputDir);
243         Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(filesList);
244         Iterable<String> options = Arrays.asList("-d", compiledOutputDir.getAbsolutePath());
245         boolean compiled = compiler.getTask(null, null, null, options, null, compilationUnits).call();
246         assertTrue(compiled);
247
248         ClassLoader loader = new URLClassLoader(new URL[] { compiledOutputDir.toURI().toURL() });
249         Class<?> nodesClass = Class.forName("org.opendaylight.yang.gen.v1.urn.opendaylight.foo.rev131008.Nodes", true,
250                 loader);
251
252         // Test annotation
253         try {
254             Method getId = nodesClass.getMethod("getId");
255             Annotation[] annotations = getId.getAnnotations();
256             assertEquals(1, annotations.length);
257             Annotation routingContext = annotations[0];
258             assertEquals(RoutingContext.class, routingContext.annotationType());
259         } catch (NoSuchMethodException e) {
260             throw new AssertionError("Method getId() not found");
261         }
262
263         cleanUp(sourcesOutputDir, compiledOutputDir);
264     }
265
266     @Test
267     public void compilationTest() throws Exception {
268         final File sourcesOutputDir = new File(GENERATOR_OUTPUT_PATH + FS + "yang");
269         assertTrue("Failed to create test file '" + sourcesOutputDir + "'", sourcesOutputDir.mkdir());
270         final File compiledOutputDir = new File(COMPILER_OUTPUT_PATH + FS + "yang");
271         assertTrue("Failed to create test file '" + compiledOutputDir + "'", compiledOutputDir.mkdir());
272
273         final List<File> sourceFiles = getSourceFiles("/yang");
274         final Set<Module> modulesToBuild = parser.parseYangModels(sourceFiles);
275         final SchemaContext context = parser.resolveSchemaContext(modulesToBuild);
276         final List<Type> types = bindingGenerator.generateTypes(context);
277         final GeneratorJavaFile generator = new GeneratorJavaFile(new HashSet<>(types));
278         generator.generateToFile(sourcesOutputDir);
279
280         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
281         StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
282
283         List<File> filesList = getJavaFiles(sourcesOutputDir);
284         Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(filesList);
285         Iterable<String> options = Arrays.asList("-d", compiledOutputDir.getAbsolutePath());
286         boolean compiled = compiler.getTask(null, null, null, options, null, compilationUnits).call();
287         assertTrue(compiled);
288
289         cleanUp(sourcesOutputDir, compiledOutputDir);
290     }
291
292     private List<File> getSourceFiles(String path) throws FileNotFoundException {
293         final String resPath = getClass().getResource(path).getPath();
294         final File sourcesDir = new File(resPath);
295         if (sourcesDir.exists()) {
296             final List<File> sourceFiles = new ArrayList<>();
297             final File[] fileArray = sourcesDir.listFiles();
298             if (fileArray == null) {
299                 throw new IllegalArgumentException("Unable to locate files in " + sourcesDir);
300             }
301             sourceFiles.addAll(Arrays.asList(fileArray));
302             return sourceFiles;
303         } else {
304             throw new FileNotFoundException("Testing files were not found(" + sourcesDir.getName() + ")");
305         }
306     }
307
308     private static void deleteTestDir(File file) {
309         if (file.isDirectory()) {
310             File[] filesToDelete = file.listFiles();
311             if (filesToDelete != null) {
312                 for (File f : filesToDelete) {
313                     deleteTestDir(f);
314                 }
315             }
316         }
317         if (!file.delete()) {
318             throw new RuntimeException("Failed to clean up after test");
319         }
320     }
321
322     /**
323      * Search recursively given directory for *.java files.
324      *
325      * @param directory
326      *            directory to search
327      * @return List of java files found
328      */
329     private List<File> getJavaFiles(File directory) {
330         List<File> result = new ArrayList<>();
331         File[] filesToRead = directory.listFiles();
332         if (filesToRead != null) {
333             for (File file : filesToRead) {
334                 if (file.isDirectory()) {
335                     result.addAll(getJavaFiles(file));
336                 } else {
337                     String absPath = file.getAbsolutePath();
338                     if (absPath.endsWith(".java")) {
339                         result.add(file);
340                     }
341                 }
342             }
343         }
344         return result;
345     }
346 }