View Javadoc
1   package org.codehaus.modello;
2   
3   /*
4    * Copyright (c) 2004, Codehaus.org
5    *
6    * Permission is hereby granted, free of charge, to any person obtaining a copy of
7    * this software and associated documentation files (the "Software"), to deal in
8    * the Software without restriction, including without limitation the rights to
9    * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10   * of the Software, and to permit persons to whom the Software is furnished to do
11   * so, subject to the following conditions:
12   *
13   * The above copyright notice and this permission notice shall be included in all
14   * copies or substantial portions of the Software.
15   *
16   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22   * SOFTWARE.
23   */
24  
25  import java.io.File;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.lang.reflect.InvocationTargetException;
29  import java.lang.reflect.Method;
30  import java.net.MalformedURLException;
31  import java.net.URL;
32  import java.net.URLClassLoader;
33  import java.util.ArrayList;
34  import java.util.Arrays;
35  import java.util.List;
36  import java.util.Properties;
37  
38  import org.codehaus.modello.verifier.VerifierException;
39  import org.codehaus.plexus.compiler.Compiler;
40  import org.codehaus.plexus.compiler.CompilerConfiguration;
41  import org.codehaus.plexus.compiler.CompilerException;
42  import org.codehaus.plexus.compiler.CompilerMessage;
43  import org.codehaus.plexus.compiler.CompilerResult;
44  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
45  import org.codehaus.plexus.util.FileUtils;
46  
47  /**
48   * Base class for unit-tests of Modello plugins that generate java code.
49   *
50   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
51   * @see #compileGeneratedSources() compileGeneratedSources() method to compile generated sources
52   * @see #verifyCompiledGeneratedSources(String) verifyCompiledGeneratedSources(String) method to run a Verifier
53   *      class against compiled generated code
54   * @see org.codehaus.modello.verifier.Verifier Verifier base class for verifiers
55   */
56  public abstract class AbstractModelloJavaGeneratorTest extends AbstractModelloGeneratorTest {
57      private List<File> dependencies = new ArrayList<File>();
58  
59      private List<URL> urls = new ArrayList<URL>();
60  
61      private List<String> classPathElements = new ArrayList<String>();
62  
63      protected AbstractModelloJavaGeneratorTest(String name) {
64          super(name);
65      }
66  
67      protected void setUp() throws Exception {
68          super.setUp();
69  
70          FileUtils.deleteDirectory(getOutputClasses());
71  
72          assertTrue(getOutputClasses().mkdirs());
73      }
74  
75      protected File getOutputDirectory() {
76          return new File(super.getOutputDirectory(), "sources");
77      }
78  
79      protected File getOutputClasses() {
80          return new File(super.getOutputDirectory(), "classes");
81      }
82  
83      protected void addDependency(String groupId, String artifactId) {
84          File dependencyFile = getDependencyFile(groupId, artifactId);
85  
86          dependencies.add(dependencyFile);
87  
88          addClassPathFile(dependencyFile);
89      }
90  
91      protected File getDependencyFile(String groupId, String artifactId) {
92          // NOTE: dependency version is managed by project POM and not selectable by test
93  
94          String libsDir = System.getProperty("tests.lib.dir", "target/test-libs");
95          File dependencyFile = new File(libsDir, artifactId + ".jar");
96  
97          assertTrue("Can't find dependency: " + dependencyFile.getAbsolutePath(), dependencyFile.isFile());
98  
99          return dependencyFile;
100     }
101 
102     public List<File> getClasspath() {
103         return dependencies;
104     }
105 
106     protected String getModelloVersion() throws IOException {
107         Properties properties = new Properties(System.getProperties());
108 
109         if (properties.getProperty("version") == null) {
110             InputStream is = getResourceAsStream("/META-INF/maven/org.codehaus.modello/modello-test/pom.properties");
111 
112             if (is != null) {
113                 properties.load(is);
114             }
115         }
116 
117         return properties.getProperty("version");
118     }
119 
120     protected void compileGeneratedSources() throws IOException, CompilerException {
121         compileGeneratedSources(getName());
122     }
123 
124     protected void compileGeneratedSources(int minJavaSource) throws IOException, CompilerException {
125         compileGeneratedSources(getName(), minJavaSource);
126     }
127 
128     protected void compileGeneratedSources(String verifierId) throws IOException, CompilerException {
129         String runtimeVersion = System.getProperty("java.specification.version");
130         if (runtimeVersion.startsWith("1.")) {
131             runtimeVersion = runtimeVersion.substring(2);
132         }
133         int runtimeSource = Integer.parseInt(runtimeVersion);
134 
135         String javaSource;
136         if (runtimeSource <= 8) {
137             javaSource = "1.3";
138         } else if (runtimeSource <= 11) {
139             javaSource = "6";
140         } else if (runtimeSource <= 17) {
141             javaSource = "7";
142         } else {
143             javaSource = "8";
144         }
145 
146         compileGeneratedSources(verifierId, javaSource);
147     }
148 
149     protected void compileGeneratedSources(String verifierId, int minJavaSource) throws IOException, CompilerException {
150         String runtimeVersion = System.getProperty("java.specification.version");
151         if (runtimeVersion.startsWith("1.")) {
152             runtimeVersion = runtimeVersion.substring(2);
153         }
154         int runtimeSource = Integer.parseInt(runtimeVersion);
155 
156         String javaSource;
157         if (runtimeSource <= 8) {
158             javaSource = "1." + Math.max(minJavaSource, 3);
159         } else if (runtimeSource <= 11) {
160             javaSource = Integer.toString(Math.max(minJavaSource, 6));
161         } else if (runtimeSource <= 17) {
162             javaSource = Integer.toString(Math.max(minJavaSource, 7));
163         } else {
164             javaSource = Integer.toString(Math.max(minJavaSource, 8));
165             ;
166         }
167 
168         compileGeneratedSources(verifierId, javaSource);
169     }
170 
171     private void compileGeneratedSources(String verifierId, String javaSource) throws IOException, CompilerException {
172         File generatedSources = getOutputDirectory();
173         File destinationDirectory = getOutputClasses();
174 
175         addDependency("junit", "junit");
176         addDependency("org.codehaus.plexus", "plexus-utils");
177         addDependency("org.codehaus.plexus", "plexus-xml");
178         // for plexus-xml 4
179         // addDependency("org.apache.maven", "maven-api-xml");
180         // addDependency("org.apache.maven", "maven-xml-impl");
181         addDependency("org.codehaus.modello", "modello-test");
182 
183         String[] classPathElements = new String[dependencies.size() + 2];
184         classPathElements[0] = getTestPath("target/classes");
185         classPathElements[1] = getTestPath("target/test-classes");
186 
187         for (int i = 0; i < dependencies.size(); i++) {
188             classPathElements[i + 2] = ((File) dependencies.get(i)).getAbsolutePath();
189         }
190 
191         File verifierDirectory = getTestFile("src/test/verifiers/" + verifierId);
192         String[] sourceDirectories;
193         if (verifierDirectory.canRead()) {
194             sourceDirectories = new String[] {verifierDirectory.getAbsolutePath(), generatedSources.getAbsolutePath()};
195         } else {
196             sourceDirectories = new String[] {generatedSources.getAbsolutePath()};
197         }
198 
199         Compiler compiler;
200         try {
201             compiler = lookup(Compiler.class, "javac");
202         } catch (ComponentLookupException e) {
203             throw new RuntimeException(e.getMessage(), e);
204         }
205 
206         CompilerConfiguration configuration = new CompilerConfiguration();
207         configuration.setClasspathEntries(Arrays.asList(classPathElements));
208         configuration.setSourceLocations(Arrays.asList(sourceDirectories));
209         configuration.setOutputLocation(destinationDirectory.getAbsolutePath());
210         configuration.setDebug(true);
211 
212         configuration.setSourceVersion(javaSource);
213         configuration.setTargetVersion(javaSource);
214 
215         CompilerResult result = compiler.performCompile(configuration);
216 
217         List<CompilerMessage> errors = new ArrayList<CompilerMessage>(0);
218         for (CompilerMessage compilerMessage : result.getCompilerMessages()) {
219             if (compilerMessage.isError()) {
220                 errors.add(compilerMessage);
221             }
222         }
223 
224         assertEquals("There was compilation errors: " + errors, 0, errors.size());
225     }
226 
227     /**
228      * Run a verifier class in a classloader context where compiled generated sources are available
229      *
230      * @param verifierClassName the class name of the verifier class
231      */
232     protected void verifyCompiledGeneratedSources(String verifierClassName) {
233         addClassPathFile(getOutputClasses());
234 
235         addClassPathFile(getTestFile("target/classes"));
236 
237         addClassPathFile(getTestFile("target/test-classes"));
238 
239         ClassLoader oldCCL = Thread.currentThread().getContextClassLoader();
240         URLClassLoader classLoader = URLClassLoader.newInstance(urls.toArray(new URL[urls.size()]), null);
241 
242         Thread.currentThread().setContextClassLoader(classLoader);
243 
244         try {
245             Class<?> clazz = classLoader.loadClass(verifierClassName);
246 
247             Method verify = clazz.getMethod("verify", new Class[0]);
248 
249             try {
250                 verify.invoke(clazz.getDeclaredConstructor().newInstance(), new Object[0]);
251             } catch (InvocationTargetException ex) {
252                 throw ex.getCause();
253             }
254         } catch (Throwable throwable) {
255             throw new VerifierException("Error verifying modello tests: " + throwable.getMessage(), throwable);
256         } finally {
257             Thread.currentThread().setContextClassLoader(oldCCL);
258         }
259     }
260 
261     protected void addClassPathFile(File file) {
262         assertTrue("File doesn't exists: " + file.getAbsolutePath(), file.exists());
263 
264         try {
265             urls.add(file.toURI().toURL());
266         } catch (MalformedURLException e) {
267             throw new RuntimeException(e);
268         }
269 
270         classPathElements.add(file.getAbsolutePath());
271     }
272 
273     protected void printClasspath(URLClassLoader classLoader) {
274         URL[] urls = classLoader.getURLs();
275 
276         for (URL url : urls) {
277             System.out.println(url);
278         }
279     }
280 
281     protected void assertGeneratedFileExists(String filename) {
282         File file = new File(getOutputDirectory(), filename);
283 
284         assertTrue("Missing generated file: " + file.getAbsolutePath(), file.canRead());
285 
286         assertTrue("The generated file is empty.", file.length() > 0);
287     }
288 
289     /**
290      * Check if a Java 5 feature test should be skipped, since it is not supported by current test environment.
291      *
292      * @return <code>true</code> if Java 5 is not available, then feature test should be skipped by caller
293      */
294     protected boolean skipJava5FeatureTest() {
295         String javaVersion = System.getProperty("java.specification.version", "1.5");
296 
297         if ("1.5".compareTo(javaVersion) > 0) {
298             System.out.println(
299                     "Skipped Java 5 feature test, not supported by current test environment (" + javaVersion + ")");
300             return true;
301         }
302 
303         return false;
304     }
305 
306     protected List<String> getClassPathElements() {
307         return classPathElements;
308     }
309 }