1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin.compiler;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.UncheckedIOException;
24 import java.net.URI;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.time.Instant;
28 import java.time.temporal.ChronoUnit;
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33
34 import org.apache.maven.api.PathScope;
35 import org.apache.maven.api.Project;
36 import org.apache.maven.api.Session;
37 import org.apache.maven.api.di.Inject;
38 import org.apache.maven.api.di.Provides;
39 import org.apache.maven.api.di.Singleton;
40 import org.apache.maven.api.model.Build;
41 import org.apache.maven.api.model.Model;
42 import org.apache.maven.api.plugin.Log;
43 import org.apache.maven.api.plugin.testing.Basedir;
44 import org.apache.maven.api.plugin.testing.InjectMojo;
45 import org.apache.maven.api.plugin.testing.MojoExtension;
46 import org.apache.maven.api.plugin.testing.MojoParameter;
47 import org.apache.maven.api.plugin.testing.MojoTest;
48 import org.apache.maven.api.plugin.testing.stubs.ProducedArtifactStub;
49 import org.apache.maven.api.plugin.testing.stubs.ProjectStub;
50 import org.apache.maven.api.plugin.testing.stubs.SessionMock;
51 import org.apache.maven.api.services.ArtifactManager;
52 import org.apache.maven.api.services.MessageBuilderFactory;
53 import org.apache.maven.api.services.ToolchainManager;
54 import org.apache.maven.impl.DefaultMessageBuilderFactory;
55 import org.apache.maven.impl.InternalSession;
56 import org.apache.maven.plugin.compiler.stubs.CompilerStub;
57 import org.junit.jupiter.api.Test;
58
59 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
60 import static org.junit.jupiter.api.Assertions.assertEquals;
61 import static org.junit.jupiter.api.Assertions.assertFalse;
62 import static org.junit.jupiter.api.Assertions.assertNull;
63 import static org.junit.jupiter.api.Assertions.assertThrows;
64 import static org.junit.jupiter.api.Assertions.assertTrue;
65 import static org.junit.jupiter.api.Assertions.fail;
66 import static org.mockito.ArgumentMatchers.any;
67 import static org.mockito.ArgumentMatchers.eq;
68 import static org.mockito.ArgumentMatchers.startsWith;
69 import static org.mockito.Mockito.doAnswer;
70 import static org.mockito.Mockito.doReturn;
71 import static org.mockito.Mockito.mock;
72 import static org.mockito.Mockito.never;
73 import static org.mockito.Mockito.verify;
74
75 @MojoTest
76 public class CompilerMojoTestCase {
77
78 private static final String LOCAL_REPO = "/target/local-repo";
79
80 @Inject
81 private Session session;
82
83
84
85
86
87
88
89
90 private static void assertCompilerStubOutputFileExists(AbstractCompilerMojo mojo) {
91 try {
92 Files.delete(assertOutputFileExists(mojo, CompilerStub.OUTPUT_FILE));
93 } catch (IOException e) {
94 throw new UncheckedIOException(e);
95 }
96 }
97
98
99
100
101
102
103
104
105
106 private static Path assertOutputFileExists(AbstractCompilerMojo mojo, String first, String... more) {
107 Path file = mojo.getOutputDirectory().resolve(Path.of(first, more));
108 assertTrue(Files.isRegularFile(file), () -> "File not found: " + file);
109 return file;
110 }
111
112
113
114
115
116
117
118
119 private static void assertOutputFileDoesNotExist(AbstractCompilerMojo mojo, String first, String... more) {
120 Path file = mojo.getOutputDirectory().resolve(Path.of(first, more));
121 assertFalse(Files.exists(file), () -> "File should not exist: " + file);
122 }
123
124
125
126
127
128 @Test
129 @Basedir("${basedir}/target/test-classes/unit/compiler-basic-test")
130 public void testCompilerBasic(
131 @InjectMojo(goal = "compile", pom = "plugin-config.xml") CompilerMojo compileMojo,
132 @InjectMojo(goal = "testCompile", pom = "plugin-config.xml")
133 @MojoParameter(name = "compileSourceRoots", value = "${project.basedir}/src/test/java")
134 TestCompilerMojo testCompileMojo) {
135
136 Log log = mock(Log.class);
137 compileMojo.logger = log;
138 compileMojo.execute();
139 verify(log).warn(startsWith("No explicit value set for --release or --target."));
140 assertOutputFileExists(compileMojo, "foo", "TestCompile0.class");
141 assertTrue(
142 session.getArtifactPath(compileMojo.projectArtifact).isPresent(),
143 "MCOMPILER-94: artifact file should only be null if there is nothing to compile");
144
145 testCompileMojo.execute();
146 assertOutputFileExists(testCompileMojo, "foo", "TestCompile0Test.class");
147 assertOutputFileDoesNotExist(compileMojo, "foo", "TestCompile0Test.class");
148 }
149
150
151
152
153
154 @Test
155 @Basedir("${basedir}/target/test-classes/unit/compiler-basic-sourcetarget")
156 public void testCompilerBasicSourceTarget(
157 @InjectMojo(goal = "compile", pom = "plugin-config.xml") CompilerMojo compileMojo) {
158
159 Log log = mock(Log.class);
160 compileMojo.logger = log;
161 compileMojo.execute();
162 verify(log, never()).warn(startsWith("No explicit value set for --release or --target."));
163 }
164
165
166
167
168 @Test
169 @Basedir("${basedir}/target/test-classes/unit/compiler-empty-source-test")
170 public void testCompilerEmptySource(
171 @InjectMojo(goal = "compile", pom = "plugin-config.xml") CompilerMojo compileMojo,
172 @InjectMojo(goal = "testCompile", pom = "plugin-config.xml")
173 @MojoParameter(name = "compileSourceRoots", value = "${basedir}/src/test/java")
174 TestCompilerMojo testCompileMojo) {
175
176 compileMojo.execute();
177 assertFalse(Files.exists(compileMojo.getOutputDirectory()));
178 assertNull(
179 session.getArtifactPath(compileMojo.projectArtifact).orElse(null),
180 "MCOMPILER-94: artifact file should be null if there is nothing to compile");
181
182 testCompileMojo.execute();
183 assertFalse(Files.exists(testCompileMojo.getOutputDirectory()));
184 }
185
186
187
188
189 @Test
190 @Basedir("${basedir}/target/test-classes/unit/compiler-includes-excludes-test")
191 public void testCompilerIncludesExcludes(
192 @InjectMojo(goal = "compile", pom = "plugin-config.xml") CompilerMojo compileMojo,
193 @InjectMojo(goal = "testCompile", pom = "plugin-config.xml")
194 @MojoParameter(name = "compileSourceRoots", value = "${project.basedir}/src/test/java")
195 TestCompilerMojo testCompileMojo) {
196
197 compileMojo.execute();
198 assertOutputFileDoesNotExist(compileMojo, "foo", "TestCompile2.class");
199 assertOutputFileDoesNotExist(compileMojo, "foo", "TestCompile3.class");
200 assertOutputFileExists(compileMojo, "foo", "TestCompile4.class");
201
202 testCompileMojo.execute();
203 assertOutputFileDoesNotExist(testCompileMojo, "foo", "TestCompile2TestCase.class");
204 assertOutputFileDoesNotExist(testCompileMojo, "foo", "TestCompile3TestCase.class");
205 assertOutputFileExists(testCompileMojo, "foo", "TestCompile4TestCase.class");
206 assertOutputFileDoesNotExist(compileMojo, "foo", "TestCompile4TestCase.class");
207 }
208
209
210
211
212 @Test
213 @Basedir("${basedir}/target/test-classes/unit/compiler-fork-test")
214 public void testCompilerFork(
215 @InjectMojo(goal = "compile", pom = "plugin-config.xml") CompilerMojo compileMojo,
216 @InjectMojo(goal = "testCompile", pom = "plugin-config.xml")
217 @MojoParameter(name = "compileSourceRoots", value = "${project.basedir}/src/test/java")
218 TestCompilerMojo testCompileMojo) {
219
220
221 String javaHome = System.getenv("JAVA_HOME");
222 if (javaHome != null) {
223 String command = new File(javaHome, "bin/javac").getPath();
224 compileMojo.executable = command;
225 testCompileMojo.executable = command;
226 }
227 compileMojo.execute();
228 assertOutputFileExists(compileMojo, "foo", "TestCompile1.class");
229
230 testCompileMojo.execute();
231 assertOutputFileExists(testCompileMojo, "foo", "TestCompile1TestCase.class");
232 assertOutputFileDoesNotExist(compileMojo, "foo", "TestCompile1TestCase.class");
233 }
234
235
236
237
238
239 @Test
240 @Basedir("${basedir}/target/test-classes/unit/compiler-one-output-file-test")
241 public void testOneOutputFileForAllInput(
242 @InjectMojo(goal = "compile", pom = "plugin-config.xml") CompilerMojo compileMojo,
243 @InjectMojo(goal = "testCompile", pom = "plugin-config.xml")
244 @MojoParameter(name = "compileSourceRoots", value = "${project.basedir}/src/test/java")
245 TestCompilerMojo testCompileMojo) {
246
247 assertEquals(CompilerStub.COMPILER_ID, compileMojo.compilerId);
248 compileMojo.execute();
249 assertCompilerStubOutputFileExists(compileMojo);
250
251 assertEquals(CompilerStub.COMPILER_ID, testCompileMojo.compilerId);
252 testCompileMojo.execute();
253 assertCompilerStubOutputFileExists(testCompileMojo);
254 }
255
256
257
258
259 @Test
260 @Basedir("${basedir}/target/test-classes/unit/compiler-args-test")
261 public void testCompilerArgs(@InjectMojo(goal = "compile", pom = "plugin-config.xml") CompilerMojo compileMojo) {
262
263 assertEquals(CompilerStub.COMPILER_ID, compileMojo.compilerId);
264 compileMojo.execute();
265
266 assertCompilerStubOutputFileExists(compileMojo);
267 assertArrayEquals(
268 new String[] {"key1=value1", "-Xlint", "-my&special:param-with+chars/not>allowed_in_XML_element_names"},
269 compileMojo.compilerArgs.toArray(String[]::new));
270
271 List<String> options = CompilerStub.getOptions();
272 assertArrayEquals(
273 new String[] {
274 "--module-version",
275 "1.0-SNAPSHOT",
276 "key1=value1",
277 "-Xlint",
278 "-my&special:param-with+chars/not>allowed_in_XML_element_names",
279 "param",
280 "value"
281 },
282 options.toArray(String[]::new));
283 }
284
285
286
287
288 @Test
289 @Basedir("${basedir}/target/test-classes/unit/compiler-implicit-test")
290 public void testImplicitFlagNone(
291 @InjectMojo(goal = "compile", pom = "plugin-config-none.xml") CompilerMojo compileMojo) {
292
293 assertEquals("none", compileMojo.implicit);
294 compileMojo.execute();
295 }
296
297
298
299
300 @Test
301 @Basedir("${basedir}/target/test-classes/unit/compiler-implicit-test")
302 public void testImplicitFlagNotSet(
303 @InjectMojo(goal = "compile", pom = "plugin-config-not-set.xml") CompilerMojo compileMojo) {
304
305 assertNull(compileMojo.implicit);
306 compileMojo.execute();
307 }
308
309
310
311
312
313
314
315
316
317 @Test
318 @Basedir("${basedir}/target/test-classes/unit/compiler-modular-project")
319 public void testModularProject(
320 @InjectMojo(goal = "compile", pom = "plugin-config.xml") CompilerMojo compileMojo,
321 @InjectMojo(goal = "testCompile", pom = "plugin-config.xml")
322 @MojoParameter(name = "compileSourceRoots", value = "${project.basedir}/src/test/java")
323 TestCompilerMojo testCompileMojo) {
324
325 compileMojo.execute();
326 assertOutputFileExists(compileMojo, SourceDirectory.MODULE_INFO + SourceDirectory.CLASS_FILE_SUFFIX);
327 assertOutputFileExists(compileMojo, "foo", "TestModular.class");
328
329 testCompileMojo.execute();
330 assertOutputFileExists(testCompileMojo, "foo", "TestModularTestCase.class");
331 assertOutputFileDoesNotExist(compileMojo, "foo", "TestModularTestCase.class");
332 }
333
334
335
336
337 @Test
338 @Basedir("${basedir}/target/test-classes/unit/compiler-fail-test")
339 public void testCompileFailure(@InjectMojo(goal = "compile", pom = "plugin-config.xml") CompilerMojo compileMojo) {
340 assertThrows(CompilationFailureException.class, compileMojo::execute, "Should throw an exception");
341 assertOutputFileExists(compileMojo, "..", "javac.args");
342 }
343
344
345
346
347 @Test
348 @Basedir("${basedir}/target/test-classes/unit/compiler-failonerror-test")
349 public void testCompileFailOnError(
350 @InjectMojo(goal = "compile", pom = "plugin-config.xml") CompilerMojo compileMojo) {
351
352 try {
353 compileMojo.execute();
354 } catch (CompilationFailureException e) {
355 fail("The compilation error should have been consumed because failOnError = false");
356 }
357 assertOutputFileExists(compileMojo, "..", "javac.args");
358 }
359
360
361
362
363
364 @Test
365 @Basedir("${basedir}/target/test-classes/unit/compiler-skip-main")
366 public void testCompileSkipMain(
367 @InjectMojo(goal = "compile", pom = "plugin-config.xml") CompilerMojo compileMojo,
368 @InjectMojo(goal = "testCompile", pom = "plugin-config.xml")
369 @MojoParameter(name = "compileSourceRoots", value = "${project.basedir}/src/test/java")
370 TestCompilerMojo testCompileMojo) {
371
372 compileMojo.skipMain = true;
373 compileMojo.execute();
374 assertOutputFileDoesNotExist(compileMojo, "foo", "TestSkipMainCompile0.class");
375
376 testCompileMojo.execute();
377 assertOutputFileExists(testCompileMojo, "foo", "TestSkipMainCompile0Test.class");
378 assertOutputFileDoesNotExist(compileMojo, "foo", "TestSkipMainCompile0Test.class");
379 }
380
381
382
383
384
385 @Test
386 @Basedir("${basedir}/target/test-classes/unit/compiler-skip-test")
387 public void testCompileSkipTest(
388 @InjectMojo(goal = "compile", pom = "plugin-config.xml") CompilerMojo compileMojo,
389 @InjectMojo(goal = "testCompile", pom = "plugin-config.xml")
390 @MojoParameter(name = "compileSourceRoots", value = "${project.basedir}/src/test/java")
391 TestCompilerMojo testCompileMojo) {
392
393 compileMojo.execute();
394 assertOutputFileExists(compileMojo, "foo/TestSkipTestCompile0.class");
395
396 testCompileMojo.skip = true;
397 testCompileMojo.execute();
398 assertOutputFileDoesNotExist(testCompileMojo, "foo", "TestSkipTestCompile0Test.class");
399 assertOutputFileDoesNotExist(compileMojo, "foo", "TestSkipTestCompile0Test.class");
400 }
401
402 @Provides
403 @Singleton
404 @SuppressWarnings("unused")
405 private static InternalSession createSession() {
406 InternalSession session = SessionMock.getMockSession(MojoExtension.getBasedir() + LOCAL_REPO);
407
408 ToolchainManager toolchainManager = mock(ToolchainManager.class);
409 doReturn(toolchainManager).when(session).getService(ToolchainManager.class);
410
411 doAnswer(iom -> Instant.now().minus(200, ChronoUnit.MILLIS))
412 .when(session)
413 .getStartTime();
414
415 var junit = new ProducedArtifactStub("junit", "junit", null, "3.8.1", "jar");
416
417 MessageBuilderFactory messageBuilderFactory = new DefaultMessageBuilderFactory();
418 doReturn(messageBuilderFactory).when(session).getService(MessageBuilderFactory.class);
419
420 Map<String, String> props = new HashMap<>();
421 props.put("basedir", MojoExtension.getBasedir());
422 doReturn(props).when(session).getUserProperties();
423
424 List<Path> artifacts = new ArrayList<>();
425 try {
426 Path artifactFile;
427 String localRepository = System.getProperty("localRepository");
428 if (localRepository != null) {
429 artifactFile = Path.of(
430 localRepository,
431 "org",
432 "junit",
433 "jupiter",
434 "junit-jupiter-api",
435 "5.10.2",
436 "junit-jupiter-api-5.10.2.jar");
437 } else {
438
439 String junitURI = Test.class.getResource("Test.class").toURI().toString();
440 junitURI = junitURI.substring("jar:".length(), junitURI.indexOf('!'));
441 artifactFile = new File(URI.create(junitURI)).toPath();
442 }
443 ArtifactManager artifactManager = session.getService(ArtifactManager.class);
444 artifactManager.setPath(junit, artifactFile);
445 artifacts.add(artifactFile);
446 } catch (Exception e) {
447 throw new RuntimeException("Unable to setup junit jar path", e);
448 }
449
450 doAnswer(iom -> List.of()).when(session).resolveDependencies(any(), eq(PathScope.MAIN_COMPILE));
451 doAnswer(iom -> artifacts).when(session).resolveDependencies(any(), eq(PathScope.TEST_COMPILE));
452
453 return session;
454 }
455
456 @Provides
457 @Singleton
458 @SuppressWarnings("unused")
459 private static Project createProject() {
460 ProjectStub stub = new ProjectStub();
461 var artifact = new ProducedArtifactStub("myGroupId", "myArtifactId", null, "1.0-SNAPSHOT", "jar");
462 stub.setMainArtifact(artifact);
463 stub.setModel(Model.newBuilder()
464 .groupId(artifact.getGroupId())
465 .artifactId(artifact.getArtifactId())
466 .version(artifact.getVersion().toString())
467 .build(Build.newBuilder()
468 .directory(MojoExtension.getBasedir() + "/target")
469 .outputDirectory(MojoExtension.getBasedir() + "/target/classes")
470 .sourceDirectory(MojoExtension.getBasedir() + "/src/main/java")
471 .testOutputDirectory(MojoExtension.getBasedir() + "/target/test-classes")
472 .build())
473 .build());
474 stub.setBasedir(Path.of(MojoExtension.getBasedir()));
475 return stub;
476 }
477 }