View Javadoc

1   /*
2    * #%L
3    * Native ARchive plugin for Maven
4    * %%
5    * Copyright (C) 2002 - 2014 NAR Maven Plugin developers.
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   * 
11   * http://www.apache.org/licenses/LICENSE-2.0
12   * 
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package com.github.maven_nar;
21  
22  import java.io.File;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Properties;
30  
31  import org.apache.maven.artifact.Artifact;
32  import org.apache.maven.artifact.factory.ArtifactFactory;
33  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
34  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
35  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
36  import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
37  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
38  import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
39  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
40  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
41  import org.apache.maven.artifact.versioning.VersionRange;
42  import org.apache.maven.execution.MavenSession;
43  import org.apache.maven.plugin.MojoExecutionException;
44  import org.apache.maven.plugin.MojoFailureException;
45  import org.apache.maven.plugins.annotations.Component;
46  import org.apache.maven.plugins.annotations.LifecyclePhase;
47  import org.apache.maven.plugins.annotations.Mojo;
48  import org.apache.maven.plugins.annotations.Parameter;
49  import org.apache.maven.plugins.annotations.ResolutionScope;
50  import org.apache.maven.project.MavenProject;
51  import org.apache.maven.shared.artifact.filter.collection.ScopeFilter;
52  import org.apache.maven.surefire.booter.ForkConfiguration;
53  import org.apache.maven.surefire.booter.SurefireBooter;
54  import org.apache.maven.surefire.booter.SurefireBooterForkException;
55  import org.apache.maven.surefire.booter.SurefireExecutionException;
56  import org.apache.maven.surefire.report.BriefConsoleReporter;
57  import org.apache.maven.surefire.report.BriefFileReporter;
58  import org.apache.maven.surefire.report.ConsoleReporter;
59  import org.apache.maven.surefire.report.DetailedConsoleReporter;
60  import org.apache.maven.surefire.report.FileReporter;
61  import org.apache.maven.surefire.report.ForkingConsoleReporter;
62  import org.apache.maven.surefire.report.XMLReporter;
63  import org.apache.maven.toolchain.Toolchain;
64  import org.apache.maven.toolchain.ToolchainManager;
65  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
66  import org.codehaus.plexus.util.StringUtils;
67  
68  // Copied from Maven maven-surefire-plugin 2.4.3
69  
70  /**
71   * Run integration tests using Surefire. This goal was copied from Maven's
72   * surefire plugin to accomodate a few things
73   * for the NAR plugin:
74   * <P>
75   * 1. To test a jar file with its native module we can only run after the
76   * package phase, so we use the integration-test phase.
77   * </P>
78   * <P>
79   * 2. We need to set java.library.path to an AOL (architecture-os-linker)
80   * specific value, but AOL is only known in the NAR plugin and thus cannot be
81   * set from the pom.
82   * </P>
83   * <P>
84   * 3. To have the java.library.path definition picked up by java we need the
85   * "pertest" forkmode. To use this goal you need to put the test sources in the
86   * regular test directories but disable the running of the tests by the
87   * maven-surefire-plugin by setting maven.test.skip.exec to false in your pom.
88   * </P>
89   *
90   * @author Jason van Zyl (modified by Mark Donszelmann, noted by DUNS)
91   * @version $Id: SurefirePlugin.java 652773 2008-05-02 05:58:54Z dfabulich $
92   *          Mods by Duns for NAR
93   */
94  // DUNS, changed class name, inheritance, goal and phase
95  @Mojo(name = "nar-integration-test", defaultPhase = LifecyclePhase.INTEGRATION_TEST,
96    requiresDependencyResolution = ResolutionScope.TEST)
97  public class NarIntegrationTestMojo extends AbstractDependencyMojo {
98    private static final String BRIEF_REPORT_FORMAT = "brief";
99  
100   private static final String PLAIN_REPORT_FORMAT = "plain";
101 
102   // DUNS added because of naming conflict
103   /**
104    * Skip running of NAR integration test plugin
105    */
106   @Parameter(property = "skipNar")
107   private boolean skipNar;
108 
109   // DUNS changed to nar. because of naming conflict
110   /**
111    * Set this to 'true' to skip running tests, but still compile them. Its use
112    * is NOT RECOMMENDED, but quite
113    * convenient on occasion.
114    * 
115    * @since 2.4
116    */
117   @Parameter(property = "skipNarTests")
118   private boolean skipNarTests;
119 
120   // DUNS changed to nar. because of naming conflict
121   /**
122    * DEPRECATED This old parameter is just like skipTests, but bound to the old
123    * property maven.test.skip.exec. Use
124    * -DskipTests instead; it's shorter.
125    * 
126    * @deprecated
127    * @since 2.3
128    */
129   @Deprecated
130   @Parameter(property = "nar.test.skip.exec")
131   private boolean skipNarExec;
132 
133   // DUNS changed to nar. because of naming conflict
134   /**
135    * Set this to true to ignore a failure during testing. Its use is NOT
136    * RECOMMENDED, but quite convenient on
137    * occasion.
138    * 
139    */
140   @Parameter(property = "nar.test.failure.ignore")
141   private boolean testFailureIgnore;
142 
143   /**
144    * The base directory of the project being tested. This can be obtained in
145    * your unit test by
146    * System.getProperty("basedir").
147    * 
148    */
149   @Parameter(property = "basedir", required = true)
150   private File basedir;
151 
152   /**
153    * The directory containing generated test classes of the project being
154    * tested.
155    */
156   @Parameter(property = "project.build.testOutputDirectory", required = true)
157   private File testClassesDirectory;
158 
159   /**
160    * The directory containing generated classes of the project being tested.
161    */
162   @Parameter(property = "project.build.outputDirectory", required = true)
163   private File classesDirectory;
164 
165   /**
166    * The Maven Project Object
167    */
168   // DUNS, made private
169   @Component
170   private MavenProject project;
171 
172   /**
173    * The classpath elements of the project being tested.
174    */
175   @Parameter(property = "project.testClasspathElements", required = true, readonly = true)
176   private List classpathElements;
177 
178   /**
179    * Additional elements to be appended to the classpath.
180    * 
181    * @since 2.4
182    */
183   @Parameter
184   private List additionalClasspathElements;
185 
186   /**
187    * Base directory where all reports are written to.
188    */
189   @Parameter(defaultValue = "${project.build.directory}/surefire-reports")
190   private File reportsDirectory;
191 
192   /**
193    * The test source directory containing test class sources.
194    * 
195    * @since 2.2
196    */
197   @Parameter(property = "project.build.testSourceDirectory", required = true)
198   private File testSourceDirectory;
199 
200   /**
201    * Specify this parameter to run individual tests by file name, overriding the
202    * <code>includes/excludes</code> parameters. Each pattern you specify here
203    * will be used to create an include pattern formatted like
204    * <code>**&#47;${test}.java</code>, so you can just type "-Dtest=MyTest" to
205    * run a single test called
206    * "foo/MyTest.java". This parameter will override the TestNG suiteXmlFiles
207    * parameter.
208    */
209   @Parameter(property = "test")
210   private String test;
211 
212   /**
213    * List of patterns (separated by commas) used to specify the tests that
214    * should be included in testing. When not
215    * specified and when the <code>test</code> parameter is not specified, the
216    * default includes will be
217    * <code>**&#47;Test*.java   **&#47;*Test.java   **&#47;*TestCase.java</code>.
218    * This parameter is ignored if TestNG
219    * suiteXmlFiles are specified.
220    */
221   @Parameter
222   private List includes;
223 
224   /**
225    * List of patterns (separated by commas) used to specify the tests that
226    * should be excluded in testing. When not
227    * specified and when the <code>test</code> parameter is not specified, the
228    * default excludes will be <code>**&#47;*$*</code> (which excludes all inner
229    * classes). This parameter is ignored if TestNG suiteXmlFiles are
230    * specified.
231    */
232   @Parameter
233   private List excludes;
234 
235   /**
236    * List of System properties to pass to the JUnit tests.
237    * 
238    */
239   @Parameter
240   private Properties systemProperties;
241 
242   /**
243    * List of properties for configuring all TestNG related configurations. This
244    * is the new preferred method of
245    * configuring TestNG.
246    */
247   @Parameter
248   private Properties properties;
249 
250   /**
251    * Map of of plugin artifacts.
252    */
253   @Parameter(property = "plugin.artifactMap", required = true, readonly = true)
254   private Map pluginArtifactMap;
255 
256   /**
257    * Map of of project artifacts.
258    */
259   @Parameter(property = "project.artifactMap", readonly = true, required = true)
260   private Map projectArtifactMap;
261 
262   /**
263    * Option to print summary of test suites or just print the test cases that
264    * has errors.
265    */
266   @Parameter(property = "surefire.printSummary", defaultValue = "true")
267   private boolean printSummary = true;
268 
269   /**
270    * Selects the formatting for the test report to be generated. Can be set as
271    * brief or plain.
272    */
273   @Parameter(property = "surefire.reportFormat", defaultValue = "brief")
274   private String reportFormat;
275 
276   /**
277    * Option to generate a file test report or just output the test report to the
278    * console.
279    * 
280    */
281   @Parameter(property = "surefire.useFile", defaultValue = "true")
282   private boolean useFile;
283 
284   // DUNS changed to nar. because of naming conflict
285   /**
286    * When forking, set this to true to redirect the unit test standard output to
287    * a file (found in
288    * reportsDirectory/testName-output.txt).
289    * 
290    * @since 2.3
291    */
292   @Parameter(property = "nar.test.redirectTestOutputToFile")
293   private boolean redirectTestOutputToFile;
294 
295   /**
296    * Set this to "true" to cause a failure if there are no tests to run.
297    * Defaults to false.
298    *
299    * @since 2.4
300    */
301   @Parameter(property = "failIfNoTests")
302   private Boolean failIfNoTests;
303 
304   /**
305    * Option to specify the forking mode. Can be "never", "once" or "always".
306    * "none" and "pertest" are also accepted
307    * for backwards compatibility.
308    * 
309    * @since 2.1
310    */
311   @Parameter(property = "forkMode", defaultValue = "once")
312   private String forkMode;
313 
314   /**
315    * Option to specify the jvm (or path to the java executable) to use with the
316    * forking options. For the default, the
317    * jvm will be the same as the one used to run Maven.
318    * 
319    * @since 2.1
320    */
321   @Parameter(property = "jvm")
322   private String jvm;
323 
324   /**
325    * Arbitrary JVM options to set on the command line.
326    * 
327    * @since 2.1
328    */
329   @Parameter(property = "argLine")
330   private String argLine;
331 
332   /**
333    * Attach a debugger to the forked JVM. If set to "true", the process will
334    * suspend and wait for a debugger to attach
335    * on port 5005. If set to some other string, that string will be appended to
336    * the argLine, allowing you to configure
337    * arbitrary debuggability options (without overwriting the other options
338    * specified in the argLine).
339    *
340    * @since 2.4
341    */
342   @Parameter(property = "maven.surefire.debug")
343   private String debugForkedProcess;
344 
345   /**
346    * Kill the forked test process after a certain number of seconds. If set to
347    * 0, wait forever for the process, never
348    * timing out.
349    * 
350    * @since 2.4
351    */
352   @Parameter(property = "surefire.timeout")
353   private int forkedProcessTimeoutInSeconds;
354 
355   /**
356    * Additional environments to set on the command line.
357    * 
358    * @since 2.1.3
359    */
360   @Parameter
361   private Map environmentVariables = new HashMap();
362 
363   /**
364    * Command line working directory.
365    * 
366    * @since 2.1.3
367    */
368   @Parameter(property = "basedir")
369   private File workingDirectory;
370 
371   /**
372    * When false it makes tests run using the standard classloader delegation
373    * instead of the default Maven isolated
374    * classloader. Only used when forking (forkMode is not "none").<br/>
375    * Setting it to false helps with some problems caused by conflicts between
376    * xml parsers in the classpath and the
377    * Java 5 provider parser.
378    * 
379    * @since 2.1
380    */
381   @Parameter(property = "childDelegation")
382   private boolean childDelegation;
383 
384   /**
385    * (TestNG only) Groups for this test. Only classes/methods/etc decorated with
386    * one of the groups specified here will
387    * be included in test run, if specified. This parameter is overridden if
388    * suiteXmlFiles are specified.
389    *
390    * @since 2.2
391    */
392   @Parameter(property = "groups")
393   private String groups;
394 
395   /**
396    * (TestNG only) Excluded groups. Any methods/classes/etc with one of the
397    * groups specified in this list will
398    * specifically not be run. This parameter is overridden if suiteXmlFiles are
399    * specified.
400    *
401    * @since 2.2
402    */
403   @Parameter(property = "excludedGroups")
404   private String excludedGroups;
405 
406   /**
407    * (TestNG only) List of TestNG suite xml file locations, seperated by commas.
408    * Note that suiteXmlFiles is
409    * incompatible with several other parameters on this plugin, like
410    * includes/excludes. This parameter is ignored if
411    * the "test" parameter is specified (allowing you to run a single test
412    * instead of an entire suite).
413    * 
414    * @since 2.2
415    */
416   @Parameter
417   private File[] suiteXmlFiles;
418 
419   /**
420    * Allows you to specify the name of the JUnit artifact. If not set,
421    * <code>junit:junit</code> will be used.
422    * 
423    * @since 2.3.1
424    */
425   @Parameter(property = "junitArtifactName", defaultValue = "junit:junit")
426   private String junitArtifactName;
427 
428   /**
429    * Allows you to specify the name of the TestNG artifact. If not set,
430    * <code>org.testng:testng</code> will be used.
431    * 
432    * @since 2.3.1
433    */
434   @Parameter(property = "testNGArtifactName", defaultValue = "org.testng:testng")
435   private String testNGArtifactName;
436 
437   /**
438    * (TestNG only) The attribute thread-count allows you to specify how many
439    * threads should be allocated for this
440    * execution. Only makes sense to use in conjunction with parallel.
441    * 
442    * @since 2.2
443    */
444   @Parameter(property = "threadCount")
445   private int threadCount;
446 
447   /**
448    * (TestNG only) When you use the parallel attribute, TestNG will try to run
449    * all your test methods in separate
450    * threads, except for methods that depend on each other, which will be run in
451    * the same thread in order to respect
452    * their order of execution.
453    * 
454    * @todo test how this works with forking, and console/file output parallelism
455    * @since 2.2
456    */
457   @Parameter(property = "parallel")
458   private String parallel;
459 
460   /**
461    * Whether to trim the stack trace in the reports to just the lines within the
462    * test, or show the full trace.
463    * 
464    * @since 2.2
465    */
466   @Parameter(property = "trimStackTrace", defaultValue = "true")
467   private boolean trimStackTrace = true;
468 
469   /**
470    * Creates the artifact
471    */
472   @Component
473   private ArtifactFactory artifactFactory;
474 
475   /**
476    * For retrieval of artifact's metadata.
477    */
478   @Component
479   private ArtifactMetadataSource metadataSource;
480 
481   private Properties originalSystemProperties;
482 
483   /**
484    * Flag to disable the generation of report files in xml format.
485    * 
486    * @since 2.2
487    */
488   @Parameter(property = "disableXmlReport")
489   private boolean disableXmlReport;
490 
491   /**
492    * Option to pass dependencies to the system's classloader instead of using an
493    * isolated class loader when forking.
494    * Prevents problems with JDKs which implement the service provider lookup
495    * mechanism by using the system's
496    * classloader. Default value is "true".
497    * 
498    * @since 2.3
499    */
500   @Parameter(property = "surefire.useSystemClassLoader")
501   private Boolean useSystemClassLoader;
502 
503   /**
504    * By default, Surefire forks your tests using a manifest-only jar; set this
505    * parameter to "false" to force it to
506    * launch your tests with a plain old Java classpath. (See
507    * http://maven.apache.org/plugins/maven-surefire-plugin/examples/class-
508    * loading.html for a more detailed explanation
509    * of manifest-only jars and their benefits.) Default value is "true". Beware,
510    * setting this to "false" may cause
511    * your tests to fail on Windows if your classpath is too long.
512    * 
513    * @since 2.4.3
514    */
515   @Parameter(property = "surefire.useManifestOnlyJar", defaultValue = "true")
516   private boolean useManifestOnlyJar = true;
517 
518   /**
519    * By default, Surefire enables JVM assertions for the execution of your test
520    * cases. To disable the assertions, set
521    * this flag to <code>false</code>.
522    * 
523    * @since 2.3.1
524    */
525   @Parameter(property = "enableAssertions", defaultValue = "true")
526   private boolean enableAssertions;
527 
528   /**
529    * The current build session instance.
530    */
531   @Component
532   private MavenSession session;
533 
534   private void addArtifact(final SurefireBooter surefireBooter, final Artifact surefireArtifact)
535       throws ArtifactNotFoundException, ArtifactResolutionException {
536     final ArtifactResolutionResult result = resolveArtifact(null, surefireArtifact);
537 
538     for (final Object element : result.getArtifacts()) {
539       final Artifact artifact = (Artifact) element;
540 
541       getLog().debug("Adding to surefire booter test classpath: " + artifact.getFile().getAbsolutePath());
542 
543       surefireBooter.addSurefireBootClassPathUrl(artifact.getFile().getAbsolutePath());
544     }
545   }
546 
547   private void addProvider(final SurefireBooter surefireBooter, final String provider, final String version,
548       final Artifact filteredArtifact) throws ArtifactNotFoundException, ArtifactResolutionException {
549     final Artifact providerArtifact = this.artifactFactory.createDependencyArtifact("org.apache.maven.surefire",
550         provider, VersionRange.createFromVersion(version), "jar", null, Artifact.SCOPE_TEST);
551     final ArtifactResolutionResult result = resolveArtifact(filteredArtifact, providerArtifact);
552 
553     for (final Object element : result.getArtifacts()) {
554       final Artifact artifact = (Artifact) element;
555 
556       getLog().debug("Adding to surefire test classpath: " + artifact.getFile().getAbsolutePath());
557 
558       surefireBooter.addSurefireClassPathUrl(artifact.getFile().getAbsolutePath());
559     }
560   }
561 
562   /**
563    * <p>
564    * Adds Reporters that will generate reports with different formatting.
565    * <p>
566    * The Reporter that will be added will be based on the value of the parameter
567    * useFile, reportFormat, and printSummary.
568    * 
569    * @param surefireBooter
570    *          The surefire booter that will run tests.
571    * @param forking
572    */
573   private void addReporters(final SurefireBooter surefireBooter, final boolean forking) {
574     final Boolean trimStackTrace = Boolean.valueOf(this.trimStackTrace);
575     if (this.useFile) {
576       if (this.printSummary) {
577         if (forking) {
578           surefireBooter.addReport(ForkingConsoleReporter.class.getName(), new Object[] {
579             trimStackTrace
580           });
581         } else {
582           surefireBooter.addReport(ConsoleReporter.class.getName(), new Object[] {
583             trimStackTrace
584           });
585         }
586       }
587 
588       if (BRIEF_REPORT_FORMAT.equals(this.reportFormat)) {
589         surefireBooter.addReport(BriefFileReporter.class.getName(), new Object[] {
590             this.reportsDirectory, trimStackTrace
591         });
592       } else if (PLAIN_REPORT_FORMAT.equals(this.reportFormat)) {
593         surefireBooter.addReport(FileReporter.class.getName(), new Object[] {
594             this.reportsDirectory, trimStackTrace
595         });
596       }
597     } else {
598       if (BRIEF_REPORT_FORMAT.equals(this.reportFormat)) {
599         surefireBooter.addReport(BriefConsoleReporter.class.getName(), new Object[] {
600           trimStackTrace
601         });
602       } else if (PLAIN_REPORT_FORMAT.equals(this.reportFormat)) {
603         surefireBooter.addReport(DetailedConsoleReporter.class.getName(), new Object[] {
604           trimStackTrace
605         });
606       }
607     }
608 
609     if (!this.disableXmlReport) {
610       surefireBooter.addReport(XMLReporter.class.getName(), new Object[] {
611           this.reportsDirectory, trimStackTrace
612       });
613     }
614   }
615 
616   private SurefireBooter constructSurefireBooter() throws MojoExecutionException, MojoFailureException {
617     final SurefireBooter surefireBooter = new SurefireBooter();
618 
619     final Artifact surefireArtifact = (Artifact) this.pluginArtifactMap
620         .get("org.apache.maven.surefire:surefire-booter");
621     if (surefireArtifact == null) {
622       throw new MojoExecutionException("Unable to locate surefire-booter in the list of plugin artifacts");
623     }
624 
625     surefireArtifact.isSnapshot(); // TODO: this is ridiculous, but it fixes
626                                    // getBaseVersion to be -SNAPSHOT if
627     // needed
628 
629     Artifact junitArtifact;
630     Artifact testNgArtifact;
631     try {
632       addArtifact(surefireBooter, surefireArtifact);
633 
634       junitArtifact = (Artifact) this.projectArtifactMap.get(this.junitArtifactName);
635       // SUREFIRE-378, junit can have an alternate artifact name
636       if (junitArtifact == null && "junit:junit".equals(this.junitArtifactName)) {
637         junitArtifact = (Artifact) this.projectArtifactMap.get("junit:junit-dep");
638       }
639 
640       // TODO: this is pretty manual, but I'd rather not require the plugin >
641       // dependencies section right now
642       testNgArtifact = (Artifact) this.projectArtifactMap.get(this.testNGArtifactName);
643 
644       if (testNgArtifact != null) {
645         final VersionRange range = VersionRange.createFromVersionSpec("[4.7,)");
646         if (!range.containsVersion(new DefaultArtifactVersion(testNgArtifact.getVersion()))) {
647           throw new MojoFailureException("TestNG support requires version 4.7 or above. You have declared version "
648               + testNgArtifact.getVersion());
649         }
650 
651         convertTestNGParameters();
652 
653         if (this.testClassesDirectory != null) {
654           this.properties.setProperty("testng.test.classpath", this.testClassesDirectory.getAbsolutePath());
655         }
656 
657         addArtifact(surefireBooter, testNgArtifact);
658 
659         // The plugin uses a JDK based profile to select the right testng. We
660         // might be explicity using a
661         // different one since its based on the source level, not the JVM. Prune
662         // using the filter.
663         addProvider(surefireBooter, "surefire-testng", surefireArtifact.getBaseVersion(), testNgArtifact);
664       } else if (junitArtifact != null && junitArtifact.getBaseVersion().startsWith("4")) {
665         addProvider(surefireBooter, "surefire-junit4", surefireArtifact.getBaseVersion(), null);
666       } else {
667         // add the JUnit provider as default - it doesn't require JUnit to be
668         // present,
669         // since it supports POJO tests.
670         addProvider(surefireBooter, "surefire-junit", surefireArtifact.getBaseVersion(), null);
671       }
672     } catch (final ArtifactNotFoundException e) {
673       throw new MojoExecutionException("Unable to locate required surefire provider dependency: " + e.getMessage(), e);
674     } catch (final InvalidVersionSpecificationException e) {
675       throw new MojoExecutionException("Error determining the TestNG version requested: " + e.getMessage(), e);
676     } catch (final ArtifactResolutionException e) {
677       throw new MojoExecutionException("Error to resolving surefire provider dependency: " + e.getMessage(), e);
678     }
679 
680     if (this.suiteXmlFiles != null && this.suiteXmlFiles.length > 0 && this.test == null) {
681       if (testNgArtifact == null) {
682         throw new MojoExecutionException("suiteXmlFiles is configured, but there is no TestNG dependency");
683       }
684 
685       // TODO: properties should be passed in here too
686       surefireBooter.addTestSuite("org.apache.maven.surefire.testng.TestNGXmlTestSuite", new Object[] {
687           this.suiteXmlFiles, this.testSourceDirectory.getAbsolutePath(), testNgArtifact.getBaseVersion(),
688           testNgArtifact.getClassifier(), this.properties, this.reportsDirectory
689       });
690     } else {
691       List includeList;
692       List excludeList;
693 
694       if (this.test != null) {
695         // Check to see if we are running a single test. The raw parameter will
696         // come through if it has not been set.
697 
698         // FooTest -> **/FooTest.java
699 
700         includeList = new ArrayList();
701 
702         excludeList = new ArrayList();
703 
704         if (this.failIfNoTests == null) {
705           this.failIfNoTests = Boolean.TRUE;
706         }
707 
708         final String[] testRegexes = StringUtils.split(this.test, ",");
709 
710         for (final String testRegexe : testRegexes) {
711           String testRegex = testRegexe;
712           if (testRegex.endsWith(".java")) {
713             testRegex = testRegex.substring(0, testRegex.length() - 5);
714           }
715           // Allow paths delimited by '.' or '/'
716           testRegex = testRegex.replace('.', '/');
717           includeList.add("**/" + testRegex + ".java");
718         }
719       } else {
720         includeList = this.includes;
721 
722         excludeList = this.excludes;
723 
724         // defaults here, qdox doesn't like the end javadoc value
725         // Have to wrap in an ArrayList as surefire expects an ArrayList instead
726         // of a List for some reason
727         if (includeList == null || includeList.size() == 0) {
728           includeList = new ArrayList(Arrays.asList(new String[] {
729               "**/Test*.java", "**/*Test.java", "**/*TestCase.java"
730           }));
731         }
732         if (excludeList == null || excludeList.size() == 0) {
733           excludeList = new ArrayList(Arrays.asList(new String[] {
734             "**/*$*"
735           }));
736         }
737       }
738 
739       if (testNgArtifact != null) {
740         surefireBooter.addTestSuite("org.apache.maven.surefire.testng.TestNGDirectoryTestSuite", new Object[] {
741             this.testClassesDirectory, includeList, excludeList, this.testSourceDirectory.getAbsolutePath(),
742             testNgArtifact.getBaseVersion(), testNgArtifact.getClassifier(), this.properties, this.reportsDirectory
743         });
744       } else {
745         String junitDirectoryTestSuite;
746         if (junitArtifact != null && junitArtifact.getBaseVersion() != null
747             && junitArtifact.getBaseVersion().startsWith("4")) {
748           junitDirectoryTestSuite = "org.apache.maven.surefire.junit4.JUnit4DirectoryTestSuite";
749         } else {
750           junitDirectoryTestSuite = "org.apache.maven.surefire.junit.JUnitDirectoryTestSuite";
751         }
752 
753         // fall back to JUnit, which also contains POJO support. Also it can run
754         // classes compiled against JUnit since it has a dependency on JUnit
755         // itself.
756         surefireBooter.addTestSuite(junitDirectoryTestSuite, new Object[] {
757             this.testClassesDirectory, includeList, excludeList
758         });
759       }
760     }
761 
762     // ----------------------------------------------------------------------
763     //
764     // ----------------------------------------------------------------------
765 
766     getLog().debug("Test Classpath :");
767 
768     // Check if we need to add configured classes/test classes directories here.
769     // If they are configured, we should remove the default to avoid conflicts.
770     if (!this.project.getBuild().getOutputDirectory().equals(this.classesDirectory.getAbsolutePath())) {
771       this.classpathElements.remove(this.project.getBuild().getOutputDirectory());
772       this.classpathElements.add(this.classesDirectory.getAbsolutePath());
773     }
774     if (!this.project.getBuild().getTestOutputDirectory().equals(this.testClassesDirectory.getAbsolutePath())) {
775       this.classpathElements.remove(this.project.getBuild().getTestOutputDirectory());
776       this.classpathElements.add(this.testClassesDirectory.getAbsolutePath());
777     }
778 
779     for (final Object classpathElement1 : this.classpathElements) {
780       final String classpathElement = (String) classpathElement1;
781 
782       getLog().debug("  " + classpathElement);
783 
784       surefireBooter.addClassPathUrl(classpathElement);
785     }
786 
787     final Toolchain tc = getToolchain();
788 
789     if (tc != null) {
790       getLog().info("Toolchain in surefire-plugin: " + tc);
791       if (ForkConfiguration.FORK_NEVER.equals(this.forkMode)) {
792         this.forkMode = ForkConfiguration.FORK_ONCE;
793       }
794       if (this.jvm != null) {
795         getLog().warn("Toolchains are ignored, 'executable' parameter is set to " + this.jvm);
796       } else {
797         this.jvm = tc.findTool("java"); // NOI18N
798       }
799     }
800 
801     if (this.additionalClasspathElements != null) {
802       for (final Object additionalClasspathElement : this.additionalClasspathElements) {
803         final String classpathElement = (String) additionalClasspathElement;
804 
805         getLog().debug("  " + classpathElement);
806 
807         surefireBooter.addClassPathUrl(classpathElement);
808       }
809     }
810 
811     // ----------------------------------------------------------------------
812     // Forking
813     // ----------------------------------------------------------------------
814 
815     final ForkConfiguration fork = new ForkConfiguration();
816 
817     // DUNS
818     if (this.project.getPackaging().equals("nar") || getNarArtifacts().size() > 0) {
819       this.forkMode = "pertest";
820     }
821 
822     fork.setForkMode(this.forkMode);
823 
824     processSystemProperties(!fork.isForking());
825 
826     if (getLog().isDebugEnabled()) {
827       showMap(this.systemProperties, "system property");
828     }
829 
830     if (fork.isForking()) {
831       this.useSystemClassLoader = this.useSystemClassLoader == null ? Boolean.TRUE : this.useSystemClassLoader;
832       fork.setUseSystemClassLoader(this.useSystemClassLoader.booleanValue());
833       fork.setUseManifestOnlyJar(this.useManifestOnlyJar);
834 
835       fork.setSystemProperties(this.systemProperties);
836 
837       if ("true".equals(this.debugForkedProcess)) {
838         this.debugForkedProcess = "-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005";
839       }
840 
841       fork.setDebugLine(this.debugForkedProcess);
842 
843       if (this.jvm == null || "".equals(this.jvm)) {
844         // use the same JVM as the one used to run Maven (the "java.home" one)
845         this.jvm = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
846         getLog().debug("Using JVM: " + this.jvm);
847       }
848 
849       fork.setJvmExecutable(this.jvm);
850 
851       if (this.workingDirectory != null) {
852         fork.setWorkingDirectory(this.workingDirectory);
853       } else {
854         fork.setWorkingDirectory(this.basedir);
855       }
856 
857       // BEGINDUNS
858       if (this.argLine == null) {
859         this.argLine = "";
860       }
861 
862       final StringBuffer javaLibraryPath = new StringBuffer();
863       if (testJNIModule()) {
864         // Add libraries to java.library.path for testing
865         final File jniLibraryPathEntry = getLayout().getLibDirectory(getTargetDirectory(),
866             getMavenProject().getArtifactId(), getMavenProject().getVersion(), getAOL().toString(), Library.JNI);
867         if (jniLibraryPathEntry.exists()) {
868           getLog().debug("Adding library directory to java.library.path: " + jniLibraryPathEntry);
869           if (javaLibraryPath.length() > 0) {
870             javaLibraryPath.append(File.pathSeparator);
871           }
872           javaLibraryPath.append(jniLibraryPathEntry);
873         }
874 
875         final File sharedLibraryPathEntry = getLayout().getLibDirectory(getTargetDirectory(),
876             getMavenProject().getArtifactId(), getMavenProject().getVersion(), getAOL().toString(), Library.SHARED);
877         if (sharedLibraryPathEntry.exists()) {
878           getLog().debug("Adding library directory to java.library.path: " + sharedLibraryPathEntry);
879           if (javaLibraryPath.length() > 0) {
880             javaLibraryPath.append(File.pathSeparator);
881           }
882           javaLibraryPath.append(sharedLibraryPathEntry);
883         }
884 
885         // add jar file to classpath, as one may want to read a
886         // properties file for artifactId and version
887         final String narFile = "target/" + this.project.getArtifactId() + "-" + this.project.getVersion() + ".jar";
888         getLog().debug("Adding to surefire test classpath: " + narFile);
889         surefireBooter.addClassPathUrl(narFile);
890       }
891 
892       final List dependencies = getNarArtifacts(); // TODO: get seems heavy, not
893                                                    // sure if we can push this
894                                                    // up to before the fork to
895                                                    // use it multiple times.
896       for (final Object dependency1 : dependencies) {
897         final NarArtifact dependency = (NarArtifact) dependency1;
898         // FIXME this should be overridable
899         // NarInfo info = dependency.getNarInfo();
900         // String binding = info.getBinding(getAOL(), Library.STATIC);
901         // NOTE: fixed to shared, jni
902         final String[] bindings = { Library.SHARED, Library.JNI
903         };
904         for (final String binding2 : bindings) {
905           final String binding = binding2;
906           if (!binding.equals(Library.STATIC)) {
907             final File depLibPathEntry = getLayout()
908                 .getLibDirectory(getUnpackDirectory(), dependency.getArtifactId(), dependency.getBaseVersion(),
909                     getAOL().toString(), binding);
910             //dependency.getVersion() calls the maven super class, which is not used when
911             //unpacking the NarDependencies in AbstractDependencyMojo.  This causes
912             //the path to not exist and not be added to the library path.
913             if (depLibPathEntry.exists()) {
914               getLog().debug("Adding dependency directory to java.library.path: " + depLibPathEntry);
915               if (javaLibraryPath.length() > 0) {
916                 javaLibraryPath.append(File.pathSeparator);
917               }
918               javaLibraryPath.append(depLibPathEntry);
919             }
920           }
921         }
922       }
923 
924       // add final javalibrary path
925       if (javaLibraryPath.length() > 0) {
926         // NOTE java.library.path only works for the jni lib itself, and
927         // not for its dependent shareables.
928         // NOTE: java.library.path does not work with arguments with
929         // spaces as
930         // SureFireBooter splits the line in parts and then quotes
931         // it wrongly
932         NarUtil.addLibraryPathToEnv(javaLibraryPath.toString(), this.environmentVariables, getOS());
933       }
934 
935       // necessary to find WinSxS
936       if (getOS().equals(OS.WINDOWS)) {
937         this.environmentVariables.put("SystemRoot", NarUtil.getEnv("SystemRoot", "SystemRoot", "C:\\Windows"));
938       }
939       // ENDDUNS
940 
941       fork.setArgLine(this.argLine);
942 
943       fork.setEnvironmentVariables(this.environmentVariables);
944 
945       if (getLog().isDebugEnabled()) {
946         showMap(this.environmentVariables, "environment variable");
947 
948         fork.setDebug(true);
949       }
950 
951       if (this.argLine != null) {
952         final List args = Arrays.asList(this.argLine.split(" "));
953         if (args.contains("-da") || args.contains("-disableassertions")) {
954           this.enableAssertions = false;
955         }
956       }
957     }
958 
959     surefireBooter.setFailIfNoTests(this.failIfNoTests == null ? false : this.failIfNoTests.booleanValue());
960 
961     surefireBooter.setForkedProcessTimeoutInSeconds(this.forkedProcessTimeoutInSeconds);
962 
963     surefireBooter.setRedirectTestOutputToFile(this.redirectTestOutputToFile);
964 
965     surefireBooter.setForkConfiguration(fork);
966 
967     surefireBooter.setChildDelegation(this.childDelegation);
968 
969     surefireBooter.setEnableAssertions(this.enableAssertions);
970 
971     surefireBooter.setReportsDirectory(this.reportsDirectory);
972 
973     addReporters(surefireBooter, fork.isForking());
974 
975     return surefireBooter;
976   }
977 
978   /**
979    * Converts old TestNG configuration parameters over to new properties based
980    * configuration method. (if any are
981    * defined the old way)
982    */
983   private void convertTestNGParameters() {
984     if (this.properties == null) {
985       this.properties = new Properties();
986     }
987 
988     if (this.parallel != null) {
989       this.properties.setProperty("parallel", this.parallel);
990     }
991     if (this.excludedGroups != null) {
992       this.properties.setProperty("excludegroups", this.excludedGroups);
993     }
994     if (this.groups != null) {
995       this.properties.setProperty("groups", this.groups);
996     }
997 
998     if (this.threadCount > 0) {
999       this.properties.setProperty("threadcount", Integer.toString(this.threadCount));
1000     }
1001   }
1002 
1003   /**
1004    * List the dependencies needed for integration tests executions, those
1005    * dependencies are used to declare the paths
1006    * of shared and jni libraries in java.library.path
1007    */
1008   @Override
1009   protected ScopeFilter getArtifactScopeFilter() {
1010     return new ScopeFilter( Artifact.SCOPE_TEST, null );
1011   }
1012 
1013   // TODO remove the part with ToolchainManager lookup once we depend on
1014   // 3.0.9 (have it as prerequisite). Define as regular component field then.
1015   private Toolchain getToolchain() {
1016     Toolchain tc = null;
1017     try {
1018       if (this.session != null) // session is null in tests..
1019       {
1020         final ToolchainManager toolchainManager = (ToolchainManager) this.session.getContainer().lookup(
1021             ToolchainManager.ROLE);
1022         if (toolchainManager != null) {
1023           tc = toolchainManager.getToolchainFromBuildContext("jdk", this.session);
1024         }
1025       }
1026     } catch (final ComponentLookupException componentLookupException) {
1027       // just ignore, could happen in pre-3.0.9 builds..
1028     }
1029     return tc;
1030   }
1031 
1032   @Override
1033   protected File getUnpackDirectory() {
1034     return getTestUnpackDirectory() == null ? super.getUnpackDirectory() : getTestUnpackDirectory();
1035   }
1036 
1037   /**
1038    * @return SurefirePlugin Returns the skipExec.
1039    */
1040   public boolean isSkipExec() {
1041     return this.skipNarTests;
1042   }
1043 
1044   // DUNS, changed name
1045   @Override
1046   public void narExecute() throws MojoExecutionException, MojoFailureException {
1047     if (this.skipTests) {
1048       getLog().info("Tests are skipped");
1049     } else if (verifyParameters()) {
1050 
1051       final SurefireBooter surefireBooter = constructSurefireBooter();
1052 
1053       getLog().info("Surefire report directory: " + this.reportsDirectory);
1054 
1055       int result;
1056       try {
1057         result = surefireBooter.run();
1058       } catch (final SurefireBooterForkException | SurefireExecutionException e) {
1059         throw new MojoExecutionException(e.getMessage(), e);
1060       }
1061 
1062       if (this.originalSystemProperties != null && !surefireBooter.isForking()) {
1063         // restore system properties, only makes sense when not forking..
1064         System.setProperties(this.originalSystemProperties);
1065       }
1066 
1067       if (result == 0) {
1068         return;
1069       }
1070 
1071       String msg;
1072 
1073       if (result == SurefireBooter.NO_TESTS_EXIT_CODE) {
1074         if (this.failIfNoTests == null || !this.failIfNoTests.booleanValue()) {
1075           return;
1076         }
1077         // TODO: i18n
1078         throw new MojoFailureException("No tests were executed!  (Set -DfailIfNoTests=false to ignore this error.)");
1079       } else {
1080         // TODO: i18n
1081         msg = "There are test failures.\n\nPlease refer to " + this.reportsDirectory
1082             + " for the individual test results.";
1083 
1084       }
1085 
1086       if (this.testFailureIgnore) {
1087         getLog().error(msg);
1088       } else {
1089         throw new MojoFailureException(msg);
1090       }
1091     }
1092   }
1093 
1094   protected void processSystemProperties(final boolean setInSystem) {
1095     if (this.systemProperties == null) {
1096       this.systemProperties = new Properties();
1097     }
1098 
1099     this.originalSystemProperties = (Properties) System.getProperties().clone();
1100 
1101     // We used to take all of our system properties and dump them in with the
1102     // user specified properties for SUREFIRE-121, causing SUREFIRE-491.
1103     // Not gonna do THAT any more... but I'm leaving this code here in case
1104     // we need it later when we try to fix SUREFIRE-121 again.
1105 
1106     // Get the properties from the MavenSession instance to make embedded use
1107     // work correctly
1108     final Properties userSpecifiedProperties = (Properties) this.session.getExecutionProperties().clone();
1109     userSpecifiedProperties.putAll(this.systemProperties);
1110     // systemProperties = userSpecifiedProperties;
1111 
1112     this.systemProperties.setProperty("basedir", this.basedir.getAbsolutePath());
1113     this.systemProperties.setProperty("user.dir", this.workingDirectory.getAbsolutePath());
1114 
1115     // DUNS, use access method rather than "localRepository" field.
1116     this.systemProperties.setProperty("localRepository", getLocalRepository().getBasedir());
1117 
1118     if (setInSystem) {
1119       // Add all system properties configured by the user
1120 
1121       for (final Object o : this.systemProperties.keySet()) {
1122         final String key = (String) o;
1123 
1124         final String value = this.systemProperties.getProperty(key);
1125 
1126         System.setProperty(key, value);
1127       }
1128     }
1129   }
1130 
1131   private ArtifactResolutionResult resolveArtifact(final Artifact filteredArtifact, final Artifact providerArtifact)
1132       throws ArtifactResolutionException, ArtifactNotFoundException {
1133     ArtifactFilter filter = null;
1134     if (filteredArtifact != null) {
1135       filter = new ExcludesArtifactFilter(Collections.singletonList(filteredArtifact.getGroupId() + ":"
1136           + filteredArtifact.getArtifactId()));
1137     }
1138 
1139     final Artifact originatingArtifact = this.artifactFactory.createBuildArtifact("dummy", "dummy", "1.0", "jar");
1140 
1141     // DUNS, use access method rather than "localRepository" field.
1142     return this.artifactResolver.resolveTransitively(Collections.singleton(providerArtifact), originatingArtifact,
1143         getLocalRepository(), getRemoteRepositories(), this.metadataSource, filter);
1144   }
1145 
1146   /**
1147    * @param skipExec
1148    *          the skipExec to set
1149    */
1150   public void setSkipExec(final boolean skipExec) {
1151     this.skipNarTests = skipExec;
1152   }
1153 
1154   private void showMap(final Map map, final String setting) {
1155     for (final Object o : map.keySet()) {
1156       final String key = (String) o;
1157       final String value = (String) map.get(key);
1158       getLog().debug("Setting " + setting + " [" + key + "]=[" + value + "]");
1159     }
1160   }
1161 
1162   // DUNS added test for JNI module
1163   private boolean testJNIModule() {
1164     for (final Object element : getLibraries()) {
1165       final Library lib = (Library) element;
1166       final String type = lib.getType();
1167       if (type.equals(Library.JNI) || type.equals(Library.SHARED)) {
1168         return true;
1169       }
1170     }
1171     return false;
1172   }
1173 
1174   private boolean verifyParameters() throws MojoFailureException {
1175     // DUNS
1176     if (this.skipNar || this.skipNarTests || this.skipNarExec) {
1177       getLog().info("Tests are skipped.");
1178       return false;
1179     }
1180 
1181     if (!this.testClassesDirectory.exists()) {
1182       if (this.failIfNoTests != null && this.failIfNoTests.booleanValue()) {
1183         throw new MojoFailureException("No tests to run!");
1184       }
1185       getLog().info("No tests to run.");
1186       return false;
1187     }
1188 
1189     if (this.useSystemClassLoader != null && ForkConfiguration.FORK_NEVER.equals(this.forkMode)) {
1190       getLog().warn("useSystemClassloader setting has no effect when not forking");
1191     }
1192 
1193     return true;
1194   }
1195 }