View Javadoc

1   package org.apache.maven.plugin.surefire;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.lang.reflect.Method;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collections;
27  import java.util.Iterator;
28  import java.util.LinkedHashSet;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Properties;
32  import java.util.Set;
33  import org.apache.maven.artifact.Artifact;
34  import org.apache.maven.artifact.DependencyResolutionRequiredException;
35  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
36  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
37  import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
38  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
39  import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
40  import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
41  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
42  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
43  import org.apache.maven.artifact.versioning.VersionRange;
44  import org.apache.maven.plugin.AbstractMojo;
45  import org.apache.maven.plugin.MojoExecutionException;
46  import org.apache.maven.plugin.MojoFailureException;
47  import org.apache.maven.plugin.surefire.booterclient.ChecksumCalculator;
48  import org.apache.maven.plugin.surefire.booterclient.ForkConfiguration;
49  import org.apache.maven.plugin.surefire.booterclient.ForkStarter;
50  import org.apache.maven.shared.artifact.filter.PatternIncludesArtifactFilter;
51  import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
52  import org.apache.maven.surefire.booter.Classpath;
53  import org.apache.maven.surefire.booter.ClasspathConfiguration;
54  import org.apache.maven.surefire.booter.ProviderConfiguration;
55  import org.apache.maven.surefire.booter.StartupConfiguration;
56  import org.apache.maven.surefire.report.BriefConsoleReporter;
57  import org.apache.maven.surefire.report.BriefFileReporter;
58  import org.apache.maven.surefire.report.ConsoleReporter;
59  import org.apache.maven.surefire.report.DetailedConsoleReporter;
60  import org.apache.maven.surefire.report.FileReporter;
61  import org.apache.maven.surefire.report.ForkingConsoleReporter;
62  import org.apache.maven.surefire.report.ReporterConfiguration;
63  import org.apache.maven.surefire.report.XMLReporter;
64  import org.apache.maven.surefire.testset.DirectoryScannerParameters;
65  import org.apache.maven.surefire.testset.TestArtifactInfo;
66  import org.apache.maven.surefire.testset.TestRequest;
67  import org.apache.maven.surefire.util.NestedRuntimeException;
68  import org.apache.maven.surefire.util.Relocator;
69  import org.apache.maven.toolchain.Toolchain;
70  import org.codehaus.plexus.util.StringUtils;
71  
72  /**
73   * Abstract base class for running tests using Surefire.
74   *
75   * @author Stephen Connolly
76   * @version $Id: SurefirePlugin.java 945065 2010-05-17 10:26:22Z stephenc $
77   */
78  public abstract class AbstractSurefireMojo
79      extends AbstractMojo
80      implements SurefireExecutionParameters
81  {
82  
83      private static final String BRIEF_REPORT_FORMAT = "brief";
84  
85      private static final String PLAIN_REPORT_FORMAT = "plain";
86  
87      // common field getters/setters
88  
89      // common code
90  
91      protected abstract String getPluginName();
92  
93      private SurefireDependencyResolver dependencyResolver;
94  
95      public void execute()
96          throws MojoExecutionException, MojoFailureException
97      {
98  
99          if ( verifyParameters() && !hasExecutedBefore() )
100         {
101             logReportsDirectory();
102             executeAfterPreconditionsChecked();
103         }
104     }
105 
106     protected boolean verifyParameters()
107         throws MojoFailureException
108     {
109         if ( isSkipExecution() )
110         {
111             getLog().info( "Tests are skipped." );
112             return false;
113         }
114 
115         if ( !getTestClassesDirectory().exists() )
116         {
117             if ( Boolean.TRUE.equals( getFailIfNoTests() ) )
118             {
119                 throw new MojoFailureException( "No tests to run!" );
120             }
121             getLog().info( "No tests to run." );
122         }
123         else
124         {
125             ensureWorkingDirectoryExists();
126             ensureParallelRunningCompatibility();
127             warnIfUselessUseSystemClassLoaderParameter();
128         }
129 
130         return true;
131     }
132 
133     protected abstract boolean isSkipExecution();
134 
135     protected abstract void executeAfterPreconditionsChecked()
136         throws MojoExecutionException, MojoFailureException;
137 
138     private Artifact surefireArtifact;
139 
140 
141     protected List initialize()
142         throws MojoFailureException
143     {
144         dependencyResolver =
145             new SurefireDependencyResolver( getArtifactResolver(), getArtifactFactory(), getLog(), getLocalRepository(),
146                                             getRemoteRepositories(), getMetadataSource(), getPluginName() );
147 
148         try
149         {
150             final Artifact junitDepArtifact = getJunitDepArtifact();
151             ProviderList wellKnownProviders = new ProviderList(
152                 new ProviderInfo[]{ new TestNgProviderInfo( getTestNgArtifact() ),
153                     new JUnitCoreProviderInfo( getJunitArtifact(), junitDepArtifact ),
154                     new JUnit4ProviderInfo( getJunitArtifact(), junitDepArtifact ), new JUnit3ProviderInfo() },
155                 new DynamicProviderInfo( null ) );
156 
157             return wellKnownProviders.resolve( getLog() );
158         }
159         catch ( InvalidVersionSpecificationException e )
160         {
161             throw new NestedRuntimeException( e );
162         }
163     }
164 
165     protected void logReportsDirectory()
166     {
167         getLog().info(
168             StringUtils.capitalizeFirstLetter( getPluginName() ) + " report directory: " + getReportsDirectory() );
169     }
170 
171 
172     protected final Toolchain getToolchain()
173     {
174         Toolchain tc = null;
175 
176         if ( getToolchainManager() != null )
177         {
178             tc = getToolchainManager().getToolchainFromBuildContext( "jdk", getSession() );
179         }
180 
181         return tc;
182     }
183 
184     /**
185      * Converts old TestNG configuration parameters over to new properties based configuration
186      * method. (if any are defined the old way)
187      */
188     private void convertTestNGParameters()
189     {
190         if ( getProperties() == null ) // May be predefined from plugin paramaters
191         {
192             setProperties( new Properties() );
193         }
194 
195         if ( this.getParallel() != null )
196         {
197             getProperties().setProperty( "parallel", this.getParallel() );
198         }
199         if ( this.getExcludedGroups() != null )
200         {
201             getProperties().setProperty( "excludegroups", this.getExcludedGroups() );
202         }
203         if ( this.getGroups() != null )
204         {
205             getProperties().setProperty( "groups", this.getGroups() );
206         }
207 
208         if ( this.getThreadCount() > 0 )
209         {
210             getProperties().setProperty( "threadcount", Integer.toString( this.getThreadCount() ) );
211         }
212         if ( this.getObjectFactory() != null )
213         {
214             getProperties().setProperty( "objectfactory", this.getObjectFactory() );
215         }
216         if ( this.getTestClassesDirectory() != null )
217         {
218             getProperties().setProperty( "testng.test.classpath", getTestClassesDirectory().getAbsolutePath() );
219         }
220 
221 
222     }
223 
224     protected boolean isAnyConcurrencySelected()
225     {
226         return this.getParallel() != null && this.getParallel().trim().length() > 0;
227     }
228 
229     /**
230      * Converts old JUnit configuration parameters over to new properties based configuration
231      * method. (if any are defined the old way)
232      */
233     private void convertJunitCoreParameters()
234     {
235         if ( getProperties() == null )
236         {
237             setProperties( new Properties() );
238         }
239 
240         if ( this.getParallel() != null )
241         {
242             getProperties().setProperty( "parallel", this.getParallel() );
243         }
244         if ( this.getThreadCount() > 0 )
245         {
246             getProperties().setProperty( "threadCount", Integer.toString( this.getThreadCount() ) );
247         }
248         getProperties().setProperty( "perCoreThreadCount", Boolean.toString( getPerCoreThreadCount() ) );
249         getProperties().setProperty( "useUnlimitedThreads", Boolean.toString( getUseUnlimitedThreads() ) );
250     }
251 
252     private boolean isJunit47Compatible( Artifact artifact )
253     {
254         return dependencyResolver.isWithinVersionSpec( artifact, "[4.7,)" );
255     }
256 
257     private boolean isAnyJunit4( Artifact artifact )
258     {
259         return dependencyResolver.isWithinVersionSpec( artifact, "[4.0,)" );
260     }
261 
262     protected boolean isForkModeNever()
263     {
264         return ForkConfiguration.FORK_NEVER.equals( getForkMode() );
265     }
266 
267     protected ProviderConfiguration createProviderConfiguration( ForkConfiguration forkConfiguration,
268                                                                  boolean shadefire )
269         throws MojoExecutionException, MojoFailureException
270     {
271 
272         List reports = getReporters( forkConfiguration.isForking() );
273         reports = shadefire ? new Relocator().relocateReports( reports ) : reports;
274         Integer timeoutSet =
275             getForkedProcessTimeoutInSeconds() > 0 ? Integer.valueOf( getForkedProcessTimeoutInSeconds() ) : null;
276         ReporterConfiguration reporterConfiguration =
277             new ReporterConfiguration( reports, getReportsDirectory(), Boolean.valueOf( isTrimStackTrace() ),
278                                        timeoutSet );
279 
280         Artifact testNgArtifact;
281         try
282         {
283             testNgArtifact = getTestNgArtifact();
284         }
285         catch ( InvalidVersionSpecificationException e )
286         {
287             throw new MojoExecutionException( "Error determining the TestNG version requested: " + e.getMessage(), e );
288         }
289 
290         DirectoryScannerParameters directoryScannerParameters = null;
291         final boolean isTestNg = testNgArtifact != null;
292         TestArtifactInfo testNg =
293             isTestNg ? new TestArtifactInfo( testNgArtifact.getVersion(), testNgArtifact.getClassifier() ) : null;
294         List testXml = getSuiteXmlFiles() != null ? Arrays.asList( getSuiteXmlFiles() ) : null;
295         TestRequest testSuiteDefinition =
296             new TestRequest( testXml, getTestSourceDirectory(), getTest(), getTestMethod() );
297         final boolean failIfNoTests;
298 
299         if ( isValidSuiteXmlFileConfig() && getTest() == null )
300         {
301             failIfNoTests = getFailIfNoTests() != null && getFailIfNoTests().booleanValue();
302             if ( !isTestNg )
303             {
304                 throw new MojoExecutionException( "suiteXmlFiles is configured, but there is no TestNG dependency" );
305             }
306         }
307         else
308         {
309             if ( isSpecificTestSpecified() && getFailIfNoTests() == null )
310             {
311                 setFailIfNoTests( Boolean.TRUE );
312             }
313 
314             failIfNoTests = getFailIfNoTests() != null && getFailIfNoTests().booleanValue();
315 
316             List includes = getIncludeList();
317             List excludes = getExcludeList();
318             directoryScannerParameters = new DirectoryScannerParameters( getTestClassesDirectory(), includes, excludes,
319                                                                          Boolean.valueOf( failIfNoTests ),
320                                                                          getRunOrder() );
321         }
322 
323         Properties providerProperties = getProperties();
324         if ( providerProperties == null )
325         {
326             providerProperties = new Properties();
327         }
328 
329         ProviderConfiguration providerConfiguration1 =
330             new ProviderConfiguration( directoryScannerParameters, failIfNoTests, reporterConfiguration, testNg,
331                                        testSuiteDefinition, providerProperties, null );
332 
333         Toolchain tc = getToolchain();
334 
335         if ( tc != null )
336         {
337             getLog().info( "Toolchain in " + getPluginName() + "-plugin: " + tc );
338             if ( isForkModeNever() )
339             {
340                 setForkMode( ForkConfiguration.FORK_ONCE );
341             }
342             if ( getJvm() != null )
343             {
344                 getLog().warn( "Toolchains are ignored, 'executable' parameter is set to " + getJvm() );
345             }
346             else
347             {
348                 setJvm( tc.findTool( "java" ) ); //NOI18N
349             }
350         }
351 
352         return providerConfiguration1;
353     }
354 
355     protected StartupConfiguration createStartupConfiguration( ForkConfiguration forkConfiguration,
356                                                                ProviderInfo provider,
357                                                                ClassLoaderConfiguration classLoaderConfiguration )
358         throws MojoExecutionException, MojoFailureException
359     {
360 
361         try
362         {
363             provider.addProviderProperties();
364             String providerName = provider.getProviderName();
365             final Classpath providerClasspath = provider.getProviderClasspath();
366             final Classpath testClasspath = generateTestClasspath();
367 
368             logClasspath( testClasspath, "test classpath" );
369             logClasspath( testClasspath, "provider classpath" );
370             final ClasspathConfiguration classpathConfiguration =
371                 new ClasspathConfiguration( testClasspath, providerClasspath, isEnableAssertions(),
372                                             isChildDelegation() );
373 
374             return new StartupConfiguration( providerName, classpathConfiguration, classLoaderConfiguration,
375                                              forkConfiguration.isForking(), false, isRedirectTestOutputToFile() );
376         }
377         catch ( DependencyResolutionRequiredException e )
378         {
379             throw new MojoExecutionException( "Unable to generate classpath: " + e, e );
380         }
381         catch ( ArtifactResolutionException e )
382         {
383             throw new MojoExecutionException( "Unable to generate classpath: " + e, e );
384         }
385         catch ( ArtifactNotFoundException e )
386         {
387             throw new MojoExecutionException( "Unable to generate classpath: " + e, e );
388         }
389         catch ( InvalidVersionSpecificationException e )
390         {
391             throw new MojoExecutionException( "Unable to generate classpath: " + e, e );
392         }
393 
394     }
395 
396     public void logClasspath( Classpath classpath, String descriptor )
397     {
398         getLog().debug( descriptor + " classpath:" );
399         for ( Iterator i = classpath.getClassPath().iterator(); i.hasNext(); )
400         {
401             String classpathElement = (String) i.next();
402             if ( classpathElement == null )
403             {
404                 getLog().warn( "The test classpath contains a null element." );
405             }
406             else
407             {
408                 getLog().debug( "  " + classpathElement );
409             }
410         }
411     }
412 
413 
414     private boolean isSpecificTestSpecified()
415     {
416         return getTest() != null;
417     }
418 
419     private boolean isValidSuiteXmlFileConfig()
420     {
421         return getSuiteXmlFiles() != null && getSuiteXmlFiles().length > 0;
422     }
423 
424     private List getExcludeList()
425     {
426         List excludes;
427         if ( isSpecificTestSpecified() )
428         {
429             // Check to see if we are running a single test. The raw parameter will
430             // come through if it has not been set.
431             // FooTest -> **/FooTest.java
432 
433             excludes = new ArrayList();
434         }
435         else
436         {
437 
438             excludes = this.getExcludes();
439 
440             // defaults here, qdox doesn't like the end javadoc value
441             // Have to wrap in an ArrayList as surefire expects an ArrayList instead of a List for some reason
442             if ( excludes == null || excludes.size() == 0 )
443             {
444                 excludes = new ArrayList( Arrays.asList( new String[]{ "**/*$*" } ) );
445             }
446         }
447         return excludes;
448     }
449 
450     private List getIncludeList()
451     {
452         List includes;
453         if ( isSpecificTestSpecified() )
454         {
455             // Check to see if we are running a single test. The raw parameter will
456             // come through if it has not been set.
457 
458             // FooTest -> **/FooTest.java
459 
460             includes = new ArrayList();
461 
462             String[] testRegexes = StringUtils.split( getTest(), "," );
463 
464             for ( int i = 0; i < testRegexes.length; i++ )
465             {
466                 String testRegex = testRegexes[i];
467                 if ( testRegex.endsWith( ".java" ) )
468                 {
469                     testRegex = testRegex.substring( 0, testRegex.length() - 5 );
470                 }
471                 // Allow paths delimited by '.' or '/'
472                 testRegex = testRegex.replace( '.', '/' );
473                 includes.add( "**/" + testRegex + ".java" );
474             }
475         }
476         else
477         {
478             includes = this.getIncludes();
479 
480             // defaults here, qdox doesn't like the end javadoc value
481             // Have to wrap in an ArrayList as surefire expects an ArrayList instead of a List for some reason
482             if ( includes == null || includes.size() == 0 )
483             {
484                 includes = new ArrayList( Arrays.asList( getDefaultIncludes() ) );
485             }
486         }
487         return includes;
488     }
489 
490     private Artifact getTestNgArtifact()
491         throws MojoFailureException, InvalidVersionSpecificationException
492     {
493         // TODO: this is pretty manual, but I'd rather not require the plugin > dependencies section right now
494         Artifact artifact = (Artifact) getProjectArtifactMap().get( getTestNGArtifactName() );
495 
496         if ( artifact != null )
497         {
498             VersionRange range = VersionRange.createFromVersionSpec( "[4.7,)" );
499             if ( !range.containsVersion( new DefaultArtifactVersion( artifact.getVersion() ) ) )
500             {
501                 throw new MojoFailureException(
502                     "TestNG support requires version 4.7 or above. You have declared version "
503                         + artifact.getVersion() );
504             }
505         }
506         return artifact;
507 
508     }
509 
510     private Artifact getJunitArtifact()
511     {
512         return (Artifact) getProjectArtifactMap().get( getJunitArtifactName() );
513     }
514 
515     private Artifact getJunitDepArtifact()
516     {
517         return (Artifact) getProjectArtifactMap().get( "junit:junit-dep" );
518     }
519 
520     protected ForkStarter createForkStarter( ProviderInfo provider, ForkConfiguration forkConfiguration,
521                                              ClassLoaderConfiguration classLoaderConfiguration )
522         throws MojoExecutionException, MojoFailureException
523     {
524         StartupConfiguration startupConfiguration =
525             createStartupConfiguration( forkConfiguration, provider, classLoaderConfiguration );
526         ProviderConfiguration providerConfiguration =
527             createProviderConfiguration( forkConfiguration, startupConfiguration.isShadefire() );
528         return new ForkStarter( providerConfiguration, startupConfiguration, getReportsDirectory(), forkConfiguration,
529                                 getForkedProcessTimeoutInSeconds(), isPrintSummary() );
530     }
531 
532     protected ForkConfiguration getForkConfiguration()
533     {
534         File tmpDir = getSurefireTempDir();
535         //noinspection ResultOfMethodCallIgnored
536         tmpDir.mkdirs();
537 
538         Artifact shadeFire = (Artifact) getPluginArtifactMap().get( "org.apache.maven.surefire:surefire-shadefire" );
539 
540         surefireArtifact = (Artifact) getPluginArtifactMap().get( "org.apache.maven.surefire:surefire-booter" );
541         if ( surefireArtifact == null )
542         {
543             throw new RuntimeException( "Unable to locate surefire-booter in the list of plugin artifacts" );
544         }
545 
546         surefireArtifact.isSnapshot(); // MNG-2961: before Maven 2.0.8, fixes getBaseVersion to be -SNAPSHOT if needed
547 
548         final Classpath bootClasspathConfiguration =
549             getArtifactClasspath( shadeFire != null ? shadeFire : surefireArtifact );
550 
551         ForkConfiguration fork = new ForkConfiguration( bootClasspathConfiguration, getForkMode(), tmpDir );
552 
553         fork.setTempDirectory( tmpDir );
554 
555         processSystemProperties( !fork.isForking() );
556 
557         verifyLegalSystemProperties();
558 
559         if ( getLog().isDebugEnabled() )
560         {
561             showMap( getInternalSystemProperties(), "system property" );
562         }
563 
564         if ( fork.isForking() )
565         {
566             setUseSystemClassLoader( isUseSystemClassLoader() );
567 
568             fork.setSystemProperties( getInternalSystemProperties() );
569 
570             if ( "true".equals( getDebugForkedProcess() ) )
571             {
572                 setDebugForkedProcess(
573                     "-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005" );
574             }
575 
576             fork.setDebugLine( getDebugForkedProcess() );
577 
578             if ( getJvm() == null || "".equals( getJvm() ) )
579             {
580                 // use the same JVM as the one used to run Maven (the "java.home" one)
581                 setJvm( System.getProperty( "java.home" ) + File.separator + "bin" + File.separator + "java" );
582                 getLog().debug( "Using JVM: " + getJvm() );
583             }
584 
585             fork.setJvmExecutable( getJvm() );
586 
587             if ( getWorkingDirectory() != null )
588             {
589                 fork.setWorkingDirectory( getWorkingDirectory() );
590             }
591             else
592             {
593                 fork.setWorkingDirectory( getBasedir() );
594             }
595 
596             fork.setArgLine( getArgLine() );
597 
598             fork.setEnvironmentVariables( getEnvironmentVariables() );
599 
600             if ( getLog().isDebugEnabled() )
601             {
602                 showMap( getEnvironmentVariables(), "environment variable" );
603 
604                 fork.setDebug( true );
605             }
606 
607             if ( getArgLine() != null )
608             {
609                 List args = Arrays.asList( getArgLine().split( " " ) );
610                 if ( args.contains( "-da" ) || args.contains( "-disableassertions" ) )
611                 {
612                     setEnableAssertions( false );
613                 }
614             }
615         }
616         return fork;
617     }
618 
619     private void verifyLegalSystemProperties()
620     {
621         final Properties properties = getInternalSystemProperties();
622         Iterator iter = properties.keySet().iterator();
623 
624         while ( iter.hasNext() )
625         {
626             String key = (String) iter.next();
627 
628             if ( "java.library.path".equals( key ) )
629             {
630                 getLog().warn(
631                     "java.library.path cannot be set as system property, use <argLine>-Djava.library.path=...<argLine> instead" );
632             }
633         }
634     }
635 
636 
637     /**
638      * Where surefire stores its own temp files
639      *
640      * @return A file pointing to the location of surefire's own temp files
641      */
642     private File getSurefireTempDir()
643     {
644         return new File( getReportsDirectory().getParentFile(), "surefire" );
645     }
646 
647     private String getConfigChecksum()
648     {
649         ChecksumCalculator checksum = new ChecksumCalculator();
650         checksum.add( getPluginName() );
651         checksum.add( isSkipTests() );
652         checksum.add( isSkipExec() );
653         checksum.add( isSkip() );
654         checksum.add( getTestClassesDirectory() );
655         checksum.add( getClassesDirectory() );
656         checksum.add( getClasspathDependencyExcludes() );
657         checksum.add( getClasspathDependencyScopeExclude() );
658         checksum.add( getAdditionalClasspathElements() );
659         checksum.add( getReportsDirectory() );
660         checksum.add( getTestSourceDirectory() );
661         checksum.add( getTest() );
662         checksum.add( getIncludes() );
663         checksum.add( getExcludes() );
664         checksum.add( getLocalRepository() );
665         checksum.add( getSystemProperties() );
666         checksum.add( getSystemPropertyVariables() );
667         checksum.add( getProperties() );
668         checksum.add( isPrintSummary() );
669         checksum.add( getReportFormat() );
670         checksum.add( isUseFile() );
671         checksum.add( isRedirectTestOutputToFile() );
672         checksum.add( getForkMode() );
673         checksum.add( getJvm() );
674         checksum.add( getArgLine() );
675         checksum.add( getDebugForkedProcess() );
676         checksum.add( getForkedProcessTimeoutInSeconds() );
677         checksum.add( getEnvironmentVariables() );
678         checksum.add( getWorkingDirectory() );
679         checksum.add( isChildDelegation() );
680         checksum.add( getGroups() );
681         checksum.add( getExcludedGroups() );
682         checksum.add( getSuiteXmlFiles() );
683         checksum.add( getJunitArtifact() );
684         checksum.add( getTestNGArtifactName() );
685         checksum.add( getThreadCount() );
686         checksum.add( getPerCoreThreadCount() );
687         checksum.add( getUseUnlimitedThreads() );
688         checksum.add( getParallel() );
689         checksum.add( isTrimStackTrace() );
690         checksum.add( getRemoteRepositories() );
691         checksum.add( isDisableXmlReport() );
692         checksum.add( isUseSystemClassLoader() );
693         checksum.add( isUseManifestOnlyJar() );
694         checksum.add( isEnableAssertions() );
695         checksum.add( getObjectFactory() );
696         checksum.add( getFailIfNoTests() );
697         checksum.add( getRunOrder() );
698         addPluginSpecificChecksumItems( checksum );
699         return checksum.getSha1();
700 
701     }
702 
703     protected abstract void addPluginSpecificChecksumItems( ChecksumCalculator checksum );
704 
705     protected boolean hasExecutedBefore()
706     {
707         // A tribute to Linus Torvalds
708         String configChecksum = getConfigChecksum();
709         Map pluginContext = getPluginContext();
710         if ( pluginContext.containsKey( configChecksum ) )
711         {
712             getLog().info( "Skipping execution of surefire because it has already been run for this configuration" );
713             return true;
714         }
715         pluginContext.put( configChecksum, configChecksum );
716 
717         return false;
718     }
719 
720     protected ClassLoaderConfiguration getClassLoaderConfiguration( ForkConfiguration fork )
721     {
722         return fork.isForking()
723             ? new ClassLoaderConfiguration( isUseSystemClassLoader(), isUseManifestOnlyJar() )
724             : new ClassLoaderConfiguration( false, false );
725     }
726 
727     protected abstract String[] getDefaultIncludes();
728 
729     /**
730      * Generate the test classpath.
731      *
732      * @return List containing the classpath elements
733      * @throws org.apache.maven.artifact.DependencyResolutionRequiredException
734      *                                     when dependency resolution fails
735      * @throws org.apache.maven.plugin.MojoExecutionException
736      *                                     upon other problems
737      * @throws InvalidVersionSpecificationException
738      *                                     when it happens
739      * @throws MojoFailureException        when it happens
740      * @throws ArtifactNotFoundException   when it happens
741      * @throws ArtifactResolutionException when it happens
742      */
743     public Classpath generateTestClasspath()
744         throws DependencyResolutionRequiredException, MojoExecutionException, InvalidVersionSpecificationException,
745         MojoFailureException, ArtifactResolutionException, ArtifactNotFoundException
746     {
747         List classpath = new ArrayList( 2 + getProject().getArtifacts().size() );
748 
749         classpath.add( getTestClassesDirectory().getAbsolutePath() );
750 
751         classpath.add( getClassesDirectory().getAbsolutePath() );
752 
753         Set classpathArtifacts = getProject().getArtifacts();
754 
755         if ( getClasspathDependencyScopeExclude() != null && !getClasspathDependencyScopeExclude().equals( "" ) )
756         {
757             ArtifactFilter dependencyFilter = new ScopeArtifactFilter( getClasspathDependencyScopeExclude() );
758             classpathArtifacts = this.filterArtifacts( classpathArtifacts, dependencyFilter );
759         }
760 
761         if ( getClasspathDependencyExcludes() != null )
762         {
763             ArtifactFilter dependencyFilter = new PatternIncludesArtifactFilter( getClasspathDependencyExcludes() );
764             classpathArtifacts = this.filterArtifacts( classpathArtifacts, dependencyFilter );
765         }
766 
767         for ( Iterator iter = classpathArtifacts.iterator(); iter.hasNext(); )
768         {
769             Artifact artifact = (Artifact) iter.next();
770             if ( artifact.getArtifactHandler().isAddedToClasspath() )
771             {
772                 File file = artifact.getFile();
773                 if ( file != null )
774                 {
775                     classpath.add( file.getPath() );
776                 }
777             }
778         }
779 
780         // Add additional configured elements to the classpath
781         if ( getAdditionalClasspathElements() != null )
782         {
783             for ( Iterator iter = getAdditionalClasspathElements().iterator(); iter.hasNext(); )
784             {
785                 String classpathElement = (String) iter.next();
786                 if ( classpathElement != null )
787                 {
788                     classpath.add( classpathElement );
789                 }
790             }
791         }
792 
793         // adding TestNG MethodSelector to the classpath
794         // Todo: move
795         if ( getTestNgArtifact() != null )
796         {
797             Artifact testNgUtils = getTestNgUtilsArtifact();
798             String path = testNgUtils.getFile().getPath();
799             classpath.add( path );
800 
801         }
802 
803         return new Classpath( classpath );
804     }
805 
806     protected Artifact getTestNgUtilsArtifact()
807         throws ArtifactResolutionException, ArtifactNotFoundException
808     {
809         Artifact surefireArtifact =
810             (Artifact) getPluginArtifactMap().get( "org.apache.maven.surefire:surefire-booter" );
811         String surefireVersion = surefireArtifact.getBaseVersion();
812         Artifact testNgUtils =
813             getArtifactFactory().createArtifact( "org.apache.maven.surefire", "surefire-testng-utils", surefireVersion,
814                                                  "runtime", "jar" );
815 
816         getArtifactResolver().resolve( testNgUtils, getRemoteRepositories(), getLocalRepository() );
817         return testNgUtils;
818     }
819 
820     /**
821      * Return a new set containing only the artifacts accepted by the given filter.
822      *
823      * @param artifacts The unfiltered artifacts
824      * @param filter    The filter to apply
825      * @return The filtered result
826      */
827     private Set filterArtifacts( Set artifacts, ArtifactFilter filter )
828     {
829         Set filteredArtifacts = new LinkedHashSet();
830 
831         for ( Iterator iter = artifacts.iterator(); iter.hasNext(); )
832         {
833             Artifact artifact = (Artifact) iter.next();
834             if ( !filter.include( artifact ) )
835             {
836                 filteredArtifacts.add( artifact );
837             }
838         }
839 
840         return filteredArtifacts;
841     }
842 
843     private void showMap( Map map, String setting )
844     {
845         for ( Iterator i = map.keySet().iterator(); i.hasNext(); )
846         {
847             String key = (String) i.next();
848             String value = (String) map.get( key );
849             getLog().debug( "Setting " + setting + " [" + key + "]=[" + value + "]" );
850         }
851     }
852 
853 
854     private ArtifactResolutionResult resolveArtifact( Artifact filteredArtifact, Artifact providerArtifact )
855     {
856         ArtifactFilter filter = null;
857         if ( filteredArtifact != null )
858         {
859             filter = new ExcludesArtifactFilter(
860                 Collections.singletonList( filteredArtifact.getGroupId() + ":" + filteredArtifact.getArtifactId() ) );
861         }
862 
863         Artifact originatingArtifact = getArtifactFactory().createBuildArtifact( "dummy", "dummy", "1.0", "jar" );
864 
865         try
866         {
867             return getArtifactResolver().resolveTransitively( Collections.singleton( providerArtifact ),
868                                                               originatingArtifact, getLocalRepository(),
869                                                               getRemoteRepositories(), getMetadataSource(), filter );
870         }
871         catch ( ArtifactResolutionException e )
872         {
873             throw new NestedRuntimeException( e );
874         }
875         catch ( ArtifactNotFoundException e )
876         {
877             throw new NestedRuntimeException( e );
878         }
879     }
880 
881     private Classpath getArtifactClasspath( Artifact surefireArtifact )
882     {
883         ArtifactResolutionResult result = resolveArtifact( null, surefireArtifact );
884 
885         List items = new ArrayList();
886         for ( Iterator i = result.getArtifacts().iterator(); i.hasNext(); )
887         {
888             Artifact artifact = (Artifact) i.next();
889 
890             getLog().debug(
891                 "Adding to " + getPluginName() + " booter test classpath: " + artifact.getFile().getAbsolutePath()
892                     + " Scope: " + artifact.getScope() );
893 
894             items.add( artifact.getFile().getAbsolutePath() );
895         }
896         return new Classpath( items );
897     }
898 
899     protected void processSystemProperties( boolean setInSystem )
900     {
901         copyPropertiesToInternalSystemProperties( getSystemProperties() );
902 
903         if ( this.getSystemPropertyVariables() != null )
904         {
905             for ( Iterator i = getSystemPropertyVariables().keySet().iterator(); i.hasNext(); )
906             {
907                 String key = (String) i.next();
908                 String value = (String) getSystemPropertyVariables().get( key );
909                 //java Properties does not accept null value
910                 if ( value != null )
911                 {
912                     getInternalSystemProperties().setProperty( key, value );
913                 }
914             }
915         }
916 
917         setOriginalSystemProperties( (Properties) System.getProperties().clone() );
918 
919         // We used to take all of our system properties and dump them in with the
920         // user specified properties for SUREFIRE-121, causing SUREFIRE-491.
921         // Not gonna do THAT any more... instead, we only propagate those system properties
922         // that have been explicitly specified by the user via -Dkey=value on the CLI
923 
924         copyPropertiesToInternalSystemProperties( getUserProperties() );
925 
926         getInternalSystemProperties().setProperty( "basedir", getBasedir().getAbsolutePath() );
927         getInternalSystemProperties().setProperty( "user.dir", getWorkingDirectory().getAbsolutePath() );
928         getInternalSystemProperties().setProperty( "localRepository", getLocalRepository().getBasedir() );
929 
930         if ( setInSystem )
931         {
932             // Add all system properties configured by the user
933             Iterator iter = getInternalSystemProperties().keySet().iterator();
934 
935             while ( iter.hasNext() )
936             {
937                 String key = (String) iter.next();
938 
939                 String value = getInternalSystemProperties().getProperty( key );
940 
941                 System.setProperty( key, value );
942             }
943         }
944     }
945 
946     private void copyPropertiesToInternalSystemProperties( Properties properties )
947     {
948         if ( properties != null )
949         {
950             for ( Iterator i = properties.keySet().iterator(); i.hasNext(); )
951             {
952                 String key = (String) i.next();
953                 String value = properties.getProperty( key );
954                 getInternalSystemProperties().setProperty( key, value );
955             }
956         }
957     }
958 
959     private Properties getUserProperties()
960     {
961         Properties props = null;
962         try
963         {
964             // try calling MavenSession.getUserProperties() from Maven 2.1.0-M1+
965             Method getUserProperties = getSession().getClass().getMethod( "getUserProperties", null );
966             props = (Properties) getUserProperties.invoke( getSession(), null );
967         }
968         catch ( Exception e )
969         {
970             String msg = "Build uses Maven 2.0.x, cannot propagate system properties"
971                 + " from command line to tests (cf. SUREFIRE-121)";
972             if ( getLog().isDebugEnabled() )
973             {
974                 getLog().warn( msg, e );
975             }
976             else
977             {
978                 getLog().warn( msg );
979             }
980         }
981         if ( props == null )
982         {
983             props = new Properties();
984         }
985         return props;
986     }
987 
988     /**
989      * <p/>
990      * Adds Reporters that will generate reports with different formatting.
991      * <p/>
992      * The Reporter that will be added will be based on the value of the parameter useFile, reportFormat, and
993      * printSummary.
994      *
995      * @param forking forking
996      * @return a list of reporters
997      */
998     private List getReporters( boolean forking )
999     {
1000         List reports = new ArrayList();
1001         final String consoleReporter = getConsoleReporter( forking );
1002         if ( consoleReporter != null )
1003         {
1004             reports.add( consoleReporter );
1005         }
1006 
1007         if ( isUseFile() )
1008         {
1009             if ( BRIEF_REPORT_FORMAT.equals( getReportFormat() ) )
1010             {
1011                 reports.add( BriefFileReporter.class.getName() );
1012             }
1013             else if ( PLAIN_REPORT_FORMAT.equals( getReportFormat() ) )
1014             {
1015                 reports.add( FileReporter.class.getName() );
1016             }
1017         }
1018 
1019         if ( !isDisableXmlReport() )
1020         {
1021             reports.add( XMLReporter.class.getName() );
1022         }
1023         return reports;
1024     }
1025 
1026     /**
1027      * Returns the reporter that will write to the console
1028      *
1029      * @param forking forking
1030      * @return a console reporter of null if no console reporting
1031      */
1032     private String getConsoleReporter( boolean forking )
1033     {
1034         if ( isUseFile() )
1035         {
1036             if ( forking )
1037             {
1038                 return ForkingConsoleReporter.class.getName();
1039             }
1040             else
1041             {
1042                 return isPrintSummary() ? ConsoleReporter.class.getName() : null;
1043             }
1044         }
1045         else if ( BRIEF_REPORT_FORMAT.equals( getReportFormat() ) )
1046         {
1047             return BriefConsoleReporter.class.getName();
1048         }
1049         else if ( PLAIN_REPORT_FORMAT.equals( getReportFormat() ) )
1050         {
1051             return DetailedConsoleReporter.class.getName();
1052         }
1053         return null;
1054     }
1055 
1056     protected void ensureWorkingDirectoryExists()
1057         throws MojoFailureException
1058     {
1059         if ( getWorkingDirectory() == null )
1060         {
1061             throw new MojoFailureException( "workingDirectory cannot be null" );
1062         }
1063 
1064         if ( !getWorkingDirectory().exists() )
1065         {
1066             if ( !getWorkingDirectory().mkdirs() )
1067             {
1068                 throw new MojoFailureException( "Cannot create workingDirectory " + getWorkingDirectory() );
1069             }
1070         }
1071 
1072         if ( !getWorkingDirectory().isDirectory() )
1073         {
1074             throw new MojoFailureException(
1075                 "workingDirectory " + getWorkingDirectory() + " exists and is not a directory" );
1076         }
1077     }
1078 
1079     protected void ensureParallelRunningCompatibility()
1080         throws MojoFailureException
1081     {
1082         if ( isMavenParallel() && isForkModeNever() )
1083         {
1084             throw new MojoFailureException( "parallel maven execution is not compatible with surefire forkmode NEVER" );
1085         }
1086     }
1087 
1088     protected void warnIfUselessUseSystemClassLoaderParameter()
1089     {
1090         if ( isUseSystemClassLoader() && isForkModeNever() )
1091         {
1092             getLog().warn( "useSystemClassloader setting has no effect when not forking" );
1093         }
1094     }
1095 
1096     class TestNgProviderInfo
1097         implements ProviderInfo
1098     {
1099         private final Artifact testNgArtifact;
1100 
1101         TestNgProviderInfo( Artifact testNgArtifact )
1102         {
1103             this.testNgArtifact = testNgArtifact;
1104         }
1105 
1106         public String getProviderName()
1107         {
1108             return "org.apache.maven.surefire.testng.TestNGProvider";
1109         }
1110 
1111         public boolean isApplicable()
1112         {
1113             return testNgArtifact != null;
1114         }
1115 
1116         public void addProviderProperties()
1117         {
1118             convertTestNGParameters();
1119         }
1120 
1121         public Classpath getProviderClasspath()
1122             throws ArtifactResolutionException, ArtifactNotFoundException
1123         {
1124             Artifact surefireArtifact =
1125                 (Artifact) getPluginArtifactMap().get( "org.apache.maven.surefire:surefire-booter" );
1126             return dependencyResolver.getProviderClasspath( "surefire-testng", surefireArtifact.getBaseVersion(),
1127                                                             testNgArtifact );
1128         }
1129     }
1130 
1131     class JUnit3ProviderInfo
1132         implements ProviderInfo
1133     {
1134         public String getProviderName()
1135         {
1136             return "org.apache.maven.surefire.junit.JUnit3Provider";
1137         }
1138 
1139         public boolean isApplicable()
1140         {
1141             return true;
1142         }
1143 
1144         public void addProviderProperties()
1145         {
1146         }
1147 
1148         public Classpath getProviderClasspath()
1149             throws ArtifactResolutionException, ArtifactNotFoundException
1150         {
1151             // add the JUnit provider as default - it doesn't require JUnit to be present,
1152             // since it supports POJO tests.
1153             return dependencyResolver.getProviderClasspath( "surefire-junit3", surefireArtifact.getBaseVersion(),
1154                                                             null );
1155 
1156         }
1157 
1158     }
1159 
1160     class JUnit4ProviderInfo
1161         implements ProviderInfo
1162     {
1163         private final Artifact junitArtifact;
1164 
1165         private final Artifact junitDepArtifact;
1166 
1167         JUnit4ProviderInfo( Artifact junitArtifact, Artifact junitDepArtifact )
1168         {
1169             this.junitArtifact = junitArtifact;
1170             this.junitDepArtifact = junitDepArtifact;
1171         }
1172 
1173         public String getProviderName()
1174         {
1175             return "org.apache.maven.surefire.junit4.JUnit4Provider";
1176         }
1177 
1178         public boolean isApplicable()
1179         {
1180             return junitDepArtifact != null || isAnyJunit4( junitArtifact );
1181         }
1182 
1183         public void addProviderProperties()
1184         {
1185         }
1186 
1187         public Classpath getProviderClasspath()
1188             throws ArtifactResolutionException, ArtifactNotFoundException
1189         {
1190             return dependencyResolver.getProviderClasspath( "surefire-junit4", surefireArtifact.getBaseVersion(),
1191                                                             null );
1192 
1193         }
1194 
1195     }
1196 
1197     class JUnitCoreProviderInfo
1198         implements ProviderInfo
1199     {
1200         private final Artifact junitArtifact;
1201 
1202         private final Artifact junitDepArtifact;
1203 
1204         JUnitCoreProviderInfo( Artifact junitArtifact, Artifact junitDepArtifact )
1205         {
1206             this.junitArtifact = junitArtifact;
1207             this.junitDepArtifact = junitDepArtifact;
1208         }
1209 
1210         public String getProviderName()
1211         {
1212             return "org.apache.maven.surefire.junitcore.JUnitCoreProvider";
1213         }
1214 
1215         private boolean is47CompatibleJunitDep()
1216         {
1217             return junitDepArtifact != null && isJunit47Compatible( junitDepArtifact );
1218         }
1219 
1220         public boolean isApplicable()
1221         {
1222             final boolean isJunitArtifact47 = isAnyJunit4( junitArtifact ) && isJunit47Compatible( junitArtifact );
1223             return isAnyConcurrencySelected() && ( isJunitArtifact47 || is47CompatibleJunitDep() );
1224         }
1225 
1226         public void addProviderProperties()
1227         {
1228             convertJunitCoreParameters();
1229         }
1230 
1231         public Classpath getProviderClasspath()
1232             throws ArtifactResolutionException, ArtifactNotFoundException
1233         {
1234             return dependencyResolver.getProviderClasspath( "surefire-junit47", surefireArtifact.getBaseVersion(),
1235                                                             null );
1236         }
1237 
1238     }
1239 
1240     public class DynamicProviderInfo
1241         implements ConfigurableProviderInfo
1242     {
1243         final String providerName;
1244 
1245         DynamicProviderInfo( String providerName )
1246         {
1247             this.providerName = providerName;
1248         }
1249 
1250         public ProviderInfo instantiate( String providerName )
1251         {
1252             return new DynamicProviderInfo( providerName );
1253         }
1254 
1255         public String getProviderName()
1256         {
1257             return providerName;
1258         }
1259 
1260         public boolean isApplicable()
1261         {
1262             return true;
1263         }
1264 
1265         public void addProviderProperties()
1266         {
1267             // Ok this is a bit lazy.
1268             convertJunitCoreParameters();
1269             convertTestNGParameters();
1270         }
1271 
1272 
1273         public Classpath getProviderClasspath()
1274             throws ArtifactResolutionException, ArtifactNotFoundException
1275         {
1276             final Map pluginArtifactMap = getPluginArtifactMap();
1277             Artifact plugin = (Artifact) pluginArtifactMap.get( "org.apache.maven.plugins:maven-surefire-plugin" );
1278             return dependencyResolver.addProviderToClasspath( pluginArtifactMap, plugin );
1279         }
1280 
1281     }
1282 
1283 }