package io.takari.maven.plugins.compile; import static io.takari.maven.testing.TestResources.assertFileContents; 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 java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.MojoExecution; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.xml.Xpp3Dom; import org.junit.Assert; import org.junit.Assume; import org.junit.Ignore; import org.junit.Test; import org.junit.runners.Parameterized.Parameters; import io.takari.maven.plugins.compile.AbstractCompileMojo.Proc; import io.takari.maven.plugins.compile.javac.CompilerJavac; import io.takari.maven.plugins.compile.jdt.CompilerJdt; public class AnnotationProcessingTest extends AbstractCompileTest { public AnnotationProcessingTest(String compilerId) { super(compilerId); } @Parameters(name = "{0}") public static Iterable<Object[]> compilers() { List<Object[]> compilers = new ArrayList<Object[]>(); compilers.add(new Object[] {"javac"}); compilers.add(new Object[] {"forked-javac"}); compilers.add(new Object[] {"jdt"}); return compilers; } private File procCompile(String projectName, Proc proc, Xpp3Dom... parameters) throws Exception, IOException { File basedir = resources.getBasedir(projectName); return procCompile(basedir, proc, parameters); } private File procCompile(File basedir, Proc proc, Xpp3Dom... parameters) throws Exception, IOException { File processor = compileAnnotationProcessor(); return processAnnotations(basedir, proc, processor, parameters); } private File processAnnotations(File basedir, Proc proc, File processor, Xpp3Dom... parameters) throws Exception { MavenProject project = mojos.readMavenProject(basedir); processAnnotations(project, processor, proc, parameters); return basedir; } protected void processAnnotations(MavenProject project, File processor, Proc proc, Xpp3Dom... parameters) throws Exception { MavenSession session = mojos.newMavenSession(project); processAnnotations(session, project, "compile", processor, proc, parameters); } protected void processAnnotations(MavenSession session, MavenProject project, String goal, File processor, Proc proc, Xpp3Dom... parameters) throws Exception { MojoExecution execution = mojos.newMojoExecution(goal); addDependency(project, "processor", new File(processor, "target/classes")); Xpp3Dom configuration = execution.getConfiguration(); if (proc != null) { configuration.addChild(newParameter("proc", proc.name())); } if (parameters != null) { for (Xpp3Dom parameter : parameters) { configuration.addChild(parameter); } } mojos.executeMojo(session, project, execution); } private File compileAnnotationProcessor() throws Exception, IOException { File processor = compile("compile-proc/processor"); cp(processor, "src/main/resources/META-INF/services/javax.annotation.processing.Processor", "target/classes/META-INF/services/javax.annotation.processing.Processor"); return processor; } @Test public void testProc_only() throws Exception { File basedir = procCompile("compile-proc/proc", Proc.only); mojos.assertBuildOutputs(new File(basedir, "target/generated-sources/annotations"), "proc/GeneratedSource.java", "proc/AnotherGeneratedSource.java"); } @Test public void testProc_none() throws Exception { File basedir = procCompile("compile-proc/proc", Proc.none); mojos.assertBuildOutputs(new File(basedir, "target"), "classes/proc/Source.class"); } @Test public void testProc_proc() throws Exception { File basedir = procCompile("compile-proc/proc", Proc.proc); mojos.assertBuildOutputs(new File(basedir, "target"), // "classes/proc/Source.class", // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/GeneratedSource.class", // "generated-sources/annotations/proc/AnotherGeneratedSource.java", // "classes/proc/AnotherGeneratedSource.class"); } @Test public void testProc_incrementalProcessorChange() throws Exception { File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc"); processAnnotations(basedir, Proc.proc, processor); mojos.assertBuildOutputs(new File(basedir, "target"), // "classes/proc/Source.class", // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/GeneratedSource.class", // "generated-sources/annotations/proc/AnotherGeneratedSource.java", // "classes/proc/AnotherGeneratedSource.class"); rm(processor, "target/classes/META-INF/services/javax.annotation.processing.Processor"); mojos.flushClasspathCaches(); processAnnotations(basedir, Proc.proc, processor); mojos.assertBuildOutputs(new File(basedir, "target"), "classes/proc/Source.class"); mojos.assertDeletedOutputs(new File(basedir, "target"), // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/GeneratedSource.class", // "generated-sources/annotations/proc/AnotherGeneratedSource.java", // "classes/proc/AnotherGeneratedSource.class"); } @Test public void testProc_dummyOutput() throws Exception { File basedir = procCompile("compile-proc/proc", Proc.proc, newProcessors("processor.Processor_dummyOutput")); mojos.assertBuildOutputs(new File(basedir, "target"), "classes/proc/Source.class"); } @Test public void testProcTypeReference() throws Exception { File basedir = procCompile("compile-proc/proc-type-reference", Proc.proc); mojos.assertBuildOutputs(new File(basedir, "target"), // "classes/proc/Source.class", // "classes/proc/GeneratedSourceSubclass.class", // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/GeneratedSource.class", // "generated-sources/annotations/proc/AnotherGeneratedSource.java", // "classes/proc/AnotherGeneratedSource.class"); } @Test public void testProc_createResource() throws Exception { File basedir = procCompile("compile-proc/proc", Proc.proc, newProcessors("processor.ProcessorCreateResource")); mojos.assertBuildOutputs(new File(basedir, "target"), // "classes/proc/Source.class", // "generated-sources/annotations/proc/GeneratedSource.java"); } @Test public void testProc_annotationProcessors() throws Exception { Xpp3Dom processors = newProcessors("processor.Processor"); File basedir = procCompile("compile-proc/proc", Proc.proc, processors); mojos.assertBuildOutputs(new File(basedir, "target"), // "classes/proc/Source.class", // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/GeneratedSource.class"); } @Test public void testProc_processorErrorMessage() throws Exception { Xpp3Dom processors = newProcessors("processor.ErrorMessageProcessor"); File basedir = resources.getBasedir("compile-proc/proc"); try { procCompile(basedir, Proc.only, processors); Assert.fail(); } catch (MojoExecutionException e) { // expected } mojos.assertBuildOutputs(new File(basedir, "target"), new String[0]); ErrorMessage expected = new ErrorMessage(compilerId); expected.setSnippets("jdt", "ERROR Source.java [6:14] test error message"); // TODO why 14? expected.setSnippets("javac", "ERROR Source.java [6:8] test error message"); mojos.assertMessage(basedir, "src/main/java/proc/Source.java", expected); Collection<String> pomMessages = mojos.getBuildContextLog().getMessages(new File(basedir, "pom.xml")); Assert.assertEquals(3, pomMessages.size()); // TODO assert actual messages are as expected } @Test public void testProc_messages() throws Exception { ErrorMessage expected = new ErrorMessage(compilerId); expected.setSnippets("javac", "ERROR BrokenSource.java [2:29]", "cannot find symbol"); expected.setSnippets("jdt", "ERROR BrokenSource.java [2:29]", "cannot be resolved to a type"); File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc"); String[] outputs; if (CompilerJdt.ID.equals(compilerId)) { outputs = new String[] {"classes/proc/Source.class" // , "generated-sources/annotations/proc/BrokenSource.java"}; } else { // TODO investigate why javac does not generate classes/proc/Source.class outputs = new String[] {"generated-sources/annotations/proc/BrokenSource.java"}; } Xpp3Dom processors = newProcessors("processor.BrokenProcessor"); try { processAnnotations(basedir, Proc.proc, processor, processors); Assert.fail(); } catch (MojoExecutionException e) { // expected } mojos.assertBuildOutputs(new File(basedir, "target"), outputs); assertProcMessage(basedir, "target/generated-sources/annotations/proc/BrokenSource.java", expected); // no change rebuild should produce the same messages try { processAnnotations(basedir, Proc.proc, processor, processors); Assert.fail(); } catch (MojoExecutionException e) { // expected } mojos.assertCarriedOverOutputs(new File(basedir, "target"), outputs); assertProcMessage(basedir, "target/generated-sources/annotations/proc/BrokenSource.java", expected); } private void assertProcMessage(File basedir, String path, ErrorMessage expected) throws Exception { // javac reports the same compilation error twice when Proc.proc Set<String> messages = new HashSet<String>(mojos.getBuildContextLog().getMessages(new File(basedir, path))); Assert.assertEquals(messages.toString(), 1, messages.size()); String message = messages.iterator().next(); Assert.assertTrue(expected.isMatch(message)); } @Test public void testProc_processorOptions() throws Exception { Xpp3Dom processors = newProcessors("processor.ProcessorWithOptions"); Xpp3Dom options = new Xpp3Dom("annotationProcessorOptions"); options.addChild(newParameter("optionA", "valueA")); options.addChild(newParameter("optionB", "valueB")); procCompile("compile-proc/proc", Proc.proc, processors, options); } @Test public void testProc_staleGeneratedSourcesCleanup() throws Exception { File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc"); processAnnotations(basedir, Proc.proc, processor); mojos.assertBuildOutputs(new File(basedir, "target"), // "classes/proc/Source.class", // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/GeneratedSource.class", // "generated-sources/annotations/proc/AnotherGeneratedSource.java", // "classes/proc/AnotherGeneratedSource.class"); // remove annotation cp(basedir, "src/main/java/proc/Source.java-remove-annotation", "src/main/java/proc/Source.java"); processAnnotations(basedir, Proc.proc, processor); mojos.assertDeletedOutputs(new File(basedir, "target"), // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/GeneratedSource.class", // "generated-sources/annotations/proc/AnotherGeneratedSource.java", // "classes/proc/AnotherGeneratedSource.class"); } @Test public void testProc_incrementalDeleteLastAnnotatedSource() throws Exception { File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc"); Xpp3Dom processors = newProcessors("processor.Processor"); // initial compilation processAnnotations(basedir, Proc.proc, processor, processors); mojos.assertBuildOutputs(new File(basedir, "target"), // "classes/proc/Source.class", // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/GeneratedSource.class"); // no-change rebuild processAnnotations(basedir, Proc.proc, processor, processors); mojos.assertCarriedOverOutputs(new File(basedir, "target"), // "classes/proc/Source.class", // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/GeneratedSource.class"); // remove annotated class rm(basedir, "src/main/java/proc/Source.java"); processAnnotations(basedir, Proc.proc, processor, processors); mojos.assertDeletedOutputs(new File(basedir, "target"), // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/Source.class", // "classes/proc/GeneratedSource.class"); } @Test public void testProc_nonIncrementalProcessor_onlyEX_deleteSource() throws Exception { File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc"); File target = new File(basedir, "target"); Xpp3Dom processors = newProcessors("processor.NonIncrementalProcessor"); processAnnotations(basedir, Proc.onlyEX, processor, processors); mojos.assertBuildOutputs(target, // "generated-sources/annotations/proc/NonIncrementalSource.java"); rm(basedir, "src/main/java/proc/Source.java"); processAnnotations(basedir, Proc.onlyEX, processor, processors); mojos.assertDeletedOutputs(target, // "generated-sources/annotations/proc/NonIncrementalSource.java"); } @Test public void testProc_projectSourceRoots() throws Exception { File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc"); MavenProject project = mojos.readMavenProject(basedir); addDependency(project, "processor", new File(processor, "target/classes")); mojos.compile(project, newParameter("proc", Proc.proc.name()), newProcessors("processor.Processor")); Assert.assertTrue(project.getCompileSourceRoots().contains(new File(basedir, "target/generated-sources/annotations").getAbsolutePath())); // TODO testCompile } @Test public void testIncrementalDelete() throws Exception { File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc-incremental-delete"); Xpp3Dom processors = newProcessors("processor.Processor"); // initial compilation processAnnotations(basedir, Proc.proc, processor, processors); mojos.assertBuildOutputs(new File(basedir, "target"), // "classes/proc/Keep.class", // "classes/proc/Source.class", // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/GeneratedSource.class"); // no-change rebuild processAnnotations(basedir, Proc.proc, processor, processors); mojos.assertCarriedOverOutputs(new File(basedir, "target"), // "classes/proc/Keep.class", // "classes/proc/Source.class", // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/GeneratedSource.class"); // remove annotated source rm(basedir, "src/main/java/proc/Source.java"); processAnnotations(basedir, Proc.proc, processor, processors); mojos.assertDeletedOutputs(new File(basedir, "target"), // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/Source.class", // "classes/proc/GeneratedSource.class"); } @Test public void testConvertGeneratedSourceToHandwritten() throws Exception { // this test demonstrates the following scenario // 1. annotation processor generates java source and the generated source is compiled by the compiler // 2. annotation is removed from original source and the generated source is moved to a dependency // assert original generatedSource.java and generatedSource.class are deleted File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc-incremental-move"); File moduleA = new File(basedir, "module-a"); File moduleB = new File(basedir, "module-b"); Xpp3Dom processors = newProcessors("processor.Processor"); mojos.compile(moduleB); MavenProject projectA = mojos.readMavenProject(moduleA); addDependency(projectA, "module-b", new File(moduleB, "target/classes")); processAnnotations(projectA, processor, Proc.proc, processors); mojos.assertBuildOutputs(new File(moduleA, "target"), // "classes/proc/Source.class", // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/GeneratedSource.class"); mojos.flushClasspathCaches(); // move generated source to module-b/src/main/java cp(moduleB, "src/main/java/proc/GeneratedSource.java-moved", "src/main/java/proc/GeneratedSource.java"); cp(moduleA, "src/main/java/modulea/ModuleA.java-new", "src/main/java/modulea/ModuleA.java"); cp(moduleA, "src/main/java/proc/Source.java-remove-annotation", "src/main/java/proc/Source.java"); mojos.compile(moduleB); mojos.assertBuildOutputs(moduleB, "target/classes/proc/GeneratedSource.class"); projectA = mojos.readMavenProject(moduleA); addDependency(projectA, "module-b", new File(moduleB, "target/classes")); processAnnotations(projectA, processor, Proc.proc, processors); mojos.assertBuildOutputs(new File(moduleA, "target"), // "classes/proc/Source.class", "classes/modulea/ModuleA.class"); mojos.assertDeletedOutputs(new File(moduleA, "target"), // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/GeneratedSource.class"); } private Xpp3Dom newProcessors(String... processors) { Xpp3Dom annotationProcessors = new Xpp3Dom("annotationProcessors"); for (String processor : processors) { annotationProcessors.addChild(newParameter("processor", processor)); } return annotationProcessors; } @Test public void testRequireProc() throws Exception { File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/require-proc"); try { processAnnotations(basedir, null, processor); Assert.fail(); } catch (IllegalArgumentException expected) { // TODO assert message } processAnnotations(basedir, null, null); } @Test public void testRecompile() throws Exception { /** * <pre> * Source.java -> Source.class * \-> GeneratedSource.java -> GeneratedSource.class * ^ * Client.java -> Client.class * </pre> */ File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc-incremental-recompile"); Xpp3Dom processors = newProcessors("processor.ProcessorSiblingBody"); Xpp3Dom options = new Xpp3Dom("annotationProcessorOptions"); options.addChild(newParameter("basedir", new File(basedir, "src/main/java").getCanonicalPath())); processAnnotations(basedir, Proc.proc, processor, processors, options); mojos.assertBuildOutputs(new File(basedir, "target"), // "classes/proc/Source.class", // "classes/proc/Client.class", // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/GeneratedSource.class"); cp(basedir, "src/main/java/proc/GeneratedSource.body-changed", "src/main/java/proc/GeneratedSource.body"); touch(basedir, "src/main/java/proc/Source.java"); processAnnotations(basedir, Proc.proc, processor, processors, options); mojos.assertBuildOutputs(new File(basedir, "target"), // "classes/proc/Source.class", // "classes/proc/Client.class", // "generated-sources/annotations/proc/GeneratedSource.java", // "classes/proc/GeneratedSource.class"); } @Test public void testProc_processorLastRound() throws Exception { Xpp3Dom processors = newProcessors("processor.ProcessorLastRound"); File basedir = procCompile("compile-proc/proc", Proc.onlyEX, processors); mojos.assertBuildOutputs(new File(basedir, "target"), // "classes/types.lst"); assertFileContents("proc.Source\n", basedir, "target/classes/types.lst"); } @Test public void testIncremental_proc_only() throws Exception { // the point of this test is to assert that changes to annotations trigger affected sources reprocessing when proc=only // note sourcepath=disable, otherwise proc:only is all-or-nothing File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc-incremental-proconly"); File generatedSources = new File(basedir, "target/generated-sources/annotations"); Xpp3Dom processors = newProcessors("processor.Processor"); compile(basedir, processor, "compile-only"); processAnnotations(basedir, Proc.only, processor, processors, newParameter("sourcepath", "disable")); mojos.assertBuildOutputs(generatedSources, "proc/GeneratedConcrete.java", "proc/GeneratedAbstract.java", "proc/GeneratedAnother.java"); cp(basedir, "src/main/java/proc/Abstract.java-remove-annotation", "src/main/java/proc/Abstract.java"); compile(basedir, processor, "compile-only"); processAnnotations(basedir, Proc.only, processor, processors, newParameter("sourcepath", "disable")); mojos.assertDeletedOutputs(generatedSources, "proc/GeneratedConcrete.java", "proc/GeneratedAbstract.java"); if (CompilerJdt.ID.equals(compilerId)) { mojos.assertCarriedOverOutputs(generatedSources, "proc/GeneratedAnother.java"); } else { mojos.assertBuildOutputs(generatedSources, "proc/GeneratedAnother.java"); } } private void compile(File basedir, File processor, String executionId) throws Exception { MavenProject project = mojos.readMavenProject(basedir); MavenSession session = mojos.newMavenSession(project); MojoExecution execution = mojos.newMojoExecution(); MojoExecution cloned = new MojoExecution(execution.getMojoDescriptor(), executionId, null); cloned.setConfiguration(execution.getConfiguration()); execution.getConfiguration().addChild(newParameter("proc", Proc.none.name())); addDependency(project, "processor", new File(processor, "target/classes")); mojos.executeMojo(session, project, cloned); } @Test public void testMutliround_procOnly() throws Exception { File basedir = procCompile("compile-proc/multiround", Proc.only); File generatedSources = new File(basedir, "target/generated-sources/annotations"); mojos.assertMessages(basedir, "src/main/java/multiround/Source.java", new String[] {}); mojos.assertBuildOutputs(generatedSources, "multiround/GeneratedSource.java", "multiround/AnotherGeneratedSource.java"); } @Test public void testMultiround_processorLastRound() throws Exception { // processor.ProcessorLastRound creates well-known resource during last round // the point of this test is to assert this works during incremental build // when compiler may be invoked several times to compile indirectly affected sources File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/multiround-type-reference"); processAnnotations(basedir, Proc.procEX, processor, newProcessors("processor.ProcessorLastRound")); mojos.assertBuildOutputs(new File(basedir, "target"), // "classes/proc/Source.class", // "classes/proc/AnotherSource.class", // "classes/types.lst"); cp(basedir, "src/main/java/proc/Source.java-changed", "src/main/java/proc/Source.java"); processAnnotations(basedir, Proc.procEX, processor, newProcessors("processor.ProcessorLastRound")); mojos.assertBuildOutputs(new File(basedir, "target"), // "classes/proc/Source.class", // "classes/proc/AnotherSource.class", // "classes/types.lst"); } @Test public void testLastRound_typeIndex() throws Exception { // apparently javac can't resolve "forward" references to types generated during apt last round Assume.assumeTrue(CompilerJdt.ID.equals(compilerId)); Xpp3Dom processors = newProcessors("processor.ProcessorLastRound_typeIndex"); File basedir = procCompile("compile-proc/multiround-type-index", Proc.procEX, processors); File target = new File(basedir, "target"); mojos.assertBuildOutputs(target, // "generated-sources/annotations/generated/TypeIndex.java", // "generated-sources/annotations/generated/TypeIndex2.java", // "classes/generated/TypeIndex.class", // "classes/generated/TypeIndex2.class", // "classes/typeindex/Annotated.class", // "classes/typeindex/Consumer.class" // ); } @Test public void testReprocess() throws Exception { File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/reprocess"); File target = new File(basedir, "target"); processAnnotations(basedir, Proc.proc, processor, newProcessors("processor.ProessorValue")); mojos.assertBuildOutputs(target, // "classes/reprocess/Annotated.class", // "classes/reprocess/Annotated.value", // "classes/reprocess/SimpleA.class", // "classes/reprocess/SimpleB.class"); Assert.assertEquals("1", FileUtils.fileRead(new File(target, "classes/reprocess/Annotated.value"))); cp(basedir, "src/main/java/reprocess/SimpleA.java-changed", "src/main/java/reprocess/SimpleA.java"); touch(new File(basedir, "src/main/java/reprocess/Annotated.java")); processAnnotations(basedir, Proc.proc, processor, newProcessors("processor.ProessorValue")); mojos.assertBuildOutputs(target, // "classes/reprocess/Annotated.class", // "classes/reprocess/Annotated.value", // "classes/reprocess/SimpleA.class", // "classes/reprocess/SimpleB.class"); Assert.assertEquals("10", FileUtils.fileRead(new File(target, "classes/reprocess/Annotated.value"))); } @Test public void testProc_nonIncrementalProcessor() throws Exception { Assume.assumeTrue(CompilerJdt.ID.equals(compilerId)); File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc"); File target = new File(basedir, "target"); processAnnotations(basedir, Proc.procEX, processor, newProcessors("processor.NonIncrementalProcessor")); mojos.assertBuildOutputs(target, // "classes/proc/Source.class", // "generated-sources/annotations/proc/NonIncrementalSource.java", // "classes/proc/NonIncrementalSource.class"); FileUtils.deleteDirectory(target); processAnnotations(basedir, Proc.onlyEX, processor, newProcessors("processor.NonIncrementalProcessor")); mojos.assertBuildOutputs(target, // "generated-sources/annotations/proc/NonIncrementalSource.java"); FileUtils.deleteDirectory(target); try { processAnnotations(basedir, Proc.proc, processor, newProcessors("processor.NonIncrementalProcessor")); Assert.fail(); } catch (MojoExecutionException expected) { // TODO validate the error message } FileUtils.deleteDirectory(target); try { processAnnotations(basedir, Proc.only, processor, newProcessors("processor.NonIncrementalProcessor")); Assert.fail(); } catch (MojoExecutionException expected) { // TODO validate the error message } } @Test public void testSourcepathDependency() throws Exception { File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc-sourcepath"); File dependencyBasedir = new File(basedir, "dependency"); File projectBasedir = new File(basedir, "project"); Xpp3Dom processors = newProcessors("processor.Processor"); Xpp3Dom sourcepath = newParameter("sourcepath", "reactorDependencies"); MavenProject dependency = mojos.readMavenProject(dependencyBasedir); MavenProject project = mojos.readMavenProject(projectBasedir); mojos.newDependency(new File(dependencyBasedir, "target/classes")) // .setGroupId(dependency.getGroupId()) // .setArtifactId(dependency.getArtifactId()) // .setVersion(dependency.getVersion()) // .addTo(project); MavenSession session = mojos.newMavenSession(project); session.setProjects(Arrays.asList(project, dependency)); processAnnotations(session, project, "compile", processor, Proc.only, processors, sourcepath); mojos.assertBuildOutputs(new File(projectBasedir, "target"), // "generated-sources/annotations/sourcepath/project/GeneratedSource.java" // ); } @Test public void testSourcepathDependency_incremental() throws Exception { // the point of this test is assert that changes to sourcepath files are expected to trigger reprocessing of affected sources File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc-sourcepath"); File dependencyBasedir = new File(basedir, "dependency"); File projectBasedir = new File(basedir, "project"); Xpp3Dom processors = newProcessors("processor.Processor"); Xpp3Dom sourcepath = newParameter("sourcepath", "reactorDependencies"); MavenProject dependency = mojos.readMavenProject(dependencyBasedir); MavenProject project = mojos.readMavenProject(projectBasedir); mojos.newDependency(new File(dependencyBasedir, "target/classes")) // .setGroupId(dependency.getGroupId()) // .setArtifactId(dependency.getArtifactId()) // .setVersion(dependency.getVersion()) // .addTo(project); MavenSession session = mojos.newMavenSession(project); session.setProjects(Arrays.asList(project, dependency)); processAnnotations(session, project, "compile", processor, Proc.only, processors, sourcepath); mojos.assertBuildOutputs(new File(projectBasedir, "target"), // "generated-sources/annotations/sourcepath/project/GeneratedSource.java" // ); // second, incremental, compilation with one of sourcepath files removed rm(dependencyBasedir, "src/main/java/sourcepath/dependency/SourcepathDependency.java"); mojos.flushClasspathCaches(); dependency = mojos.readMavenProject(dependencyBasedir); project = mojos.readMavenProject(projectBasedir); mojos.newDependency(new File(dependencyBasedir, "target/classes")) // .setGroupId(dependency.getGroupId()) // .setArtifactId(dependency.getArtifactId()) // .setVersion(dependency.getVersion()) // .addTo(project); session = mojos.newMavenSession(project); session.setProjects(Arrays.asList(project, dependency)); try { processAnnotations(session, project, "compile", processor, Proc.only, processors, sourcepath); } catch (MojoExecutionException expected) {} ErrorMessage message = new ErrorMessage(compilerId); message.setSnippets(CompilerJdt.ID, "sourcepath.dependency.SourcepathDependency cannot be resolved to a type"); message.setSnippets(CompilerJavac.ID, "package sourcepath.dependency does not exist"); mojos.assertMessage(projectBasedir, "src/main/java/sourcepath/project/Source.java", message); // oddly enough, both jdt and javac generate GeneratedSource despite the error } @Test public void testSourcepathDependency_classifiedDependency() throws Exception { File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc-sourcepath"); File dependencyBasedir = new File(basedir, "dependency"); File projectBasedir = new File(basedir, "project"); Xpp3Dom processors = newProcessors("processor.Processor"); Xpp3Dom sourcepath = newParameter("sourcepath", "reactorDependencies"); MavenProject dependency = mojos.readMavenProject(dependencyBasedir); MavenProject project = mojos.readMavenProject(projectBasedir); mojos.newDependency(new File(dependencyBasedir, "target/classes")) // .setGroupId(dependency.getGroupId()) // .setArtifactId(dependency.getArtifactId()) // .setVersion(dependency.getVersion()) // .setClassifier("classifier") // .addTo(project); MavenSession session = mojos.newMavenSession(project); session.setProjects(Arrays.asList(project, dependency)); try { processAnnotations(session, project, "compile", processor, Proc.only, processors, sourcepath); Assert.fail(); } catch (MojoExecutionException expected) { Assert.assertTrue(expected.getMessage().contains(dependency.getGroupId() + ":" + dependency.getArtifactId())); } } @Test public void testSourcepathIncludes() throws Exception { Xpp3Dom includes = new Xpp3Dom("includes"); includes.addChild(newParameter("include", "sourcepath/project/*.java")); Xpp3Dom processors = newProcessors("processor.Processor"); Xpp3Dom sourcepath = newParameter("sourcepath", "reactorDependencies"); File basedir = procCompile("compile-proc/proc-sourcepath-includes", Proc.only, includes, processors, sourcepath); mojos.assertBuildOutputs(new File(basedir, "target"), // "generated-sources/annotations/sourcepath/project/GeneratedSource.java" // ); } @Test public void testSourcepathDependency_testCompile() throws Exception { File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc-sourcepath"); File dependencyBasedir = new File(basedir, "dependency"); File projectBasedir = new File(basedir, "project"); Xpp3Dom processors = newProcessors("processor.Processor"); Xpp3Dom sourcepath = newParameter("sourcepath", "reactorDependencies"); MavenProject dependency = mojos.readMavenProject(dependencyBasedir); MavenProject project = mojos.readMavenProject(projectBasedir); mojos.newDependency(new File(dependencyBasedir, "target/classes")) // .setGroupId(dependency.getGroupId()) // .setArtifactId(dependency.getArtifactId()) // .setVersion(dependency.getVersion()) // .addTo(project); mojos.newDependency(new File(dependencyBasedir, "target/test-classes")) // .setGroupId(dependency.getGroupId()) // .setArtifactId(dependency.getArtifactId()) // .setType("test-jar") // .setVersion(dependency.getVersion()) // .addTo(project); MavenSession session = mojos.newMavenSession(project); session.setProjects(Arrays.asList(project, dependency)); processAnnotations(session, project, "testCompile", processor, Proc.only, processors, sourcepath); mojos.assertBuildOutputs(new File(projectBasedir, "target"), // "generated-test-sources/test-annotations/sourcepath/project/test/GeneratedSourceTest.java" // ); } @Test public void testSourcepath_classpathVisibility() throws Exception { Assume.assumeTrue(CompilerJdt.ID.equals(compilerId)); File basedir = resources.getBasedir(); Xpp3Dom sourcepath = newParameter("sourcepath", "reactorDependencies"); Xpp3Dom classpathVisibility = newParameter("privatePackageReference", "error"); try { procCompile(basedir, Proc.only, sourcepath, classpathVisibility); Assert.fail(); } catch (MojoExecutionException expected) { Assert.assertTrue(expected.getMessage().contains("privatePackageReference")); } } @Test @Ignore("Neither javac nor jdt support secondary types on sourcepath") public void testSourcepathSecondatytype() throws Exception { File processor = compileAnnotationProcessor(); File basedir = resources.getBasedir("compile-proc/proc-sourcepath-secondarytype"); File dependencyBasedir = new File(basedir, "dependency"); File projectBasedir = new File(basedir, "project"); Xpp3Dom processors = newProcessors("processor.Processor"); Xpp3Dom sourcepath = newParameter("sourcepath", "reactorDependencies"); MavenProject dependency = mojos.readMavenProject(dependencyBasedir); MavenProject project = mojos.readMavenProject(projectBasedir); mojos.newDependency(new File(dependencyBasedir, "target/classes")) // .setGroupId(dependency.getGroupId()) // .setArtifactId(dependency.getArtifactId()) // .setVersion(dependency.getVersion()) // .addTo(project); MavenSession session = mojos.newMavenSession(project); session.setProjects(Arrays.asList(project, dependency)); processAnnotations(session, project, "compile", processor, Proc.only, processors, sourcepath); } }