package net.rkunze.maven.compiler.jsr308javac; /** * The MIT License * * Copyright (c) 2005, The Codehaus * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import org.codehaus.plexus.compiler.AbstractCompilerTest; import org.codehaus.plexus.compiler.CompilerConfiguration; import org.codehaus.plexus.util.StringUtils; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TreeSet; import static junit.framework.Assert.assertEquals; import static org.codehaus.plexus.PlexusTestCase.getBasedir; import org.codehaus.plexus.compiler.CompilerException; import org.codehaus.plexus.compiler.CompilerMessage; import org.codehaus.plexus.util.FileUtils; /** * @author <a href="mailto:jason@plexus.org">Jason van Zyl</a> */ public class JavacJSR308CompilerTest extends AbstractCompilerTest { private static final String PS = File.pathSeparator; public void setUp() throws Exception { super.setUp(); setCompilerDebug( true ); setCompilerDeprecationWarnings( true ); } protected String getRoleHint() { return "javac+jsr308"; } protected int expectedErrors() { // javac output changed for misspelled modifiers starting in 1.6...they now generate 2 errors per occurrence, not one. if ( "1.5".compareTo( getJavaVersion() ) < 0 ) { return 4; } else { return 3; } } protected int expectedWarnings() { return 2; } protected Collection<String> expectedOutputFiles() { return Arrays.asList( new String[]{ "org/codehaus/foo/Deprecation.class", "org/codehaus/foo/ExternalDeps.class", "org/codehaus/foo/JSR308Nullness.class", "org/codehaus/foo/Person.class", "org/codehaus/foo/ReservedWord.class" } ); } protected Collection<String> expectedOutputFilesNullnessChecker() { return Arrays.asList( new String[]{ "org/codehaus/foo/Deprecation.class", "org/codehaus/foo/ExternalDeps.class", "org/codehaus/foo/Person.class" } ); } public void internalTest( CompilerConfiguration compilerConfiguration, List<String> expectedArguments ) throws CompilerException { String[] actualArguments = JavacJSR308Compiler.buildCompilerArguments( compilerConfiguration, new String[0] ); assertEquals( "The expected and actual argument list sizes differ.", expectedArguments.size(), actualArguments.length ); for ( int i = 0; i < actualArguments.length; i++ ) { assertEquals( "Unexpected argument", expectedArguments.get( i ), actualArguments[i] ); } } public void testBuildCompilerArgs13() throws CompilerException { List<String> expectedArguments = new ArrayList<String>(); CompilerConfiguration compilerConfiguration = new CompilerConfiguration(); compilerConfiguration.setCompilerVersion( "1.3" ); populateArguments( compilerConfiguration, expectedArguments, true, true ); internalTest( compilerConfiguration, expectedArguments ); } public void testBuildCompilerArgs14() throws CompilerException { List<String> expectedArguments = new ArrayList<String>(); CompilerConfiguration compilerConfiguration = new CompilerConfiguration(); compilerConfiguration.setCompilerVersion( "1.4" ); populateArguments( compilerConfiguration, expectedArguments, false, false ); internalTest( compilerConfiguration, expectedArguments ); } public void testBuildCompilerArgs15() throws CompilerException { List<String> expectedArguments = new ArrayList<String>(); CompilerConfiguration compilerConfiguration = new CompilerConfiguration(); compilerConfiguration.setCompilerVersion( "1.5" ); populateArguments( compilerConfiguration, expectedArguments, false, false ); internalTest( compilerConfiguration, expectedArguments ); } public void testBuildCompilerArgsUnspecifiedVersion() throws CompilerException { List<String> expectedArguments = new ArrayList<String>(); CompilerConfiguration compilerConfiguration = new CompilerConfiguration(); populateArguments( compilerConfiguration, expectedArguments, false, false ); internalTest( compilerConfiguration, expectedArguments ); } public void testBuildCompilerDebugLevel() throws CompilerException { List<String> expectedArguments = new ArrayList<String>(); CompilerConfiguration compilerConfiguration = new CompilerConfiguration(); compilerConfiguration.setDebug( true ); compilerConfiguration.setDebugLevel( "none" ); populateArguments( compilerConfiguration, expectedArguments, false, false ); internalTest( compilerConfiguration, expectedArguments ); } // PLXCOMP-190 public void testJRuntimeArguments() throws CompilerException { List<String> expectedArguments = new ArrayList<String>(); CompilerConfiguration compilerConfiguration = new CompilerConfiguration(); // outputLocation compilerConfiguration.setOutputLocation( "/output" ); expectedArguments.add( "-d" ); expectedArguments.add( new File( "/output" ).getAbsolutePath() ); // classpath (automatically added by JavacJSR308Compiler) expectedArguments.add("-Xbootclasspath/p:" + ClasspathConfig.getAnnotatedJDK(System.getProperty("java.version")).getAbsolutePath() + PS + ClasspathConfig.getCompilerJar().getAbsolutePath() + PS ); expectedArguments.add( "-classpath" ); expectedArguments.add( ClasspathConfig.getCheckerJar().getAbsolutePath() + PS ); // targetVersion compilerConfiguration.setTargetVersion( "1.7" ); expectedArguments.add( "-target" ); expectedArguments.add( "1.7" ); // sourceVersion compilerConfiguration.setSourceVersion( "1.7" ); expectedArguments.add( "-source" ); expectedArguments.add( "1.7" ); // customCompilerArguments Map<String, String> customCompilerArguments = new LinkedHashMap<String, String>(); customCompilerArguments.put( "-J-Duser.language=en_us", null ); compilerConfiguration.setCustomCompilerArgumentsAsMap( customCompilerArguments ); // don't expect this argument!! internalTest( compilerConfiguration, expectedArguments ); } /* This test fails on Java 1.4. The multiple parameters of the same source file cause an error, as it is interpreted as a DuplicateClass * Setting the size of the array to 3 is fine, but does not exactly test what it is supposed to - disabling the test for now public void testCommandLineTooLongWhenForking() throws Exception { JavacJSR308Compiler compiler = (JavacJSR308Compiler) lookup( org.codehaus.plexus.compiler.Compiler.ROLE, getRoleHint() ); File destDir = new File( "target/test-classes-cmd" ); destDir.mkdirs(); // fill the cmd line arguments, 300 is enough to make it break String[] args = new String[400]; args[0] = "-d"; args[1] = destDir.getAbsolutePath(); for ( int i = 2; i < args.length; i++ ) { args[i] = "org/codehaus/foo/Person.java"; } CompilerConfiguration config = new CompilerConfiguration(); config.setWorkingDirectory( new File( getBasedir() + "/src/test-input/src/main" ) ); config.setFork( true ); List messages = compiler.compileOutOfProcess( config, "javac", args ); assertEquals( "There were errors launching the external compiler: " + messages, 0, messages.size() ); } */ private void populateArguments( CompilerConfiguration compilerConfiguration, List<String> expectedArguments, boolean suppressSourceVersion, boolean suppressEncoding ) throws CompilerException { // outputLocation compilerConfiguration.setOutputLocation( "/output" ); expectedArguments.add( "-d" ); expectedArguments.add( new File( "/output" ).getAbsolutePath() ); // Bootstrap classpath for the annotated JDK and the JSR308 javac expectedArguments.add("-Xbootclasspath/p:" + ClasspathConfig.getAnnotatedJDK(System.getProperty("java.version")).getAbsolutePath() + PS + ClasspathConfig.getCompilerJar().getAbsolutePath() + PS); // classpathEntires List<String> classpathEntries = new ArrayList<String>(); classpathEntries.add( "/myjar1.jar" ); classpathEntries.add( "/myjar2.jar" ); compilerConfiguration.setClasspathEntries( classpathEntries ); expectedArguments.add( "-classpath" ); expectedArguments.add( ClasspathConfig.getCheckerJar().getAbsolutePath() + PS + "/myjar1.jar" + PS + "/myjar2.jar" + PS ); // sourceRoots List<String> compileSourceRoots = new ArrayList<String>(); compileSourceRoots.add( "/src/main/one" ); compileSourceRoots.add( "/src/main/two" ); compilerConfiguration.setSourceLocations( compileSourceRoots ); expectedArguments.add( "-sourcepath" ); expectedArguments.add( "/src/main/one" + PS + "/src/main/two" + PS ); // debug compilerConfiguration.setDebug( true ); if ( StringUtils.isNotEmpty( compilerConfiguration.getDebugLevel() ) ) { expectedArguments.add( "-g:" + compilerConfiguration.getDebugLevel() ); } else { expectedArguments.add( "-g" ); } // showDeprecation compilerConfiguration.setShowDeprecation( true ); expectedArguments.add( "-deprecation" ); // targetVersion compilerConfiguration.setTargetVersion( "1.7" ); expectedArguments.add( "-target" ); expectedArguments.add( "1.7" ); // sourceVersion compilerConfiguration.setSourceVersion( "1.7" ); if ( !suppressSourceVersion ) { expectedArguments.add( "-source" ); expectedArguments.add( "1.7" ); } // sourceEncoding compilerConfiguration.setSourceEncoding( "iso-8859-1" ); if ( !suppressEncoding ) { expectedArguments.add( "-encoding" ); expectedArguments.add( "iso-8859-1" ); } // customerCompilerArguments Map<String, String> customerCompilerArguments = new LinkedHashMap<String, String>(); customerCompilerArguments.put( "arg1", null ); customerCompilerArguments.put( "foo", "bar" ); compilerConfiguration.setCustomCompilerArgumentsAsMap( customerCompilerArguments ); expectedArguments.add( "arg1" ); expectedArguments.add( "foo" ); expectedArguments.add( "bar" ); } @SuppressWarnings( "unchecked" ) public void testCompilingSourcesWithNullnessChecker() throws Exception { List<CompilerMessage> messages = new ArrayList<CompilerMessage>(); Collection<String> files = new TreeSet<String>(); for ( CompilerConfiguration compilerConfig : getJsr308CompilerConfigurations() ) { File outputDir = new File( compilerConfig.getOutputLocation() ); org.codehaus.plexus.compiler.Compiler compiler = (org.codehaus.plexus.compiler.Compiler) lookup( org.codehaus.plexus.compiler.Compiler.ROLE, getRoleHint() ); messages.addAll( compiler.performCompile( compilerConfig ).getCompilerMessages() ); if ( outputDir.isDirectory() ) { files.addAll( normalizePaths( FileUtils.getFileNames( outputDir, null, null, false ) ) ); } } final int expectedCompilerErrors = 6; final int expectedCompilerWarnings = 1; int numCompilerErrors = compilerErrorCount( messages ); int numCompilerWarnings = messages.size() - numCompilerErrors; if ( expectedCompilerErrors != numCompilerErrors ) { System.out.println( numCompilerErrors + " error(s) found:" ); for ( CompilerMessage error : messages ) { if ( !error.isError() ) { continue; } System.out.println( "----" ); System.out.println( error.getFile() ); System.out.println( error.getMessage() ); System.out.println( "----" ); } assertEquals( "Wrong number of compilation errors.", expectedCompilerErrors, numCompilerErrors ); } if ( expectedCompilerWarnings != numCompilerWarnings ) { System.out.println( numCompilerWarnings + " warning(s) found:" ); for ( CompilerMessage error : messages ) { if ( error.isError() ) { continue; } System.out.println( "----" ); System.out.println( error.getFile() ); System.out.println( error.getMessage() ); System.out.println( "----" ); } assertEquals( "Wrong number of compilation warnings.", expectedCompilerWarnings, numCompilerWarnings ); } assertEquals( new TreeSet<String>( normalizePaths( expectedOutputFilesNullnessChecker() ) ), files ); } private List<CompilerConfiguration> getJsr308CompilerConfigurations() throws Exception { String sourceDir = getBasedir() + "/src/test-input/src/main"; @SuppressWarnings( "unchecked" ) List<String> filenames = FileUtils.getFileNames( new File( sourceDir ), "**/*.java", null, false, true ); Collections.sort( filenames ); List<CompilerConfiguration> compilerConfigurations = new ArrayList<CompilerConfiguration>(); int index = 0; for ( Iterator<String> it = filenames.iterator(); it.hasNext(); index++ ) { String filename = it.next(); CompilerConfiguration compilerConfig = new CompilerConfiguration(); compilerConfig.setDebug( true ); compilerConfig.setShowDeprecation( true ); compilerConfig.setClasspathEntries( getJsr308Classpath() ); compilerConfig.addSourceLocation( sourceDir ); compilerConfig.setOutputLocation( getBasedir() + "/target/" + getRoleHint() + "/classes-jsr308-" + index ); FileUtils.deleteDirectory( compilerConfig.getOutputLocation() ); compilerConfig.addInclude( filename ); compilerConfig.setAnnotationProcessors(new String[] { "org.checkerframework.checker.nullness.NullnessChecker" }); compilerConfig.setTargetVersion( "1.8" ); compilerConfig.setSourceVersion( "1.8" ); compilerConfig.setVerbose(true); compilerConfigurations.add( compilerConfig ); } return compilerConfigurations; } protected List<String> getJsr308Classpath() throws Exception { List<String> cp = getClasspath(); File file = getLocalArtifactPath( "org.checkerframework", "checker-qual", "${checker-framework.version}", "jar" ); assertTrue( "test prerequisite: checker-qual library must be available in local repository, expected " + file.getAbsolutePath(), file.canRead() ); cp.add( file.getAbsolutePath() ); return cp; } private List<String> normalizePaths( Collection<String> relativePaths ) { List<String> normalizedPaths = new ArrayList<String>(); for ( String relativePath : relativePaths ) { normalizedPaths.add( relativePath.replace( File.separatorChar, '/' ) ); } return normalizedPaths; } }