/** * Copyright © 2006-2016 Web Cohesion (info@webcohesion.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.webcohesion.enunciate; import com.webcohesion.enunciate.module.DependencySpec; import com.webcohesion.enunciate.module.DependingModuleAwareModule; import com.webcohesion.enunciate.module.EnunciateModule; import com.webcohesion.enunciate.module.TypeDetectingModule; import org.jgrapht.DirectedGraph; import org.jgrapht.graph.DefaultEdge; import org.junit.Test; import org.reflections.Reflections; import org.reflections.adapters.MetadataAdapter; import javax.tools.JavaCompiler; import javax.tools.ToolProvider; import java.io.*; import java.net.URI; import java.net.URL; import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import static org.junit.Assert.*; /** * @author Ryan Heaton */ public class EnunciateTest { @Test public void testBuildModuleGraph() throws Exception { final Map<String, TestModule> myModules = new HashMap<String, TestModule>(); List<String> moduleCallOrder = new ArrayList<String>(); myModules.put("a", new TestModule("a", moduleCallOrder)); myModules.put("b", new TestModule("b", moduleCallOrder)); myModules.put("c", new TestModule("c", moduleCallOrder)); myModules.put("d", new TestModule("d", moduleCallOrder, "a")); myModules.put("e", new TestModule("e", moduleCallOrder, "b", "c")); myModules.put("f", new TestModule("f", moduleCallOrder, "d", "e")); Enunciate enunciate = new Enunciate(); DirectedGraph<String, DefaultEdge> graph = enunciate.buildModuleGraph(myModules); assertEquals(1, myModules.get("a").dependingModules.size()); assertEquals("d", myModules.get("a").dependingModules.iterator().next()); assertEquals(1, myModules.get("b").dependingModules.size()); assertEquals("e", myModules.get("b").dependingModules.iterator().next()); assertEquals(1, myModules.get("c").dependingModules.size()); assertEquals("e", myModules.get("c").dependingModules.iterator().next()); assertEquals(1, myModules.get("d").dependingModules.size()); assertEquals("f", myModules.get("d").dependingModules.iterator().next()); assertEquals(1, myModules.get("e").dependingModules.size()); assertEquals("f", myModules.get("e").dependingModules.iterator().next()); myModules.put("a", new TestModule("a", moduleCallOrder, "f")); //replace 'a' with a circular dependency. try { enunciate.buildModuleGraph(myModules); fail(); } catch (EnunciateException e) { //fall through... } } @Test public void testCallOrder() throws Exception { final Map<String, TestModule> myModules = new HashMap<String, TestModule>(); List<String> moduleCallOrder = Collections.synchronizedList(new ArrayList<String>()); myModules.put("a", new TestModule("a", moduleCallOrder)); myModules.put("b", new TestModule("b", moduleCallOrder)); myModules.put("c", new TestModule("c", moduleCallOrder)); myModules.put("d", new TestModule("d", moduleCallOrder, "a")); myModules.put("e", new TestModule("e", moduleCallOrder, "b", "c")); myModules.put("f", new TestModule("f", moduleCallOrder, "d", "e")); Enunciate enunciate = new Enunciate(); enunciate.composeEngine(new EnunciateContext(null, null, null, null, null, null), myModules, enunciate.buildModuleGraph(myModules)).toBlocking().single(); assertEquals(6, moduleCallOrder.size()); assertTrue("'a' should be before 'd': " + moduleCallOrder, moduleCallOrder.indexOf("a") < moduleCallOrder.indexOf("d")); assertTrue("'b' should be before 'e': " + moduleCallOrder, moduleCallOrder.indexOf("b") < moduleCallOrder.indexOf("e")); assertTrue("'c' should be before 'e': " + moduleCallOrder, moduleCallOrder.indexOf("c") < moduleCallOrder.indexOf("e")); assertTrue("'d' should be before 'f': " + moduleCallOrder, moduleCallOrder.indexOf("d") < moduleCallOrder.indexOf("f")); assertTrue("'e' should be before 'f': " + moduleCallOrder, moduleCallOrder.indexOf("e") < moduleCallOrder.indexOf("f")); } @Test public void testClasspathScanning() throws Exception { Enunciate enunciate = new Enunciate(); enunciate.setModules(Arrays.asList((EnunciateModule) new TestModule("test", new ArrayList<String>()))); Reflections reflections = enunciate.loadApiReflections(buildTestClasspath()); Set<String> scannedEntries = reflections.getStore().get(EnunciateReflectionsScanner.class.getSimpleName()).keySet(); assertTrue(scannedEntries.contains("enunciate.Class1")); assertTrue(scannedEntries.contains("enunciate.Class2")); assertTrue(scannedEntries.contains("enunciate.Class3")); assertTrue(scannedEntries.contains("enunciate/Class1.java")); assertEquals(4, scannedEntries.size()); assertFalse(scannedEntries.isEmpty()); } private List<URL> buildTestClasspath() throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); Enunciate.URLFileObject source1 = new Enunciate.URLFileObject(getClass().getResource("/enunciate/Class1.java"), "utf-8"); File outputDir1 = createTempDir(); List<String> options = Arrays.asList("-d", outputDir1.getAbsolutePath() ); assertTrue(compiler.getTask(null, null, null, options, null, Arrays.asList(source1)).call()); File sourceFile1 = new File(new File(outputDir1, "enunciate"), "Class1.java"); InputStream in = getClass().getResourceAsStream("/enunciate/Class1.java"); OutputStream out = new FileOutputStream(sourceFile1); byte[] buffer = new byte[1024]; //buffer of 1K should be fine. int len; while ((len = in.read(buffer)) > 0) { out.write(buffer, 0, len); } in.close(); out.close(); Enunciate.URLFileObject source2 = new Enunciate.URLFileObject(getClass().getResource("/enunciate/Class2.java"), "utf-8"); File outputDir2 = createTempDir(); options = Arrays.asList("-d", outputDir2.getAbsolutePath() ); assertTrue(compiler.getTask(null, null, null, options, null, Arrays.asList(source2)).call()); Enunciate.URLFileObject source3 = new Enunciate.URLFileObject(getClass().getResource("/enunciate/Class3.java"), "utf-8"); File outputDir3 = createTempDir(); options = Arrays.asList("-d", outputDir3.getAbsolutePath() ); assertTrue(compiler.getTask(null, null, null, options, null, Arrays.asList(source3)).call()); File jar1 = File.createTempFile("EnunciateTest", ".jar"); jar(jar1, outputDir1); File jar2 = File.createTempFile("EnunciateTest", ".jar"); jar(jar2, outputDir2); return Arrays.asList(jar1.toURI().toURL(), jar2.toURI().toURL(), outputDir3.toURI().toURL()); } public void jar(File toFile, File... dirs) throws IOException { if (!toFile.getParentFile().exists()) { toFile.getParentFile().mkdirs(); } byte[] buffer = new byte[2 * 1024]; //buffer of 2K should be fine. JarOutputStream jarout = new JarOutputStream(new FileOutputStream(toFile)); for (File dir : dirs) { URI baseURI = dir.toURI(); ArrayList<File> files = new ArrayList<File>(); buildFileList(files, dir); for (File file : files) { JarEntry entry = new JarEntry(baseURI.relativize(file.toURI()).getPath()); jarout.putNextEntry(entry); if (!file.isDirectory()) { FileInputStream in = new FileInputStream(file); int len; while ((len = in.read(buffer)) > 0) { jarout.write(buffer, 0, len); } in.close(); } // Complete the entry jarout.closeEntry(); } } jarout.close(); } protected void buildFileList(List<File> list, File... dirs) { for (File dir : dirs) { for (File file : dir.listFiles()) { if (file.isDirectory()) { buildFileList(list, file); } else { list.add(file); } } } } private File createTempDir() throws IOException { final Double random = Math.random() * 10000; //this random name is applied to avoid an "access denied" error on windows. final File tempDir = File.createTempFile("EnunciateTest" + random.intValue(), ""); tempDir.delete(); tempDir.mkdirs(); return tempDir; } private class TestModule implements EnunciateModule, DependingModuleAwareModule, DependencySpec, TypeDetectingModule { private final String name; private final Set<String> moduleDependencies; private Set<String> dependingModules; private final List<String> moduleCallOrder; private EnunciateContext context; private TestModule(String name, List<String> moduleCallOrder, String... moduleDependencies) { this.name = name; this.moduleCallOrder = moduleCallOrder; this.moduleDependencies = new TreeSet<String>(Arrays.asList(moduleDependencies)); } @Override public void acknowledgeDependingModules(Set<String> dependingModules) { this.dependingModules = dependingModules; } @Override public String getName() { return this.name; } @Override public boolean isEnabled() { return true; } @Override public List<DependencySpec> getDependencySpecifications() { return Arrays.asList((DependencySpec)this); } @Override public boolean accept(EnunciateModule module) { return this.moduleDependencies.contains(module.getName()); } @Override public boolean isFulfilled() { return true; } @Override public void init(Enunciate engine) { } @Override public void init(EnunciateContext context) { this.context = context; } @Override public void call(EnunciateContext context) { this.moduleCallOrder.add(getName()); } @Override public boolean typeDetected(Object type, MetadataAdapter metadata) { return true; } } }