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 javax.inject.Inject;
26  
27  import java.io.File;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.lang.reflect.InvocationTargetException;
31  import java.lang.reflect.Method;
32  import java.net.MalformedURLException;
33  import java.net.URL;
34  import java.net.URLClassLoader;
35  import java.util.ArrayList;
36  import java.util.Arrays;
37  import java.util.List;
38  import java.util.Properties;
39  
40  import org.codehaus.modello.verifier.VerifierException;
41  import org.codehaus.plexus.compiler.Compiler;
42  import org.codehaus.plexus.compiler.CompilerConfiguration;
43  import org.codehaus.plexus.compiler.CompilerException;
44  import org.codehaus.plexus.compiler.CompilerMessage;
45  import org.codehaus.plexus.compiler.CompilerResult;
46  import org.codehaus.plexus.util.FileUtils;
47  
48  import static org.codehaus.plexus.testing.PlexusExtension.getTestFile;
49  import static org.codehaus.plexus.testing.PlexusExtension.getTestPath;
50  import static org.junit.jupiter.api.Assertions.assertEquals;
51  import static org.junit.jupiter.api.Assertions.assertTrue;
52  
53  /**
54   * Base class for unit-tests of Modello plugins that generate java code.
55   *
56   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
57   * @see #compileGeneratedSources() compileGeneratedSources() method to compile generated sources
58   * @see #verifyCompiledGeneratedSources(String) verifyCompiledGeneratedSources(String) method to run a Verifier
59   *      class against compiled generated code
60   * @see org.codehaus.modello.verifier.Verifier Verifier base class for verifiers
61   */
62  public abstract class AbstractModelloJavaGeneratorTest extends AbstractModelloGeneratorTest {
63      private List<File> dependencies = new ArrayList<File>();
64  
65      private List<URL> urls = new ArrayList<URL>();
66  
67      private List<String> classPathElements = new ArrayList<String>();
68  
69      @Inject
70      private Compiler compiler = null; // todo
71  
72      protected AbstractModelloJavaGeneratorTest(String name) {
73          super(name);
74      }
75  
76      public void setUp() throws Exception {
77          FileUtils.deleteDirectory(getOutputClasses());
78  
79          assertTrue(getOutputClasses().mkdirs());
80      }
81  
82      protected File getOutputDirectory() {
83          return new File(super.getOutputDirectory(), "sources");
84      }
85  
86      protected File getOutputClasses() {
87          return new File(super.getOutputDirectory(), "classes");
88      }
89  
90      protected void addDependency(String groupId, String artifactId) {
91          File dependencyFile = getDependencyFile(groupId, artifactId);
92  
93          dependencies.add(dependencyFile);
94  
95          addClassPathFile(dependencyFile);
96      }
97  
98      protected File getDependencyFile(String groupId, String artifactId) {
99          // NOTE: dependency version is managed by project POM and not selectable by test
100 
101         String libsDir = System.getProperty("tests.lib.dir", "target/test-libs");
102         File dependencyFile = new File(libsDir, artifactId + ".jar");
103 
104         assertTrue(dependencyFile.isFile(), "Can't find dependency: " + dependencyFile.getAbsolutePath());
105 
106         return dependencyFile;
107     }
108 
109     public List<File> getClasspath() {
110         return dependencies;
111     }
112 
113     protected String getModelloVersion() throws IOException {
114         Properties properties = new Properties(System.getProperties());
115 
116         if (properties.getProperty("version") == null) {
117             InputStream is = this.getClass()
118                     .getResourceAsStream("/META-INF/maven/org.codehaus.modello/modello-test/pom.properties");
119 
120             if (is != null) {
121                 properties.load(is);
122             }
123         }
124 
125         return properties.getProperty("version");
126     }
127 
128     protected void compileGeneratedSources() throws IOException, CompilerException {
129         compileGeneratedSources(getName(), 8);
130     }
131 
132     protected void compileGeneratedSources(int minJavaSource) throws IOException, CompilerException {
133         compileGeneratedSources(getName(), minJavaSource);
134     }
135 
136     protected void compileGeneratedSources(String verifierId, int minJavaSource) throws IOException, CompilerException {
137         String runtimeVersion = System.getProperty("java.specification.version");
138         if (runtimeVersion.startsWith("1.")) {
139             runtimeVersion = runtimeVersion.substring(2);
140         }
141         int runtimeSource = Integer.parseInt(runtimeVersion);
142 
143         String javaSource;
144         // review when Java will drop support for Java 8 as source
145         if (runtimeSource <= 21) {
146             javaSource = Integer.toString(Math.max(minJavaSource, 8));
147         } else {
148             javaSource = Integer.toString(Math.max(minJavaSource, 8));
149         }
150 
151         compileGeneratedSources(verifierId, javaSource);
152     }
153 
154     private void compileGeneratedSources(String verifierId, String javaSource) throws IOException, CompilerException {
155         File generatedSources = getOutputDirectory();
156         File destinationDirectory = getOutputClasses();
157 
158         addDependency("junit", "junit");
159         addDependency("org.junit.jupiter", "junit-jupiter-api");
160         addDependency("org.opentest4j", "opentest4j");
161         addDependency("org.codehaus.plexus", "plexus-utils");
162         addDependency("org.codehaus.plexus", "plexus-xml");
163         // for plexus-xml 4
164         // addDependency("org.apache.maven", "maven-api-xml");
165         // addDependency("org.apache.maven", "maven-xml-impl");
166         addDependency("org.codehaus.modello", "modello-test");
167 
168         String[] classPathElements = new String[dependencies.size() + 2];
169         classPathElements[0] = getTestPath("target/classes");
170         classPathElements[1] = getTestPath("target/test-classes");
171 
172         for (int i = 0; i < dependencies.size(); i++) {
173             classPathElements[i + 2] = ((File) dependencies.get(i)).getAbsolutePath();
174         }
175 
176         File verifierDirectory = getTestFile("src/test/verifiers/" + verifierId);
177         String[] sourceDirectories;
178         if (verifierDirectory.canRead()) {
179             sourceDirectories = new String[] {verifierDirectory.getAbsolutePath(), generatedSources.getAbsolutePath()};
180         } else {
181             sourceDirectories = new String[] {generatedSources.getAbsolutePath()};
182         }
183 
184         CompilerConfiguration configuration = new CompilerConfiguration();
185         configuration.setClasspathEntries(Arrays.asList(classPathElements));
186         configuration.setSourceLocations(Arrays.asList(sourceDirectories));
187         configuration.setOutputLocation(destinationDirectory.getAbsolutePath());
188         configuration.setDebug(true);
189 
190         configuration.setSourceVersion(javaSource);
191         configuration.setTargetVersion(javaSource);
192 
193         CompilerResult result = compiler.performCompile(configuration);
194 
195         List<CompilerMessage> errors = new ArrayList<CompilerMessage>(0);
196         for (CompilerMessage compilerMessage : result.getCompilerMessages()) {
197             if (compilerMessage.isError()) {
198                 errors.add(compilerMessage);
199             }
200         }
201 
202         assertEquals(0, errors.size(), "There was compilation errors: " + errors);
203     }
204 
205     /**
206      * Run a verifier class in a classloader context where compiled generated sources are available
207      *
208      * @param verifierClassName the class name of the verifier class
209      */
210     protected void verifyCompiledGeneratedSources(String verifierClassName) {
211         addClassPathFile(getOutputClasses());
212 
213         addClassPathFile(getTestFile("target/classes"));
214 
215         addClassPathFile(getTestFile("target/test-classes"));
216 
217         ClassLoader oldCCL = Thread.currentThread().getContextClassLoader();
218         URLClassLoader classLoader = URLClassLoader.newInstance(urls.toArray(new URL[urls.size()]), null);
219 
220         Thread.currentThread().setContextClassLoader(classLoader);
221 
222         try {
223             Class<?> clazz = classLoader.loadClass(verifierClassName);
224 
225             Method verify = clazz.getMethod("verify", new Class[0]);
226 
227             try {
228                 verify.invoke(clazz.getDeclaredConstructor().newInstance(), new Object[0]);
229             } catch (InvocationTargetException ex) {
230                 throw ex.getCause();
231             }
232         } catch (Throwable throwable) {
233             throw new VerifierException("Error verifying modello tests: " + throwable.getMessage(), throwable);
234         } finally {
235             Thread.currentThread().setContextClassLoader(oldCCL);
236         }
237     }
238 
239     protected void addClassPathFile(File file) {
240         assertTrue(file.exists(), "File doesn't exists: " + file.getAbsolutePath());
241 
242         try {
243             urls.add(file.toURI().toURL());
244         } catch (MalformedURLException e) {
245             throw new RuntimeException(e);
246         }
247 
248         classPathElements.add(file.getAbsolutePath());
249     }
250 
251     protected void printClasspath(URLClassLoader classLoader) {
252         URL[] urls = classLoader.getURLs();
253 
254         for (URL url : urls) {
255             System.out.println(url);
256         }
257     }
258 
259     protected void assertGeneratedFileExists(String filename) {
260         File file = new File(getOutputDirectory(), filename);
261 
262         assertTrue(file.canRead(), "Missing generated file: " + file.getAbsolutePath());
263 
264         assertTrue(file.length() > 0, "The generated file is empty.");
265     }
266 
267     protected List<String> getClassPathElements() {
268         return classPathElements;
269     }
270 }