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