View Javadoc
1   package org.codehaus.plexus.compiler;
2   
3   /**
4    * The MIT License
5    *
6    * Copyright (c) 2004, The Codehaus
7    *
8    * Permission is hereby granted, free of charge, to any person obtaining a copy of
9    * this software and associated documentation files (the "Software"), to deal in
10   * the Software without restriction, including without limitation the rights to
11   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12   * of the Software, and to permit persons to whom the Software is furnished to do
13   * so, subject to the following conditions:
14   *
15   * The above copyright notice and this permission notice shall be included in all
16   * copies or substantial portions of the Software.
17   *
18   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24   * SOFTWARE.
25   */
26  import javax.inject.Inject;
27  
28  import java.io.File;
29  import java.security.CodeSource;
30  import java.util.ArrayList;
31  import java.util.Collection;
32  import java.util.Collections;
33  import java.util.Iterator;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.stream.Collectors;
37  
38  import org.codehaus.plexus.testing.PlexusTest;
39  import org.codehaus.plexus.util.FileUtils;
40  import org.codehaus.plexus.util.StringUtils;
41  import org.junit.jupiter.api.Test;
42  
43  import static org.junit.jupiter.api.Assertions.assertEquals;
44  import static org.junit.jupiter.api.Assertions.assertNotNull;
45  import static org.junit.jupiter.api.Assertions.assertTrue;
46  
47  /**
48   *
49   */
50  @PlexusTest
51  public abstract class AbstractCompilerTest {
52      private boolean compilerDebug = false;
53  
54      private boolean compilerDeprecationWarnings = false;
55  
56      private boolean forceJavacCompilerUse = false;
57  
58      @Inject
59      private Map<String, Compiler> compilers;
60  
61      protected abstract String getRoleHint();
62  
63      protected void setCompilerDebug(boolean flag) {
64          compilerDebug = flag;
65      }
66  
67      protected void setCompilerDeprecationWarnings(boolean flag) {
68          compilerDeprecationWarnings = flag;
69      }
70  
71      public void setForceJavacCompilerUse(boolean forceJavacCompilerUse) {
72          this.forceJavacCompilerUse = forceJavacCompilerUse;
73      }
74  
75      protected final Compiler getCompiler() {
76          return compilers.get(getRoleHint());
77      }
78  
79      protected List<String> getClasspath() throws Exception {
80          List<String> cp = new ArrayList<>();
81  
82          cp.add(getJarPath("org.apache.commons.lang3.StringUtils").getAbsolutePath());
83  
84          return cp;
85      }
86  
87      /**
88       * Locates the jar a class was loaded from, so that a test can put a dependency of its own on the classpath it
89       * asks the compiler to use. The dependency is declared in the pom like any other, and found here through the
90       * class loader rather than by guessing at a path inside the local repository.
91       *
92       * @param className fully qualified name of a class in the wanted jar
93       * @return the jar holding that class
94       */
95      protected static File getJarPath(String className) throws Exception {
96          Class<?> type = Class.forName(className);
97          CodeSource source = type.getProtectionDomain().getCodeSource();
98          assertNotNull(source, "test prerequisite: no code source for " + className);
99  
100         File jar = new File(source.getLocation().toURI());
101         assertTrue(jar.canRead(), "test prerequisite: unreadable jar for " + className + ": " + jar);
102 
103         return jar;
104     }
105 
106     protected void configureCompilerConfig(CompilerConfiguration compilerConfig) {}
107 
108     @Test
109     public void testCompilingSources() throws Exception {
110         List<CompilerMessage> messages = new ArrayList<>();
111         Collection<String> files = new ArrayList<>();
112 
113         for (CompilerConfiguration compilerConfig : getCompilerConfigurations()) {
114             File outputDir = new File(compilerConfig.getOutputLocation());
115 
116             messages.addAll(getCompiler().performCompile(compilerConfig).getCompilerMessages());
117 
118             if (outputDir.isDirectory()) {
119                 files.addAll(normalizePaths(FileUtils.getFileNames(outputDir, null, null, false)));
120             }
121         }
122 
123         int numCompilerErrors = compilerErrorCount(messages);
124 
125         int numCompilerWarnings = compilerWarningCount(messages);
126 
127         int expectedErrors = expectedErrors();
128 
129         if (expectedErrors != numCompilerErrors) {
130             System.out.println(numCompilerErrors + " error(s) found:");
131             List<String> errors = new ArrayList<>();
132             for (CompilerMessage error : messages) {
133                 if (!error.isError()) {
134                     continue;
135                 }
136 
137                 System.out.println("----");
138                 System.out.println(error.getFile());
139                 System.out.println(error.getMessage());
140                 System.out.println("----");
141                 errors.add(error.getMessage());
142             }
143 
144             assertEquals(
145                     expectedErrors,
146                     numCompilerErrors,
147                     "Wrong number of compilation errors (" + numCompilerErrors + "/" + expectedErrors + ") : "
148                             + displayLines(errors));
149         }
150 
151         int expectedWarnings = expectedWarnings();
152         if (expectedWarnings != numCompilerWarnings) {
153             List<String> warnings = new ArrayList<>();
154             System.out.println(numCompilerWarnings + " warning(s) found:");
155             for (CompilerMessage warning : messages) {
156                 if (!isWarning(warning)) {
157                     continue;
158                 }
159 
160                 System.out.println("----");
161                 System.out.println(warning.getFile());
162                 System.out.println(warning.getMessage());
163                 System.out.println("----");
164                 warnings.add(warning.getMessage());
165             }
166 
167             assertEquals(
168                     expectedWarnings,
169                     numCompilerWarnings,
170                     "Wrong number (" + numCompilerWarnings + "/" + expectedWarnings + ") of compilation warnings: "
171                             + displayLines(warnings));
172         }
173 
174         List<String> expectedFiles = normalizePaths(expectedOutputFiles());
175         assertEquals(
176                 expectedFiles.size(),
177                 files.size(),
178                 "Number of expected output files does not match: " + files + " vs " + expectedFiles);
179         assertTrue(
180                 files.containsAll(expectedFiles),
181                 "Output files do not contain all expected files: expected=" + expectedFiles + " actual=" + files);
182     }
183 
184     protected String displayLines(List<String> warnings) {
185         // with java8 could be as simple as String.join(System.lineSeparator(), warnings)
186         StringBuilder sb = new StringBuilder(System.lineSeparator());
187         for (String warning : warnings) {
188             sb.append('-').append(warning).append(System.lineSeparator());
189         }
190         return sb.toString();
191     }
192 
193     private List<CompilerConfiguration> getCompilerConfigurations() throws Exception {
194         String sourceDir = "src/test-input/src/main";
195 
196         List<String> filenames = FileUtils.getFileNames(new File(sourceDir), "**/*.java", null, false, true);
197         Collections.sort(filenames);
198 
199         List<CompilerConfiguration> compilerConfigurations = new ArrayList<>();
200 
201         int index = 0;
202         for (Iterator<String> it = filenames.iterator(); it.hasNext(); index++) {
203             String filename = it.next();
204 
205             CompilerConfiguration compilerConfig = new CompilerConfiguration();
206 
207             compilerConfig.setDebug(compilerDebug);
208 
209             compilerConfig.setShowDeprecation(compilerDeprecationWarnings);
210 
211             compilerConfig.setClasspathEntries(getClasspath());
212 
213             compilerConfig.addSourceLocation(sourceDir);
214 
215             compilerConfig.setOutputLocation("target/" + getRoleHint() + "/classes-" + index);
216 
217             FileUtils.deleteDirectory(compilerConfig.getOutputLocation());
218 
219             compilerConfig.addInclude(filename);
220 
221             compilerConfig.setForceJavacCompilerUse(this.forceJavacCompilerUse);
222 
223             configureCompilerConfig(compilerConfig);
224 
225             String target = getTargetVersion();
226             if (StringUtils.isNotEmpty(target)) {
227                 compilerConfig.setTargetVersion(target);
228             }
229 
230             String source = getSourceVersion();
231             if (StringUtils.isNotEmpty(source)) {
232                 compilerConfig.setSourceVersion(source);
233             }
234 
235             compilerConfigurations.add(compilerConfig);
236         }
237 
238         return compilerConfigurations;
239     }
240 
241     public String getTargetVersion() {
242         return null;
243     }
244 
245     public String getSourceVersion() {
246         return null;
247     }
248 
249     private List<String> normalizePaths(Collection<String> relativePaths) {
250         return relativePaths.stream()
251                 .map(s -> s.replace(File.separatorChar, '/'))
252                 .collect(Collectors.toList());
253     }
254 
255     protected int compilerErrorCount(List<CompilerMessage> messages) {
256         int count = 0;
257 
258         for (CompilerMessage message : messages) {
259             count += message.isError() ? 1 : 0;
260         }
261 
262         return count;
263     }
264 
265     protected int compilerWarningCount(List<CompilerMessage> messages) {
266         int count = 0;
267 
268         for (CompilerMessage message : messages) {
269             count += isWarning(message) ? 1 : 0;
270         }
271 
272         return count;
273     }
274 
275     private static boolean isWarning(CompilerMessage message) {
276         return message.getKind() == CompilerMessage.Kind.WARNING
277                 || message.getKind() == CompilerMessage.Kind.MANDATORY_WARNING;
278     }
279 
280     protected int expectedErrors() {
281         return 1;
282     }
283 
284     protected int expectedWarnings() {
285         return 0;
286     }
287 
288     protected Collection<String> expectedOutputFiles() {
289         return Collections.emptyList();
290     }
291 
292     protected String getJavaVersion() {
293         String javaVersion = System.getProperty("java.version");
294         String realJavaVersion = javaVersion;
295 
296         int dotIdx = javaVersion.indexOf(".");
297         if (dotIdx > -1) {
298             int lastDot = dotIdx;
299 
300             // find the next dot, so we can trim up to this point.
301             dotIdx = javaVersion.indexOf(".", lastDot + 1);
302             if (dotIdx > lastDot) {
303                 javaVersion = javaVersion.substring(0, dotIdx);
304             }
305         }
306 
307         System.out.println("java.version is: " + realJavaVersion + "\ntrimmed java version is: " + javaVersion
308                 + "\ncomparison: \"1.5\".compareTo( \"" + javaVersion + "\" ) == " + ("1.5".compareTo(javaVersion))
309                 + "\n");
310 
311         return javaVersion;
312     }
313 }