You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ol...@apache.org on 2019/07/18 01:40:31 UTC

[maven-invoker-plugin] branch master updated: [MINVOKER-196] Support for JUnit report style (#3)

This is an automated email from the ASF dual-hosted git repository.

olamy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-invoker-plugin.git


The following commit(s) were added to refs/heads/master by this push:
     new 1ab4140  [MINVOKER-196] Support for JUnit report style (#3)
1ab4140 is described below

commit 1ab41403146694d3b2706c7e0f6f39b2f89866ad
Author: Olivier Lamy <ol...@apache.org>
AuthorDate: Thu Jul 18 11:40:26 2019 +1000

    [MINVOKER-196] Support for JUnit report style (#3)
    
    * [MINVOKER-196] Support for JUnit report style
    
    Signed-off-by: olivier lamy <ol...@apache.org>
---
 pom.xml                                            |   5 +-
 src/it/MINVOKER-191/pom.xml                        |   1 +
 .../pom.xml                                        |  25 ++--
 .../src/it/project}/pom.xml                        |  41 +------
 .../src/it/project/verify.groovy                   |  42 +++++++
 .../src/it/project_2/pom.xml                       |  71 +++++++++++
 .../src/it/project_2/verify.groovy                 |  20 ++++
 .../src/it/settings.xml                            |  31 +++++
 .../MINVOKER-196_junit_report_file/verify.groovy   |  43 +++++++
 src/it/fail-build/pom.xml                          |   1 +
 src/it/pom-filtering-reactor/pom.xml               |   1 +
 src/it/script-errors/pom.xml                       |   1 +
 src/it/skip-run/pom.xml                            |   1 +
 .../maven/plugins/invoker/AbstractInvokerMojo.java | 130 ++++++++++++++++++---
 14 files changed, 348 insertions(+), 65 deletions(-)

diff --git a/pom.xml b/pom.xml
index 334d206..e20cf72 100644
--- a/pom.xml
+++ b/pom.xml
@@ -315,7 +315,7 @@ under the License.
             <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-invoker-plugin</artifactId>
-              <version>3.1.0</version>
+              <version>3.2.0</version>
               <configuration>
                 <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
                 <preBuildHookScript>setup</preBuildHookScript>
@@ -371,6 +371,9 @@ under the License.
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-invoker-plugin</artifactId>
               <version>${project.version}</version>
+              <configuration>
+                <writeJunitReport>true</writeJunitReport>
+              </configuration>
             </plugin>
           </plugins>
         </pluginManagement>
diff --git a/src/it/MINVOKER-191/pom.xml b/src/it/MINVOKER-191/pom.xml
index 2e8a898..e556e96 100644
--- a/src/it/MINVOKER-191/pom.xml
+++ b/src/it/MINVOKER-191/pom.xml
@@ -66,6 +66,7 @@ under the License.
           <artifactId>maven-invoker-plugin</artifactId>
           <version>@project.version@</version>
           <configuration>
+            <writeJunitReport>true</writeJunitReport>
             <properties>
               <!-- e.g. ensure that Java7 picks up TLSv1.2 when connecting with Central -->
               <https.protocols>${https.protocols}</https.protocols>
diff --git a/src/it/script-errors/pom.xml b/src/it/MINVOKER-196_junit_report_file/pom.xml
similarity index 74%
copy from src/it/script-errors/pom.xml
copy to src/it/MINVOKER-196_junit_report_file/pom.xml
index 124bd90..df2b064 100644
--- a/src/it/script-errors/pom.xml
+++ b/src/it/MINVOKER-196_junit_report_file/pom.xml
@@ -23,13 +23,12 @@ under the License.
   <modelVersion>4.0.0</modelVersion>
 
   <groupId>org.apache.maven.plugins.invoker</groupId>
-  <artifactId>script-errors</artifactId>
+  <artifactId>junit-report-file</artifactId>
   <version>1.0-SNAPSHOT</version>
-  <packaging>pom</packaging>
+  <packaging>jar</packaging>
 
   <description>
-    Test to check that any kind of error from the script, i.e. both java.lang.Exception and java.lang.Error,
-    is caught by the interpreter facade and does not go up to the main build (MINVOKER-78).
+    Test to check for proper generation of junit report files
   </description>
 
   <properties>
@@ -39,20 +38,24 @@ under the License.
   <build>
     <plugins>
       <plugin>
+        <!-- This triggers the download of a known version of this plugin which can then be safely invoked by the IT project -->
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-resources-plugin</artifactId>
+        <version>2.2</version>
+      </plugin>
+      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-invoker-plugin</artifactId>
-        <version>@project.version@</version>
+        <version>@pom.version@</version>
         <configuration>
-          <cloneProjectsTo>${project.build.directory}/its</cloneProjectsTo>
-          <pomIncludes>
-            <pomInclude>*/pom.xml</pomInclude>
-          </pomIncludes>
-          <preBuildHookScript>setup</preBuildHookScript>
+          <ignoreFailures>true</ignoreFailures>
+          <writeJunitReport>true</writeJunitReport>
           <postBuildHookScript>verify</postBuildHookScript>
+          <localRepositoryPath>${project.build.directory}/it-repo</localRepositoryPath>
+          <settingsFile>src/it/settings.xml</settingsFile>
           <goals>
             <goal>validate</goal>
           </goals>
-          <ignoreFailures>true</ignoreFailures>
         </configuration>
         <executions>
           <execution>
diff --git a/src/it/skip-run/pom.xml b/src/it/MINVOKER-196_junit_report_file/src/it/project/pom.xml
similarity index 52%
copy from src/it/skip-run/pom.xml
copy to src/it/MINVOKER-196_junit_report_file/src/it/project/pom.xml
index 1c25b73..0d15cde 100644
--- a/src/it/skip-run/pom.xml
+++ b/src/it/MINVOKER-196_junit_report_file/src/it/project/pom.xml
@@ -21,45 +21,12 @@ under the License.
 
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
-
-  <groupId>org.apache.maven.plugins.invoker</groupId>
-  <artifactId>skip-run</artifactId>
-  <version>1.0-SNAPSHOT</version>
-  <packaging>pom</packaging>
-
-  <description>Test to check that invoker.skip=true will skip all invocations</description>
+  <groupId>test</groupId>
+  <artifactId>test-junit-report</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  <packaging>jar</packaging>
 
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   </properties>
-
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-invoker-plugin</artifactId>
-        <version>@project.version@</version>
-        <configuration>
-          <debug>true</debug>
-          <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
-          <pomIncludes>
-            <pomInclude>*/pom.xml</pomInclude>
-          </pomIncludes>
-          <goals>
-            <goal>validate</goal>
-          </goals>
-        </configuration>
-        <executions>
-          <execution>
-            <id>integration-test</id>
-            <phase>initialize</phase>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
-
 </project>
diff --git a/src/it/MINVOKER-196_junit_report_file/src/it/project/verify.groovy b/src/it/MINVOKER-196_junit_report_file/src/it/project/verify.groovy
new file mode 100644
index 0000000..6b8ec74
--- /dev/null
+++ b/src/it/MINVOKER-196_junit_report_file/src/it/project/verify.groovy
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// ensure script context contains localRepositoryPath
+assert new File( basedir, "../../../target/it-repo" ).canonicalFile.equals( localRepositoryPath )
+
+File interpolatedSettings = new File( basedir, "../interpolated-settings.xml" )
+assert interpolatedSettings.isFile()
+
+def filename = new File( basedir, "../../../../../local-repo" ).canonicalPath
+// Convert URL, see org.apache.maven.plugins.invoker.AbstractInvokerMojo.toUrl(String)
+String url = "file://" + new File(  filename  ).toURI().path
+if ( url.endsWith( "/" ) )
+{
+    url = url.substring( 0, url.length() - 1 )
+}
+
+def settings = new XmlSlurper().parse( interpolatedSettings )
+
+// ensure right settings and mirror are picked up
+def sandboxMirror = settings.mirrors.mirror[0]
+assert sandboxMirror.id.text() == "sandbox"
+assert sandboxMirror.url.text() != "@localRepositoryUrl@"
+
+// sandboxMirror.url is NOT filled with localRepositoryPath, but with the localRepository of the parent Settings
+assert sandboxMirror.url.text() == url
diff --git a/src/it/MINVOKER-196_junit_report_file/src/it/project_2/pom.xml b/src/it/MINVOKER-196_junit_report_file/src/it/project_2/pom.xml
new file mode 100644
index 0000000..7234f65
--- /dev/null
+++ b/src/it/MINVOKER-196_junit_report_file/src/it/project_2/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>test</groupId>
+  <artifactId>pom-filtering-junit-report</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  <packaging>jar</packaging>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <!-- ${...} must be left unfiltered -->
+    <prop0>${project.version}</prop0>
+    <!-- these must be filtered -->
+    <prop1>@project.version@</prop1>
+    <prop2>@propertyFromPluginConfig@</prop2>
+    <prop3>@propertyFromPropertiesSection@</prop3>
+    <!-- this must not be resolved to project.version, i.e. the prefix matters -->
+    <prop4>@project-is-not-the-pom.version@</prop4>
+    <!-- POM values must precede other properties -->
+    <prop5>@project.name@</prop5>
+    <prop6>@pom.name@</prop6>
+    <!-- properties from the plugin config must precede values from the POM's <properties> section -->
+    <prop7>@itProperty@</prop7>
+    <!-- properties with prefix "project"/"pom" must fallback to plugin config if no matching POM value exists -->
+    <prop8>@project.nonExistingPomValue@</prop8>
+    <!-- built-in properties must not be overridden by properties from the plugin configuration -->
+    <prop9>@basedir@</prop9>
+    <prop10>@baseurl@</prop10>
+    <prop11>@localRepository@</prop11>
+    <prop12>@localRepositoryUrl@</prop12>
+  </properties>
+
+  <build>
+    <!-- using the Resources Plugin to create a backup of the executed POM for later inspection by the parent build -->
+    <resources>
+      <resource>
+        <directory>${basedir}</directory>
+        <filtering>false</filtering>
+        <includes>
+          <include>*.xml</include>
+        </includes>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <artifactId>maven-resources-plugin</artifactId>
+        <version>2.2</version>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/src/it/MINVOKER-196_junit_report_file/src/it/project_2/verify.groovy b/src/it/MINVOKER-196_junit_report_file/src/it/project_2/verify.groovy
new file mode 100644
index 0000000..af6082a
--- /dev/null
+++ b/src/it/MINVOKER-196_junit_report_file/src/it/project_2/verify.groovy
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+assert new File( basedir, "../../../target/it-repo_not_here" ).canonicalFile.equals( localRepositoryPath )
diff --git a/src/it/MINVOKER-196_junit_report_file/src/it/settings.xml b/src/it/MINVOKER-196_junit_report_file/src/it/settings.xml
new file mode 100644
index 0000000..ca7b8ab
--- /dev/null
+++ b/src/it/MINVOKER-196_junit_report_file/src/it/settings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<settings>
+  <mirrors>
+    <mirror>
+      <id>sandbox</id>
+      <name>A completely isolated repo to test the interpolation of "localRepositoryUrl"</name>
+      <url>@localRepositoryUrl@</url>
+      <mirrorOf>*</mirrorOf>
+    </mirror>
+  </mirrors>
+</settings>
diff --git a/src/it/MINVOKER-196_junit_report_file/verify.groovy b/src/it/MINVOKER-196_junit_report_file/verify.groovy
new file mode 100644
index 0000000..5a97c50
--- /dev/null
+++ b/src/it/MINVOKER-196_junit_report_file/verify.groovy
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+File buildLog = new File( basedir, 'build.log' )
+assert buildLog.text.contains( '[INFO] run post-build script verify.groovy' )
+
+File invokerReports = new File( new File(basedir, "target"), 'invoker-reports' )
+assert buildLog.exists()
+
+// test on first project
+def testsuite = new XmlSlurper().parse( new File( invokerReports, "TEST-project.xml" ) )
+
+assert testsuite.@tests.text() == "1"
+
+assert testsuite.testcase.@name.text() == "project"
+def systemOut = testsuite.testcase.'**'.findAll { node -> node.name() == 'system-out' }.get(0)
+assert !systemOut.text().isEmpty()
+
+
+// test on second project
+testsuite = new XmlSlurper().parse( new File( invokerReports, "TEST-project_2.xml" ) )
+
+assert testsuite.@tests.text() == "1"
+assert testsuite.@failures.text() == "1"
+
+assert testsuite.testcase.@name.text() == "project_2"
+def failureMessage = testsuite.testcase.failure.@message
+assert !failureMessage.text().isEmpty()
diff --git a/src/it/fail-build/pom.xml b/src/it/fail-build/pom.xml
index 15c63b5..10ec51d 100644
--- a/src/it/fail-build/pom.xml
+++ b/src/it/fail-build/pom.xml
@@ -40,6 +40,7 @@ under the License.
         <artifactId>maven-invoker-plugin</artifactId>
         <version>@pom.version@</version>
         <configuration>
+          <writeJunitReport>true</writeJunitReport>
           <debug>true</debug>
           <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
           <pomIncludes>
diff --git a/src/it/pom-filtering-reactor/pom.xml b/src/it/pom-filtering-reactor/pom.xml
index 1911edd..79dac5f 100644
--- a/src/it/pom-filtering-reactor/pom.xml
+++ b/src/it/pom-filtering-reactor/pom.xml
@@ -40,6 +40,7 @@ under the License.
         <artifactId>maven-invoker-plugin</artifactId>
         <version>@pom.version@</version>
         <configuration>
+          <writeJunitReport>true</writeJunitReport>
           <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
           <pomIncludes>
             <pomInclude>*/pom.xml</pomInclude>
diff --git a/src/it/script-errors/pom.xml b/src/it/script-errors/pom.xml
index 124bd90..3d600d4 100644
--- a/src/it/script-errors/pom.xml
+++ b/src/it/script-errors/pom.xml
@@ -43,6 +43,7 @@ under the License.
         <artifactId>maven-invoker-plugin</artifactId>
         <version>@project.version@</version>
         <configuration>
+          <writeJunitReport>true</writeJunitReport>
           <cloneProjectsTo>${project.build.directory}/its</cloneProjectsTo>
           <pomIncludes>
             <pomInclude>*/pom.xml</pomInclude>
diff --git a/src/it/skip-run/pom.xml b/src/it/skip-run/pom.xml
index 1c25b73..c09f180 100644
--- a/src/it/skip-run/pom.xml
+++ b/src/it/skip-run/pom.xml
@@ -40,6 +40,7 @@ under the License.
         <artifactId>maven-invoker-plugin</artifactId>
         <version>@project.version@</version>
         <configuration>
+          <writeJunitReport>true</writeJunitReport>
           <debug>true</debug>
           <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
           <pomIncludes>
diff --git a/src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java b/src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java
index bf2f618..e46dab3 100644
--- a/src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java
+++ b/src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java
@@ -70,6 +70,8 @@ import org.codehaus.plexus.util.cli.CommandLineException;
 import org.codehaus.plexus.util.cli.CommandLineUtils;
 import org.codehaus.plexus.util.cli.Commandline;
 import org.codehaus.plexus.util.cli.StreamConsumer;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.codehaus.plexus.util.xml.Xpp3DomWriter;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
@@ -626,6 +628,20 @@ public abstract class AbstractInvokerMojo
     private int timeoutInSeconds;
 
     /**
+     * Write test result in junit format.
+     * @since 3.1.2
+     */
+    @Parameter( defaultValue = "false", property = "invoker.writeJunitReport" )
+    private boolean writeJunitReport;
+
+    /**
+     * The package name use in junit report
+     * @since 3.1.2
+     */
+    @Parameter( defaultValue = "maven.invoker.it", property = "invoker.junitPackageName" )
+    private String junitPackageName = "maven.invoker.it";
+
+    /**
      * The scripter runner that is responsible to execute hook scripts.
      */
     private ScriptRunner scriptRunner;
@@ -803,11 +819,11 @@ public abstract class AbstractInvokerMojo
     private BuildJob[] getNonSetupJobs( BuildJob[] buildJobs )
     {
         List<BuildJob> result = new LinkedList<>();
-        for ( int i = 0; i < buildJobs.length; i++ )
+        for ( BuildJob buildJob : buildJobs )
         {
-            if ( !buildJobs[i].getType().equals( BuildJob.Type.SETUP ) )
+            if ( !buildJob.getType().equals( BuildJob.Type.SETUP ) )
             {
-                result.add( buildJobs[i] );
+                result.add( buildJob );
             }
         }
         BuildJob[] buildNonSetupJobs = result.toArray( new BuildJob[result.size()] );
@@ -850,9 +866,8 @@ public abstract class AbstractInvokerMojo
 
         try ( Writer writer = new BufferedWriter( new FileWriter( summaryReportFile ) ) )
         {
-            for ( int i = 0; i < buildJobs.length; i++ )
+            for ( BuildJob buildJob : buildJobs )
             {
-                BuildJob buildJob = buildJobs[i];
                 if ( !buildJob.getResult().equals( BuildJob.Result.SUCCESS ) )
                 {
                     writer.append( buildJob.getResult() );
@@ -975,7 +990,7 @@ public abstract class AbstractInvokerMojo
                 collectProjects( projectsDir, parent, projectPaths, false );
             }
 
-            Collection<String> modulePaths = new LinkedHashSet<String>();
+            Collection<String> modulePaths = new LinkedHashSet<>();
 
             modulePaths.addAll( model.getModules() );
 
@@ -1634,6 +1649,7 @@ public abstract class AbstractInvokerMojo
         // let's set what details we can
         buildJob.setName( invokerProperties.getJobName() );
         buildJob.setDescription( invokerProperties.getJobDescription() );
+        ExecutionResult executionResult = null;
 
         try
         {
@@ -1645,9 +1661,10 @@ public abstract class AbstractInvokerMojo
                 try
                 {
                     // CHECKSTYLE_OFF: LineLength
-                    executed =
+                    executionResult =
                         runBuild( basedir, interpolatedPomFile, settingsFile, actualJavaHome, invokerProperties );
                     // CHECKSTYLE_ON: LineLength
+                    executed = executionResult.executed;
                 }
                 finally
                 {
@@ -1752,7 +1769,7 @@ public abstract class AbstractInvokerMojo
         finally
         {
             deleteInterpolatedPomFile( interpolatedPomFile );
-            writeBuildReport( buildJob );
+            writeBuildReport( buildJob, executionResult );
         }
     }
 
@@ -1819,7 +1836,7 @@ public abstract class AbstractInvokerMojo
      * @param buildJob The build job whose report should be written, must not be <code>null</code>.
      * @throws org.apache.maven.plugin.MojoExecutionException If the report could not be written.
      */
-    private void writeBuildReport( BuildJob buildJob )
+    private void writeBuildReport( BuildJob buildJob, ExecutionResult executionResult )
         throws MojoExecutionException
     {
         if ( disableReports )
@@ -1845,6 +1862,73 @@ public abstract class AbstractInvokerMojo
         {
             throw new MojoExecutionException( "Failed to write build report " + reportFile, e );
         }
+
+        if ( writeJunitReport )
+        {
+            writeJunitReport( buildJob, safeFileName, executionResult );
+        }
+    }
+
+    private void writeJunitReport( BuildJob buildJob, String safeFileName, ExecutionResult executionResult )
+        throws MojoExecutionException
+    {
+        File reportFile = new File( reportsDirectory, "TEST-" + safeFileName + ".xml" );
+        Xpp3Dom testsuite = new Xpp3Dom( "testsuite" );
+        testsuite.setAttribute( "tests", "1" );
+        testsuite.setAttribute( "time", Double.toString( buildJob.getTime() ) );
+        Xpp3Dom testcase = new Xpp3Dom( "testcase" );
+        testsuite.addChild( testcase );
+        switch ( buildJob.getResult() )
+        {
+            case BuildJob.Result.SUCCESS:
+                break;
+            case BuildJob.Result.SKIPPED:
+                testsuite.setAttribute( "skipped", "1" );
+                // adding the failure element
+                Xpp3Dom skipped = new Xpp3Dom( "skipped" );
+                testcase.addChild( skipped );
+                skipped.setValue( buildJob.getFailureMessage() );
+                break;
+            case BuildJob.Result.ERROR:
+                testsuite.setAttribute( "errors", "1" );
+                break;
+            default:
+                testsuite.setAttribute( "failures", "1" );
+                // adding the failure element
+                Xpp3Dom failure = new Xpp3Dom( "failure" );
+                testcase.addChild( failure );
+                failure.setAttribute( "message", buildJob.getFailureMessage() );
+        }
+        testcase.setAttribute( "classname", junitPackageName + "." + safeFileName );
+        testcase.setAttribute( "name", safeFileName );
+        Xpp3Dom systemOut = new Xpp3Dom( "system-out" );
+        testcase.addChild( systemOut );
+
+        if ( executionResult != null && executionResult.fileLogger != null )
+        {
+            getLog().info( "fileLogger:" + executionResult.fileLogger.getOutputFile() );
+            try
+            {
+                systemOut.setValue( FileUtils.fileRead( executionResult.fileLogger.getOutputFile() ) );
+            }
+            catch ( IOException e )
+            {
+                throw new MojoExecutionException( "Failed to read logfile " + executionResult.fileLogger.getOutputFile()
+                    , e );
+            }
+        }
+        else
+        {
+            getLog().info( safeFileName + ", executionResult:" + executionResult );
+        }
+        try ( FileOutputStream fos = new FileOutputStream( reportFile );
+              Writer osw = new OutputStreamWriter( fos, buildJob.getModelEncoding() ) )
+        {
+            Xpp3DomWriter.write( osw, testsuite );
+        } catch ( IOException e )
+        {
+            throw new MojoExecutionException( "Failed to write JUnit build report " + reportFile, e );
+        }
     }
 
     /**
@@ -1872,7 +1956,7 @@ public abstract class AbstractInvokerMojo
      * @throws org.apache.maven.shared.scriptinterpreter.RunFailureException If either a hook script or the build itself
      *             failed.
      */
-    private boolean runBuild( File basedir, File pomFile, File settingsFile, File actualJavaHome,
+    private ExecutionResult runBuild( File basedir, File pomFile, File settingsFile, File actualJavaHome,
                               InvokerProperties invokerProperties )
         throws MojoExecutionException, RunFailureException
     {
@@ -1895,6 +1979,7 @@ public abstract class AbstractInvokerMojo
 
         FileLogger logger = setupBuildLogFile( basedir );
         boolean selectorResult = true;
+        ExecutionResult executionResult = new ExecutionResult();
         try
         {
             try
@@ -1910,7 +1995,8 @@ public abstract class AbstractInvokerMojo
             catch ( RunFailureException e )
             {
                 selectorResult = false;
-                return false;
+                executionResult.executed = false;
+                return executionResult;
             }
 
             scriptRunner.run( "pre-build script", basedir, preBuildHookScript, context, logger,
@@ -2010,11 +2096,10 @@ public abstract class AbstractInvokerMojo
                     }
                 }
 
-                InvocationResult result;
-
                 try
                 {
-                    result = invoker.execute( request );
+                    InvocationResult result = invoker.execute( request );
+                    verify( result, invocationIndex, invokerProperties, logger );
                 }
                 catch ( final MavenInvocationException e )
                 {
@@ -2022,7 +2107,6 @@ public abstract class AbstractInvokerMojo
                     throw new RunFailureException( "Maven invocation failed. " + e.getMessage(),
                                                    BuildJob.Result.FAILURE_BUILD );
                 }
-                verify( result, invocationIndex, invokerProperties, logger );
             }
         }
         catch ( IOException e )
@@ -2040,7 +2124,21 @@ public abstract class AbstractInvokerMojo
                 logger.close();
             }
         }
-        return true;
+        executionResult.executed = true;
+        executionResult. fileLogger = logger;
+        return executionResult;
+    }
+
+    private static class ExecutionResult
+    {
+        boolean executed;
+        FileLogger fileLogger;
+
+        @Override
+        public String toString()
+        {
+            return "ExecutionResult{" + "executed=" + executed + ", fileLogger=" + fileLogger + '}';
+        }
     }
 
     private void runPostBuildHook( File basedir, Map<String, Object> context, FileLogger logger )