Merge "Fix equals and add hashcode to generated abstract module."
[controller.git] / opendaylight / config / yang-jmx-generator-plugin / src / test / java / org / opendaylight / controller / config / yangjmxgenerator / plugin / JMXGeneratorTest.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.controller.config.yangjmxgenerator.plugin;
9
10 import com.google.common.base.Predicate;
11 import com.google.common.collect.Collections2;
12 import com.google.common.collect.Lists;
13 import com.google.common.collect.Maps;
14 import com.google.common.collect.Sets;
15 import com.google.common.io.Files;
16 import org.apache.commons.io.FileUtils;
17 import org.apache.maven.plugin.logging.Log;
18 import org.apache.maven.project.MavenProject;
19 import org.eclipse.jdt.core.JavaCore;
20 import org.eclipse.jdt.core.compiler.IProblem;
21 import org.eclipse.jdt.core.dom.AST;
22 import org.eclipse.jdt.core.dom.ASTParser;
23 import org.eclipse.jdt.core.dom.ASTVisitor;
24 import org.eclipse.jdt.core.dom.CompilationUnit;
25 import org.eclipse.jdt.core.dom.FieldDeclaration;
26 import org.eclipse.jdt.core.dom.MethodDeclaration;
27 import org.eclipse.jdt.core.dom.NormalAnnotation;
28 import org.eclipse.jdt.core.dom.PackageDeclaration;
29 import org.eclipse.jdt.core.dom.TypeDeclaration;
30 import org.junit.Before;
31 import org.junit.Test;
32 import org.opendaylight.controller.config.api.DependencyResolver;
33 import org.opendaylight.controller.config.api.DependencyResolverFactory;
34 import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
35 import org.opendaylight.controller.config.api.annotations.Description;
36 import org.opendaylight.controller.config.api.annotations.RequireInterface;
37 import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
38 import org.opendaylight.controller.config.spi.Module;
39 import org.opendaylight.controller.config.spi.ModuleFactory;
40 import org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants;
41 import org.opendaylight.controller.config.yangjmxgenerator.PackageTranslatorTest;
42 import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntryTest;
43 import org.osgi.framework.BundleContext;
44 import org.xml.sax.ErrorHandler;
45 import org.xml.sax.InputSource;
46 import org.xml.sax.SAXException;
47 import org.xml.sax.SAXParseException;
48
49 import javax.xml.parsers.DocumentBuilder;
50 import javax.xml.parsers.DocumentBuilderFactory;
51 import java.io.File;
52 import java.io.IOException;
53 import java.nio.charset.Charset;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.Collection;
57 import java.util.Collections;
58 import java.util.HashMap;
59 import java.util.HashSet;
60 import java.util.List;
61 import java.util.Map;
62 import java.util.Set;
63
64 import static org.hamcrest.CoreMatchers.equalTo;
65 import static org.hamcrest.CoreMatchers.is;
66 import static org.junit.Assert.assertEquals;
67 import static org.junit.Assert.assertNotNull;
68 import static org.junit.Assert.assertThat;
69 import static org.junit.Assert.assertTrue;
70 import static org.junit.Assert.assertFalse;
71 import static org.junit.Assert.fail;
72 import static org.junit.matchers.JUnitMatchers.containsString;
73 import static org.mockito.Matchers.any;
74 import static org.mockito.Mockito.mock;
75 import static org.mockito.Mockito.doNothing;
76 import static org.mockito.Mockito.doReturn;
77
78 public class JMXGeneratorTest extends AbstractGeneratorTest {
79
80     JMXGenerator jmxGenerator;
81
82     protected final HashMap<String, String> map = Maps.newHashMap();
83     protected File outputBaseDir;
84     File generatedResourcesDir;
85
86     private static final List<String> expectedModuleFileNames = ServiceInterfaceEntryTest
87             .toFileNames("[AbstractAsyncEventBusModule.java, AbstractAsyncEventBusModuleFactory.java, " +
88                     "AbstractDynamicThreadPoolModule.java, AbstractDynamicThreadPoolModuleFactory.java, " +
89                     "AbstractEventBusModule.java, AbstractEventBusModuleFactory.java, " +
90                     "AbstractNamingThreadFactoryModule.java, AbstractNamingThreadFactoryModuleFactory.java, " +
91                     "AsyncEventBusModule.java, AsyncEventBusModuleFactory.java, AsyncEventBusModuleMXBean.java, " +
92                     "AsyncEventBusRuntimeMXBean.java, AsyncEventBusRuntimeRegistration.java, " +
93                     "AsyncEventBusRuntimeRegistrator.java, DynamicThreadPoolModule.java, " +
94                     "DynamicThreadPoolModuleFactory.java, DynamicThreadPoolModuleMXBean.java, " +
95                     "DynamicThreadPoolRuntimeMXBean.java, DynamicThreadPoolRuntimeRegistration.java, " +
96                     "DynamicThreadPoolRuntimeRegistrator.java, EventBusModule.java, EventBusModuleFactory.java, " +
97                     "EventBusModuleMXBean.java, EventRuntimeMXBean.java, EventRuntimeRegistration.java, " +
98                     "InnerStreamList.java, NamingThreadFactoryModule.java, NamingThreadFactoryModuleFactory.java, " +
99                     "NamingThreadFactoryModuleMXBean.java, NamingThreadFactoryRuntimeMXBean.java, NamingThreadFactoryRuntimeRegistration.java, NamingThreadFactoryRuntimeRegistrator.java, Peer.java, StreamRuntimeMXBean.java, StreamRuntimeRegistration.java, ThreadRuntimeMXBean.java, ThreadRuntimeRegistration.java, ThreadStreamRuntimeMXBean.java, ThreadStreamRuntimeRegistration.java]");
100
101     private static final List<String> expectedBGPNames = ServiceInterfaceEntryTest
102             .toFileNames("[AbstractBgpListenerImplModule.java, " + "AbstractBgpListenerImplModuleFactory.java, " +
103                     "BgpListenerImplModule.java, " + "BgpListenerImplModuleFactory.java, " +
104                     "BgpListenerImplModuleMXBean.java, Peers.java]");
105
106     private static final List<String> expectedNetconfNames = ServiceInterfaceEntryTest
107             .toFileNames("[AbstractNetconfTestImplModule.java, " + "AbstractNetconfTestImplModuleFactory.java, " +
108                     "AbstractTestImplModule.java, " + "AbstractTestImplModuleFactory.java, " +
109                     "AutoCloseableServiceInterface.java, " + "ComplexDtoBInner.java, ComplexList.java, Deep.java, " +
110                     "DtoA.java, DtoA1.java, DtoAInner.java, DtoAInnerInner.java, DtoB.java, DtoC.java," + "NetconfTestImplModule.java, NetconfTestImplModuleFactory.java," + "NetconfTestImplModuleMXBean.java, Peer.java, SimpleList.java, TestImplModule.java, " + "TestImplModuleFactory.java," + " TestImplModuleMXBean.java" + "]");
111     private static final List<String> expectedTestFiles = ServiceInterfaceEntryTest
112             .toFileNames("[AbstractNetconfTestFileImplModule.java, AbstractNetconfTestFileImplModuleFactory.java, " +
113                     "AbstractNetconfTestFiles1ImplModule.java, AbstractNetconfTestFiles1ImplModuleFactory.java, " +
114                     "AbstractTestFileImplModule.java, AbstractTestFileImplModuleFactory.java, " +
115                     "AbstractTestFiles1ImplModule.java, AbstractTestFiles1ImplModuleFactory.java, DtoA.java, " +
116                     "DtoA.java, NetconfTestFileImplModuleMXBean.java, NetconfTestFileImplRuntimeMXBean.java, " +
117                     "NetconfTestFileImplRuntimeRegistration.java, NetconfTestFileImplRuntimeRegistrator.java, " +
118                     "NetconfTestFiles1ImplModule.java, NetconfTestFiles1ImplModuleFactory.java, " +
119                     "NetconfTestFiles1ImplModuleMXBean.java, NetconfTestFiles1ImplRuntimeMXBean.java, " +
120                     "NetconfTestFiles1ImplRuntimeRegistration.java, NetconfTestFiles1ImplRuntimeRegistrator.java, TestFileImplModule.java, TestFileImplModuleFactory.java, TestFileImplModuleMXBean.java, TestFileImplRuntimeMXBean.java, TestFileImplRuntimeRegistration.java, TestFileImplRuntimeRegistrator.java, TestFiles1ImplModule.java, TestFiles1ImplModuleFactory.java, TestFiles1ImplModuleMXBean.java, TestFiles1ImplRuntimeMXBean.java, TestFiles1ImplRuntimeRegistration.java, TestFiles1ImplRuntimeRegistrator.java]");
121     private static final List<String> expectedAllFileNames = ServiceInterfaceEntryTest
122             .toFileNames("[AbstractAsyncEventBusModule.java, AbstractAsyncEventBusModuleFactory.java, " +
123                     "AbstractBgpListenerImplModule.java, AbstractBgpListenerImplModuleFactory.java, " +
124                     "AbstractDynamicThreadPoolModule.java, AbstractDynamicThreadPoolModuleFactory.java, " +
125                     "AbstractEventBusModule.java, AbstractEventBusModuleFactory.java, " +
126                     "AbstractNamingThreadFactoryModule.java, AbstractNamingThreadFactoryModuleFactory.java, " +
127                     "AbstractNetconfTestFileImplModule.java, AbstractNetconfTestFileImplModuleFactory.java, " +
128                     "AbstractNetconfTestFiles1ImplModule.java, AbstractNetconfTestFiles1ImplModuleFactory.java, " +
129                     "AbstractNetconfTestImplModule.java, AbstractNetconfTestImplModuleFactory.java, " +
130                     "AbstractTestFileImplModule.java, AbstractTestFileImplModuleFactory.java, " +
131                     "AbstractTestFiles1ImplModule.java, AbstractTestFiles1ImplModuleFactory.java, " +
132                     "AbstractTestImplModule.java, AbstractTestImplModuleFactory.java, AsyncEventBusModule.java, " +
133                     "AsyncEventBusModuleFactory.java, AsyncEventBusModuleMXBean.java, " +
134                     "AsyncEventBusRuntimeMXBean.java, AsyncEventBusRuntimeRegistration.java, " +
135                     "AsyncEventBusRuntimeRegistrator.java, AutoCloseableServiceInterface.java, " +
136                     "BgpListenerImplModule.java, BgpListenerImplModuleFactory.java, BgpListenerImplModuleMXBean.java," +
137                     " BgpListenerImplRuntimeMXBean.java, BgpListenerImplRuntimeRegistration.java, " +
138                     "BgpListenerImplRuntimeRegistrator.java, ComplexDtoBInner.java, ComplexList.java, Deep.java, " +
139                     "DtoA.java, DtoA.java, DtoA.java, DtoA1.java, DtoAInner.java, DtoAInnerInner.java, DtoB.java, " +
140                     "DtoC.java, DynamicThreadPoolModule.java, DynamicThreadPoolModuleFactory.java, " +
141                     "DynamicThreadPoolModuleMXBean.java, DynamicThreadPoolRuntimeMXBean.java, " +
142                     "DynamicThreadPoolRuntimeRegistration.java, DynamicThreadPoolRuntimeRegistrator.java, " +
143                     "EventBusModule.java, EventBusModuleFactory.java, EventBusModuleMXBean.java, " +
144                     "EventBusServiceInterface.java, EventRuntimeMXBean.java, EventRuntimeRegistration.java, " +
145                     "InnerStreamList.java, NamingThreadFactoryModule.java, NamingThreadFactoryModuleFactory.java, " +
146                     "NamingThreadFactoryModuleMXBean.java, NamingThreadFactoryRuntimeMXBean.java, " +
147                     "NamingThreadFactoryRuntimeRegistration.java, NamingThreadFactoryRuntimeRegistrator.java, " +
148                     "NetconfTestFileImplModule.java, NetconfTestFileImplModuleFactory.java, " +
149                     "NetconfTestFileImplModuleMXBean.java, NetconfTestFileImplRuntimeMXBean.java, " +
150                     "NetconfTestFileImplRuntimeRegistration.java, NetconfTestFileImplRuntimeRegistrator.java, " +
151                     "NetconfTestFiles1ImplModule.java, NetconfTestFiles1ImplModuleFactory.java, " +
152                     "NetconfTestFiles1ImplModuleMXBean.java, NetconfTestFiles1ImplRuntimeMXBean.java, " +
153                     "NetconfTestFiles1ImplRuntimeRegistration.java, NetconfTestFiles1ImplRuntimeRegistrator.java, NetconfTestImplModule.java, NetconfTestImplModuleFactory.java, NetconfTestImplModuleMXBean.java, NetconfTestImplRuntimeMXBean.java, NetconfTestImplRuntimeRegistration.java, NetconfTestImplRuntimeRegistrator.java, Peer.java, Peer.java, PeersRuntimeMXBean.java, PeersRuntimeRegistration.java, ScheduledThreadPoolServiceInterface.java, SimpleList.java, StreamRuntimeMXBean.java, StreamRuntimeRegistration.java, TestFileImplModule.java, TestFileImplModuleFactory.java, TestFileImplModuleMXBean.java, TestFileImplRuntimeMXBean.java, TestFileImplRuntimeRegistration.java, TestFileImplRuntimeRegistrator.java, TestFiles1ImplModule.java, TestFiles1ImplModuleFactory.java, TestFiles1ImplModuleMXBean.java, TestFiles1ImplRuntimeMXBean.java, TestFiles1ImplRuntimeRegistration.java, TestFiles1ImplRuntimeRegistrator.java, TestImplModule.java, TestImplModuleFactory.java, TestImplModuleMXBean.java, TestImplRuntimeMXBean.java, TestImplRuntimeRegistration.java, TestImplRuntimeRegistrator.java, ThreadFactoryServiceInterface.java, ThreadPoolServiceInterface.java, ThreadRuntimeMXBean.java, ThreadRuntimeRegistration.java, ThreadStreamRuntimeMXBean.java, ThreadStreamRuntimeRegistration.java]");
154     private static final List<String> expectedGenerateMBEsListNames = ServiceInterfaceEntryTest
155             .toFileNames("[AbstractBgpListenerImplModule.java, AbstractBgpListenerImplModuleFactory.java, BgpListenerImplModule.java, BgpListenerImplModuleFactory.java, BgpListenerImplModuleMXBean.java, BgpListenerImplRuntimeMXBean.java, BgpListenerImplRuntimeRegistration.java, BgpListenerImplRuntimeRegistrator.java, PeersRuntimeMXBean.java, PeersRuntimeRegistration.java]");
156
157     @Before
158     public void setUp() {
159         map.put(JMXGenerator.NAMESPACE_TO_PACKAGE_PREFIX + "1",
160                 ConfigConstants.CONFIG_NAMESPACE
161                         + JMXGenerator.NAMESPACE_TO_PACKAGE_DIVIDER
162                         + PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX);
163         map.put(JMXGenerator.MODULE_FACTORY_FILE_BOOLEAN, "false");
164         jmxGenerator = new JMXGenerator(new FreeMarkerCodeWriterImpl());
165         jmxGenerator.setAdditionalConfig(map);
166         File targetDir = new File(generatorOutputPath, "target");
167         generatedResourcesDir = new File(targetDir, "generated-resources");
168         jmxGenerator.setResourceBaseDir(generatedResourcesDir);
169         Log mockedLog = mock(Log.class);
170         doReturn(false).when(mockedLog).isDebugEnabled();
171         doNothing().when(mockedLog).debug(any(CharSequence.class));
172         doNothing().when(mockedLog).info(any(CharSequence.class));
173         doNothing().when(mockedLog).error(any(CharSequence.class),
174                 any(Throwable.class));
175         jmxGenerator.setLog(mockedLog);
176         MavenProject project = mock(MavenProject.class);
177         doReturn(generatorOutputPath).when(project).getBasedir();
178         jmxGenerator.setMavenProject(project);
179         outputBaseDir = JMXGenerator.concatFolders(targetDir,
180                 "generated-sources", "config");
181     }
182
183     @Test
184     public void generateSIsMBsTest() {
185         Collection<File> files = jmxGenerator.generateSources(context,
186                 outputBaseDir, context.getModules());
187         List<String> expectedFileNames = new ArrayList<>();
188         expectedFileNames
189                 .addAll(ServiceInterfaceEntryTest.expectedSIEFileNames);
190         expectedFileNames.addAll(expectedModuleFileNames);
191
192         expectedFileNames.addAll(expectedBGPNames);
193         expectedFileNames.addAll(expectedNetconfNames);
194         expectedFileNames.addAll(expectedTestFiles);
195         Collections.sort(expectedFileNames);
196         // TODO: separate expectedAllFileNames into expectedBGPNames,
197         // expectedNetconfNames
198         assertEquals(expectedAllFileNames, toFileNames(files));
199
200         verifyModuleFactoryFile(false);
201     }
202
203     private void verifyModuleFactoryFile(boolean shouldBePresent) {
204         File factoryFile = new File(generatedResourcesDir, "META-INF"
205                 + File.separator + "services" + File.separator
206                 + ModuleFactory.class.getName());
207         if (!shouldBePresent)
208             assertFalse("Factory file should not be generated",
209                     factoryFile.exists());
210         else
211             assertTrue("Factory file should be generated", factoryFile.exists());
212     }
213
214     public static List<String> toFileNames(Collection<File> files) {
215         List<String> result = new ArrayList<>();
216         for (File f : files) {
217             result.add(f.getName());
218         }
219         Collections.sort(result);
220         return result;
221     }
222
223     @Test
224     public void generateSIEsTest() throws IOException {
225         Collection<File> files = jmxGenerator.generateSources(context,
226                 outputBaseDir, Sets.newHashSet(threadsModule));
227         assertEquals(ServiceInterfaceEntryTest.expectedSIEFileNames,
228                 toFileNames(files));
229
230         Map<String, ASTVisitor> verifiers = Maps.newHashMap();
231
232         for (File file : files) {
233             verifiers.put(file.getName(), new SieASTVisitor());
234         }
235
236         processGeneratedCode(files, verifiers);
237
238         for (File file : files) {
239             String fileName = file.getName();
240             SieASTVisitor verifier = (SieASTVisitor) verifiers.get(fileName);
241
242             assertEquals(fileName.substring(0, fileName.length() - 5),
243                     verifier.type);
244             assertThat(
245                     verifier.extnds,
246                     containsString("org.opendaylight.controller.config.api.annotations.AbstractServiceInterface"));
247             assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
248                     + ".threads", verifier.packageName);
249             assertNotNull(verifier.javadoc);
250
251             if ("ThreadPoolServiceInterface.java".equals(fileName)) {
252                 assertContains(verifier.descriptionAnotValue,
253                         "A simple pool of threads able to execute work.");
254                 assertContains(verifier.sieAnnotValue, "threadpool");
255                 assertContains(verifier.sieAnnotOsgiRegistrationType,
256                         PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
257                                 + ".threadpool.ThreadPool.class");
258             } else if ("ScheduledThreadPoolServiceInterface.java"
259                     .equals(fileName)) {
260                 assertContains(verifier.extnds,
261                         PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
262                                 + ".threads.ThreadPoolServiceInterface");
263                 assertContains(
264                         verifier.descriptionAnotValue,
265                         "An extension of the simple pool of threads able to schedule work to be executed at some point in time.");
266                 assertContains(verifier.sieAnnotValue, "scheduled-threadpool");
267                 assertContains(verifier.sieAnnotOsgiRegistrationType,
268                         PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
269                                 + ".threadpool.ScheduledThreadPool.class");
270             } else if ("EventBusServiceInterface.java".equals(fileName)) {
271                 assertContains(
272                         verifier.descriptionAnotValue,
273                         "Service representing an event bus. The service acts as message router between event producers and event consumers");
274                 assertContains(verifier.sieAnnotValue, "eventbus");
275                 assertContains(verifier.sieAnnotOsgiRegistrationType,
276                         "com.google.common.eventbus.EventBus.class");
277             } else if ("ThreadFactoryServiceInterface.java".equals(fileName)) {
278                 assertContains(
279                         verifier.descriptionAnotValue,
280                         "Service representing a ThreadFactory instance. It is directly useful in Java world, where various library pieces need to create threads and you may want to inject a customized thread implementation.");
281                 assertContains(verifier.sieAnnotValue, "threadfactory");
282                 assertContains(verifier.sieAnnotOsgiRegistrationType,
283                         "java.util.concurrent.ThreadFactory.class");
284
285             } else if ("ScheduledExecutorServiceServiceInterface.java"
286                     .equals(fileName)) {
287                 assertContains(verifier.sieAnnotOsgiRegistrationType,
288                         "java.util.concurrent.ScheduledExecutorService.class");
289             } else {
290                 fail("Unknown generated sie " + fileName);
291             }
292         }
293     }
294
295     @Test
296     public void generateMBEsListTest() throws Exception {
297         // default value for module factory file is true
298         map.put(JMXGenerator.MODULE_FACTORY_FILE_BOOLEAN, "randomValue");
299         jmxGenerator.setAdditionalConfig(map);
300
301         Collection<File> files = jmxGenerator.generateSources(context,
302                 outputBaseDir, Sets.newHashSet(bgpListenerJavaModule));
303
304         assertEquals(expectedGenerateMBEsListNames, toFileNames(files));
305     }
306
307     @Test
308     public void generateMBEsTest() throws Exception {
309         // default value for module factory file is true
310         map.put(JMXGenerator.MODULE_FACTORY_FILE_BOOLEAN, "randomValue");
311         jmxGenerator.setAdditionalConfig(map);
312
313         Collection<File> files = jmxGenerator.generateSources(context,
314                 outputBaseDir, Sets.newHashSet(threadsJavaModule));
315
316         assertEquals(expectedModuleFileNames, toFileNames(files));
317
318         Map<String, ASTVisitor> verifiers = Maps.newHashMap();
319
320         Collection<File> xmlFiles = Collections2.filter(files,
321                 new Predicate<File>() {
322
323                     @Override
324                     public boolean apply(File input) {
325                         return input.getName().endsWith("xml");
326                     }
327                 });
328
329         Collection<File> javaFiles = Collections2.filter(files,
330                 new Predicate<File>() {
331
332                     @Override
333                     public boolean apply(File input) {
334                         return input.getName().endsWith("java");
335                     }
336                 });
337
338         MbeASTVisitor abstractDynamicThreadPoolModuleVisitor = null;
339         MbeASTVisitor asyncEventBusModuleMXBeanVisitor = null;
340         MbeASTVisitor abstractNamingThreadFactoryModuleFactoryVisitor = null;
341         MbeASTVisitor asyncEventBusModuleVisitor = null;
342         MbeASTVisitor eventBusModuleFactoryVisitor = null;
343
344         for (File file : javaFiles) {
345             String name = file.getName();
346             MbeASTVisitor visitor = new MbeASTVisitor();
347             verifiers.put(name, visitor);
348             if (name.equals("AbstractDynamicThreadPoolModule.java"))
349                 abstractDynamicThreadPoolModuleVisitor = visitor;
350             if (name.equals("AsyncEventBusModuleMXBean.java"))
351                 asyncEventBusModuleMXBeanVisitor = visitor;
352             if (name.equals("AbstractNamingThreadFactoryModuleFactory.java"))
353                 abstractNamingThreadFactoryModuleFactoryVisitor = visitor;
354             if (name.equals("AsyncEventBusModule.java"))
355                 asyncEventBusModuleVisitor = visitor;
356             if (name.equals("EventBusModuleFactory.java"))
357                 eventBusModuleFactoryVisitor = visitor;
358         }
359
360         processGeneratedCode(javaFiles, verifiers);
361
362         assertAbstractDynamicThreadPoolModule(abstractDynamicThreadPoolModuleVisitor);
363         assertAsyncEventBusModuleMXBean(asyncEventBusModuleMXBeanVisitor);
364         assertAbstractNamingThreadFactoryModuleFactory(abstractNamingThreadFactoryModuleFactoryVisitor);
365         assertAsyncEventBusModule(asyncEventBusModuleVisitor);
366         assertEventBusModuleFactory(eventBusModuleFactoryVisitor);
367
368         verifyXmlFiles(xmlFiles);
369         // verify ModuleFactory file
370         File moduleFactoryFile = JMXGenerator.concatFolders(
371                 generatedResourcesDir, "META-INF", "services",
372                 ModuleFactory.class.getName());
373         assertThat(moduleFactoryFile.exists(), is(true));
374         Set<String> lines = Sets.newHashSet(FileUtils
375                 .readLines(moduleFactoryFile));
376         Set<String> expectedLines = Sets.newHashSet(//
377                 PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
378                         + ".threads.java.EventBusModuleFactory",//
379                 PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
380                         + ".threads.java.AsyncEventBusModuleFactory", //
381                 PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
382                         + ".threads.java.DynamicThreadPoolModuleFactory",//
383                 PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
384                         + ".threads.java.NamingThreadFactoryModuleFactory");
385         assertThat(lines, equalTo(expectedLines));
386
387     }
388
389     private void verifyXmlFiles(Collection<File> xmlFiles) throws Exception {
390         ErrorHandler errorHandler = new ErrorHandler() {
391
392             @Override
393             public void warning(SAXParseException exception)
394                     throws SAXException {
395                 fail("Generated blueprint xml is not well formed "
396                         + exception.getMessage());
397             }
398
399             @Override
400             public void fatalError(SAXParseException exception)
401                     throws SAXException {
402                 fail("Generated blueprint xml is not well formed "
403                         + exception.getMessage());
404             }
405
406             @Override
407             public void error(SAXParseException exception) throws SAXException {
408                 fail("Generated blueprint xml is not well formed "
409                         + exception.getMessage());
410             }
411         };
412
413         for (File file : xmlFiles) {
414             DocumentBuilderFactory factory = DocumentBuilderFactory
415                     .newInstance();
416             factory.setValidating(false);
417             factory.setNamespaceAware(true);
418
419             DocumentBuilder builder = factory.newDocumentBuilder();
420
421             builder.setErrorHandler(errorHandler);
422             builder.parse(new InputSource(file.getPath()));
423         }
424
425     }
426
427     private void assertEventBusModuleFactory(MbeASTVisitor visitor) {
428         assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
429                 + ".threads.java", visitor.packageName);
430         assertEquals("EventBusModuleFactory", visitor.type);
431         assertContains(visitor.extnds,
432                 PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
433                         + ".threads.java.AbstractEventBusModuleFactory");
434
435         assertEquals(0, visitor.fieldDeclarations.size());
436
437         assertEquals("Incorrenct number of generated methods", 0,
438                 visitor.methods.size());
439         assertEquals("Incorrenct number of generated constructors", 0,
440                 visitor.constructors.size());
441         assertEquals("Incorrenct number of generated method descriptions", 0,
442                 visitor.methodDescriptions.size());
443         assertEquals("Incorrenct number of generated method javadoc", 0,
444                 visitor.methodJavadoc.size());
445     }
446
447     private void assertAsyncEventBusModule(MbeASTVisitor visitor) {
448         assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
449                 + ".threads.java", visitor.packageName);
450         assertEquals("AsyncEventBusModule", visitor.type);
451         assertContains(visitor.extnds,
452                 PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
453                         + ".threads.java.AbstractAsyncEventBusModule");
454
455         assertEquals(0, visitor.fieldDeclarations.size());
456
457         assertEquals("Incorrenct number of generated methods", 2,
458                 visitor.methods.size());
459         assertEquals("Incorrenct number of generated constructors", 2,
460                 visitor.constructors.size());
461         assertEquals("Incorrenct number of generated method descriptions", 0,
462                 visitor.methodDescriptions.size());
463         assertEquals("Incorrenct number of generated method javadoc", 0,
464                 visitor.methodJavadoc.size());
465     }
466
467     private void assertAbstractNamingThreadFactoryModuleFactory(
468             MbeASTVisitor visitor) {
469         assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
470                 + ".threads.java", visitor.packageName);
471         assertEquals("AbstractNamingThreadFactoryModuleFactory", visitor.type);
472         assertContains(visitor.implmts,
473                 "org.opendaylight.controller.config.spi.ModuleFactory");
474         Set<String> fieldDeclarations = visitor.fieldDeclarations;
475         assertDeclaredField(fieldDeclarations,
476                 "public static final java.lang.String NAME=\"threadfactory-naming\"");
477         assertDeclaredField(
478                 fieldDeclarations,
479                 "private static final java.util.Set<Class<? extends org.opendaylight.controller.config.api.annotations.AbstractServiceInterface>> serviceIfcs=new java.util.HashSet<Class<? extends org.opendaylight.controller.config.api.annotations.AbstractServiceInterface>>()");
480
481         assertEquals(2, fieldDeclarations.size());
482
483         assertFactoryMethods(visitor.methods, 8);
484         assertEquals("Incorrenct number of generated method descriptions", 0,
485                 visitor.methodDescriptions.size());
486         assertEquals("Incorrenct number of generated method javadoc", 0,
487                 visitor.methodJavadoc.size());
488
489     }
490
491     private void assertFactoryMethods(Set<String> methods, int expectedSize) {
492
493         List<ArgumentAssertion> args = Lists.newArrayList();
494         ArgumentAssertion oldInstanceArg = new ArgumentAssertion(DynamicMBeanWithInstance.class.getCanonicalName(), "old");
495         ArgumentAssertion instanceNameArg = new ArgumentAssertion(String.class.getSimpleName(), "instanceName");
496         ArgumentAssertion dependencyResolverArg = new ArgumentAssertion(DependencyResolver.class.getCanonicalName(), "dependencyResolver");
497         ArgumentAssertion bundleContextArg = new ArgumentAssertion(BundleContext.class.getCanonicalName(), "bundleContext");
498
499         assertMethodPresent(methods, new MethodAssertion(String.class.getSimpleName(), "getImplementationName"));
500
501         args.add(instanceNameArg);
502         args.add(dependencyResolverArg);
503         args.add(bundleContextArg);
504         assertMethodPresent(methods, new MethodAssertion(Module.class.getCanonicalName(), "createModule", args));
505
506         args.add(2, oldInstanceArg);
507         assertMethodPresent(methods, new MethodAssertion(Module.class.getCanonicalName(), "createModule", args));
508
509         args.clear();
510         args.add(oldInstanceArg);
511         assertMethodPresent(methods, new MethodAssertion("org.opendaylight.controller.config.threads.java.NamingThreadFactoryModule", "handleChangedClass", args));
512
513         args.clear();
514         args.add(instanceNameArg);
515         args.add(dependencyResolverArg);
516         args.add(bundleContextArg);
517         assertMethodPresent(methods, new MethodAssertion("org.opendaylight.controller.config.threads.java.NamingThreadFactoryModule", "instantiateModule", args));
518
519
520         args.add(2, new ArgumentAssertion("org.opendaylight.controller.config.threads.java.NamingThreadFactoryModule", "oldModule"));
521         args.add(3, new ArgumentAssertion(AutoCloseable.class.getCanonicalName(), "oldInstance"));
522         assertMethodPresent(methods, new MethodAssertion("org.opendaylight.controller.config.threads.java.NamingThreadFactoryModule", "instantiateModule", args));
523
524         args.clear();
525         args.add(new ArgumentAssertion(DependencyResolverFactory.class.getCanonicalName(), "dependencyResolverFactory"));
526         args.add(bundleContextArg);
527         assertMethodPresent(methods, new MethodAssertion("java.util.Set<org.opendaylight.controller.config.threads.java.NamingThreadFactoryModule>", "getDefaultModules", args));
528
529         args.clear();
530         args.add(new ArgumentAssertion("Class<? extends org.opendaylight.controller.config.api.annotations.AbstractServiceInterface>", "serviceInterface"));
531         assertMethodPresent(methods, new MethodAssertion("boolean", "isModuleImplementingServiceInterface", args));
532
533         assertEquals(methods.size(), expectedSize);
534
535     }
536
537     private void assertMethodPresent(Set<String> methods, MethodAssertion methodAssertion) {
538         assertTrue(String.format("Generated methods did not contain %s, generated methods: %s",
539                 methodAssertion.toString(), methods), methods.contains(methodAssertion.toString()));
540     }
541
542     private void assertAsyncEventBusModuleMXBean(MbeASTVisitor visitor) {
543         assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
544                 + ".threads.java", visitor.packageName);
545         assertEquals("AsyncEventBusModuleMXBean", visitor.type);
546
547         assertEquals("Incorrenct number of generated methods", 2,
548                 visitor.methods.size());
549     }
550
551     private void assertAbstractDynamicThreadPoolModule(MbeASTVisitor visitor) {
552         assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
553                 + ".threads.java", visitor.packageName);
554         assertNotNull(visitor.javadoc);
555         assertContains(visitor.descriptionAnotValue,
556                 "threadpool-dynamic description");
557         assertEquals("AbstractDynamicThreadPoolModule", visitor.type);
558         assertContains(visitor.implmts,
559                 PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
560                         + ".threads.java.DynamicThreadPoolModuleMXBean",
561                 Module.class.getCanonicalName(),
562                 PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
563                         + ".threads.ScheduledThreadPoolServiceInterface",
564                 PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
565                         + ".threads.ThreadPoolServiceInterface");
566         assertEquals(2, visitor.constructors.size());
567         Set<String> fieldDeclarations = visitor.fieldDeclarations;
568         assertDeclaredField(fieldDeclarations,
569                 "private java.lang.Long maximumSize");
570         assertDeclaredField(fieldDeclarations,
571                 "private javax.management.ObjectName threadfactory");
572         assertDeclaredField(fieldDeclarations,
573                 "private java.util.concurrent.ThreadFactory threadfactoryDependency");
574         assertDeclaredField(fieldDeclarations,
575                 "private java.lang.Long keepAlive=10");
576         assertDeclaredField(fieldDeclarations,
577                 "private java.lang.Long coreSize");
578         assertDeclaredField(fieldDeclarations, "private byte[] binary");
579         assertEquals(22, fieldDeclarations.size());
580
581         assertEquals(1, visitor.requireIfc.size());
582         String reqIfc = visitor.requireIfc.get("setThreadfactory");
583         assertNotNull("Missing generated setter for threadfactory", reqIfc);
584         assertContains(reqIfc, PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
585                 + ".threads.ThreadFactoryServiceInterface");
586
587         assertEquals("Incorrenct number of generated methods", 27,
588                 visitor.methods.size());
589         assertEquals("Incorrenct number of generated method descriptions", 3,
590                 visitor.methodDescriptions.size());
591         assertEquals("Incorrenct number of generated method javadoc", 3,
592                 visitor.methodJavadoc.size());
593         assertNotNull("Missing javadoc for setMaximumSize method " + visitor.methodJavadoc,
594                 visitor.methodJavadoc.get("void setMaximumSize(java.lang.Long maximumSize)"));
595     }
596
597     private void assertDeclaredField(Set<String> fieldDeclarations,
598             String declaration) {
599         assertTrue("Missing field " + declaration + ", got: "
600                 + fieldDeclarations,
601                 fieldDeclarations.contains(declaration + ";\n"));
602     }
603
604     private static class SieASTVisitor extends ASTVisitor {
605         protected String packageName, descriptionAnotValue, sieAnnotValue,
606                 sieAnnotOsgiRegistrationType, type, extnds, javadoc;
607         protected Map<String, String> methodDescriptions = Maps.newHashMap();
608
609         @Override
610         public boolean visit(PackageDeclaration node) {
611             packageName = node.getName().toString();
612             return super.visit(node);
613         }
614
615         @Override
616         public boolean visit(NormalAnnotation node) {
617             if (node.getTypeName().toString()
618                     .equals(Description.class.getCanonicalName())) {
619                 if (node.getParent() instanceof TypeDeclaration) {
620                     descriptionAnotValue = node.values().get(0).toString();
621                 } else if (node.getParent() instanceof MethodDeclaration) {
622                     String descr = node.values().get(0).toString();
623                     methodDescriptions.put(((MethodDeclaration) node
624                             .getParent()).getName().toString(), descr);
625                 }
626             } else if (node
627                     .getTypeName()
628                     .toString()
629                     .equals(ServiceInterfaceAnnotation.class.getCanonicalName())) {
630                 String text1 = node.values().get(0).toString();
631                 String text2 = node.values().get(1).toString();
632                 if (text1.contains("value")) {
633                     sieAnnotValue = text1;
634                     sieAnnotOsgiRegistrationType = text2;
635                 } else {
636                     sieAnnotValue = text2;
637                     sieAnnotOsgiRegistrationType = text1;
638                 }
639             }
640             return super.visit(node);
641         }
642
643         @Override
644         public boolean visit(TypeDeclaration node) {
645             javadoc = node.getJavadoc() == null ? null : node.getJavadoc()
646                     .toString();
647             type = node.getName().toString();
648             List<?> superIfcs = node.superInterfaceTypes();
649             extnds = superIfcs != null && !superIfcs.isEmpty() ? superIfcs
650                     .toString() : null;
651             return super.visit(node);
652         }
653     }
654
655     private static class MbeASTVisitor extends SieASTVisitor {
656         private String implmts;
657         private final Set<String> fieldDeclarations = Sets.newHashSet();
658         private final Set<String> constructors = Sets.newHashSet();
659         private final Set<String> methods = new HashSet<String>();
660         private final Map<String, String> requireIfc = Maps.newHashMap();
661         private final Map<String, String> methodJavadoc = Maps.newHashMap();
662
663         @Override
664         public boolean visit(NormalAnnotation node) {
665             boolean result = super.visit(node);
666             if (node.getTypeName().toString()
667                     .equals(RequireInterface.class.getCanonicalName())
668                     && node.getParent() instanceof MethodDeclaration) {
669                 // remember only top level description annotation
670                 String reqVal = node.values().get(0).toString();
671                 requireIfc.put(((MethodDeclaration) node.getParent()).getName()
672                         .toString(), reqVal);
673             }
674             return result;
675         }
676
677         @Override
678         public boolean visit(FieldDeclaration node) {
679             fieldDeclarations.add(node.toString());
680             return super.visit(node);
681         }
682
683         @Override
684         public boolean visit(MethodDeclaration node) {
685             if (node.isConstructor())
686                 constructors.add(node.toString());
687             else {
688                 String methodSignature = node.getReturnType2() + " " + node.getName() + "(";
689                 boolean first = true;
690                 for (Object o : node.parameters()) {
691                     if (first){
692                         first = false;
693                     } else {
694                         methodSignature += ",";
695                     }
696                     methodSignature += o.toString();
697                 }
698                 methodSignature += ")";
699                 methods.add(methodSignature);
700                 if (node.getJavadoc() != null) {
701                     methodJavadoc.put(methodSignature, node.getJavadoc().toString());
702                 }
703             }
704             return super.visit(node);
705         }
706
707         @Override
708         public boolean visit(TypeDeclaration node) {
709             boolean visit = super.visit(node);
710             List<?> superIfcs = node.superInterfaceTypes();
711             implmts = superIfcs != null && !superIfcs.isEmpty() ? superIfcs
712                     .toString() : null;
713             extnds = node.getSuperclassType() == null ? null : node
714                     .getSuperclassType().toString();
715             return visit;
716         }
717
718     }
719
720     private void assertContains(String source, String... contained) {
721         for (String string : contained) {
722             assertThat(source, containsString(string));
723         }
724     }
725
726     private void processGeneratedCode(Collection<File> files,
727             Map<String, ASTVisitor> verifiers) throws IOException {
728         ASTParser parser = ASTParser.newParser(AST.JLS3);
729         Map<?, ?> options = JavaCore.getOptions();
730         JavaCore.setComplianceOptions(JavaCore.VERSION_1_7, options);
731         parser.setCompilerOptions(options);
732
733         parser.setKind(ASTParser.K_COMPILATION_UNIT);
734
735         for (File file : files) {
736             char[] source = readFileAsChars(file);
737             parser.setSource(source);
738             parser.setResolveBindings(true);
739
740             CompilationUnit cu = (CompilationUnit) parser.createAST(null);
741             // Check for compilation problems in generated file
742             for (IProblem c : cu.getProblems()) {
743                 // 1610613332 = Syntax error, annotations are only available if
744                 // source level is 5.0
745                 if (c.getID() == 1610613332)
746                     continue;
747                 // 1610613332 = Syntax error, parameterized types are only
748                 // available if source level is 5.0
749                 if (c.getID() == 1610613329)
750                     continue;
751                 if (c.getID() == 1610613328) // 'for each' statements are only available if source level is 5.0
752                     continue;
753                 fail("Error in generated source code " + file + ":"
754                         + c.getSourceLineNumber() + " id: " + c.getID() + " message:"  + c.toString());
755             }
756
757             ASTVisitor visitor = verifiers.get(file.getName());
758             if (visitor == null)
759                 fail("Unknown generated file " + file.getName());
760             cu.accept(visitor);
761
762         }
763     }
764
765     public static char[] readFileAsChars(File file) throws IOException {
766         List<String> readLines = Files
767                 .readLines(file, Charset.forName("utf-8"));
768         char[] retVal = new char[0];
769         for (String string : readLines) {
770             char[] line = string.toCharArray();
771             int beforeLength = retVal.length;
772             retVal = Arrays.copyOf(retVal, retVal.length + line.length + 1);
773             System.arraycopy(line, 0, retVal, beforeLength, line.length);
774             retVal[retVal.length - 1] = '\n';
775         }
776         return retVal;
777     }
778
779     private static class MethodAssertion extends ArgumentAssertion{
780
781         private List<ArgumentAssertion> arguments;
782
783
784         MethodAssertion(String type, String name, List<ArgumentAssertion> arguments) {
785             super(type, name);
786             this.arguments = arguments;
787         }
788
789         MethodAssertion(String type, String name) {
790             this(type, name, Collections.<ArgumentAssertion>emptyList());
791         }
792
793         @Override
794         public String toString() {
795             final StringBuffer sb = new StringBuffer();
796             sb.append(type).append(' ');
797             sb.append(name).append('(');
798
799             int i = 0;
800             for (ArgumentAssertion argument : arguments) {
801                 sb.append(argument.type).append(' ');
802                 sb.append(argument.name);
803                 if(++i != arguments.size())
804                     sb.append(',');
805             }
806             sb.append(')');
807             return sb.toString();
808         }
809     }
810
811     private static class ArgumentAssertion {
812
813         protected final String type, name;
814
815         private ArgumentAssertion(String type, String name) {
816             this.type = type;
817             this.name = name;
818         }
819
820         @Override
821         public String toString() {
822             final StringBuffer sb = new StringBuffer();
823             sb.append(type).append(' ');
824             sb.append(name);
825             return sb.toString();
826         }
827     }
828 }