package io.takari.maven.plugins.compile; import static io.takari.maven.testing.TestResources.cp; import static io.takari.maven.testing.TestResources.rm; import static io.takari.maven.testing.TestResources.touch; import java.io.File; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProject; import org.junit.Assert; import org.junit.Assume; import org.junit.Test; public class CompileIncrementalTest extends AbstractCompileTest { public CompileIncrementalTest(String compilerId) { super(compilerId); } @Test public void testBasic() throws Exception { File basedir = compile("compile-incremental/basic"); File classes = new File(basedir, "target/classes"); mojos.assertBuildOutputs(classes, "basic/Basic.class"); // no-change rebuild compile(basedir); mojos.assertBuildOutputs(classes, new String[0]); mojos.assertDeletedOutputs(classes, new String[0]); mojos.assertCarriedOverOutputs(classes, "basic/Basic.class"); // change cp(basedir, "src/main/java/basic/Basic.java-modified", "src/main/java/basic/Basic.java"); compile(basedir); mojos.assertBuildOutputs(classes, "basic/Basic.class"); } @Test public void testBasic_identicalClassfile() throws Exception { Assume.assumeTrue("jdt".equals(compilerId)); // javac eagerly deletes and recreates all outputs File basedir = compile("compile-incremental/basic"); File classes = new File(basedir, "target/classes"); mojos.assertBuildOutputs(classes, "basic/Basic.class"); // move back timestamp, round to 10s to accommodate filesystem timestamp rounding long timestamp = System.currentTimeMillis() - 20000L; timestamp = timestamp - (timestamp % 10000L); new File(classes, "basic/Basic.class").setLastModified(timestamp); cp(basedir, "src/main/java/basic/Basic.java-comment", "src/main/java/basic/Basic.java"); compile(basedir); mojos.assertBuildOutputs(classes, "basic/Basic.class"); Assert.assertEquals(timestamp, new File(classes, "basic/Basic.class").lastModified()); } @Test public void testDelete() throws Exception { File basedir = compile("compile-incremental/delete"); mojos.assertBuildOutputs(new File(basedir, "target/classes"), "delete/Delete.class", "delete/Keep.class"); Assert.assertTrue(new File(basedir, "src/main/java/delete/Delete.java").delete()); compile(basedir); if ("jdt".equals(compilerId)) { mojos.assertCarriedOverOutputs(new File(basedir, "target/classes"), "delete/Keep.class"); } else { mojos.assertBuildOutputs(new File(basedir, "target/classes"), "delete/Keep.class"); } mojos.assertDeletedOutputs(new File(basedir, "target/classes"), "delete/Delete.class"); } @Test public void testError() throws Exception { ErrorMessage expected = new ErrorMessage(compilerId); expected.setSnippets("jdt", "ERROR Error.java [4:11] Errorr cannot be resolved to a type"); expected.setSnippets("javac", "ERROR Error.java [4:11]", "cannot find symbol", "class Errorr", "location", "class error.Error"); File basedir = resources.getBasedir("compile-incremental/error"); try { compile(basedir); Assert.fail(); } catch (MojoExecutionException e) { // expected } mojos.assertBuildOutputs(basedir, new String[0]); mojos.assertMessage(basedir, "src/main/java/error/Error.java", expected); // no change rebuild, should still fail with the same error try { compile(basedir); Assert.fail(); } catch (MojoExecutionException e) { // expected } mojos.assertBuildOutputs(basedir, new String[0]); mojos.assertMessage(basedir, "src/main/java/error/Error.java", expected); // fixed the error should clear the message during next build cp(basedir, "src/main/java/error/Error.java-fixed", "src/main/java/error/Error.java"); compile(basedir); mojos.assertBuildOutputs(basedir, "target/classes/error/Error.class"); mojos.assertMessages(basedir, "target/classes/error/Error.class", new String[0]); } @Test public void testClasspath_reactor() throws Exception { File basedir = resources.getBasedir("compile-incremental/classpath"); File moduleB = new File(basedir, "module-b"); File moduleA = new File(basedir, "module-a"); compile(moduleB); MavenProject projectA = mojos.readMavenProject(moduleA); addDependency(projectA, "module-b", new File(moduleB, "target/classes")); mojos.compile(projectA); mojos.assertBuildOutputs(moduleA, "target/classes/modulea/ModuleA.class"); // no change rebuild mojos.flushClasspathCaches(); mojos.compile(projectA); mojos.assertBuildOutputs(moduleA, new String[0]); // dependency changed "structurally" mojos.flushClasspathCaches(); cp(moduleB, "src/main/java/moduleb/ModuleB.java-method", "src/main/java/moduleb/ModuleB.java"); touch(moduleB, "src/main/java/moduleb/ModuleB.java"); compile(moduleB); mojos.compile(projectA); mojos.assertBuildOutputs(moduleA, "target/classes/modulea/ModuleA.class"); } @Test public void testClasspath_dependency() throws Exception { File basedir = resources.getBasedir("compile-incremental/classpath"); File moduleB = new File(basedir, "module-b"); File moduleA = new File(basedir, "module-a"); MavenProject projectA = mojos.readMavenProject(moduleA); addDependency(projectA, "module-b", new File(moduleB, "module-b.jar")); mojos.compile(projectA); mojos.assertBuildOutputs(moduleA, "target/classes/modulea/ModuleA.class"); // no change rebuild projectA = mojos.readMavenProject(moduleA); addDependency(projectA, "module-b", new File(moduleB, "module-b.jar")); mojos.compile(projectA); mojos.assertBuildOutputs(moduleA, new String[0]); // dependency changed "structurally" projectA = mojos.readMavenProject(moduleA); addDependency(projectA, "module-b", new File(moduleB, "module-b-method.jar")); mojos.compile(projectA); mojos.assertBuildOutputs(moduleA, "target/classes/modulea/ModuleA.class"); } @Test public void testClasspath_changedClassMovedFromProjectToDependency() throws Exception { File basedir = resources.getBasedir("compile-incremental/move"); File moduleB = new File(basedir, "module-b"); File moduleA = new File(basedir, "module-a"); compile(moduleB); MavenProject projectA = mojos.readMavenProject(moduleA); addDependency(projectA, "module-b", new File(moduleB, "target/classes")); mojos.compile(projectA); mojos.assertBuildOutputs(moduleA, "target/classes/modulea/ModuleA.class", "target/classes/moving/Moving.class"); mojos.flushClasspathCaches(); // change and move class to the dependency rm(moduleA, "src/main/java/moving/Moving.java"); cp(moduleA, "src/main/java/modulea/ModuleA.java-changed", "src/main/java/modulea/ModuleA.java"); cp(moduleB, "src/main/java/moving/Moving.java-changed", "src/main/java/moving/Moving.java"); compile(moduleB); mojos.compile(projectA); mojos.assertDeletedOutputs(moduleA, "target/classes/moving/Moving.class"); } @Test public void testReferenceNested() throws Exception { File basedir = compile("compile-incremental/reference-nested"); File classes = new File(basedir, "target/classes"); mojos.assertBuildOutputs(classes, "nested/A.class", "nested/Asecondary.class", "nested/B.class"); cp(basedir, "src/main/java/nested/A.java-changed", "src/main/java/nested/A.java"); touch(basedir, "src/main/java/nested/B.java"); try { compile(basedir); Assert.fail(); } catch (MojoExecutionException e) { // TODO check error message } Assert.assertFalse(new File(classes, "nested/B.class").exists()); } }