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