You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ti...@apache.org on 2017/05/02 22:43:49 UTC

[2/2] maven-surefire git commit: [SUREFIRE-1364] Report XML should contain system properties of forked JVM

[SUREFIRE-1364] Report XML should contain system properties of forked JVM


Project: http://git-wip-us.apache.org/repos/asf/maven-surefire/repo
Commit: http://git-wip-us.apache.org/repos/asf/maven-surefire/commit/99471032
Tree: http://git-wip-us.apache.org/repos/asf/maven-surefire/tree/99471032
Diff: http://git-wip-us.apache.org/repos/asf/maven-surefire/diff/99471032

Branch: refs/heads/SUREFIRE-1364_2
Commit: 99471032efed8f7ba7e3867974c97863d0c1878d
Parents: b54e33e
Author: Tibor17 <ti...@lycos.com>
Authored: Wed May 3 00:43:31 2017 +0200
Committer: Tibor17 <ti...@lycos.com>
Committed: Wed May 3 00:43:31 2017 +0200

----------------------------------------------------------------------
 .../surefire/StartupReportConfiguration.java    |  19 +-
 .../booterclient/ForkConfiguration.java         |   2 +-
 .../surefire/booterclient/ForkStarter.java      |  14 +-
 .../booterclient/output/ForkClient.java         |  32 ++-
 .../surefire/report/StatelessXmlReporter.java   |  35 ++--
 .../surefire/report/TestSetRunListener.java     |   9 +-
 .../surefire/report/WrappedReportEntry.java     |  26 ++-
 .../plugin/surefire/util/ImmutableMap.java      | 133 ------------
 .../booterclient/ForkingRunListenerTest.java    |  35 ++--
 .../surefire/booterclient/MockReporter.java     |  12 +-
 .../report/StatelessXmlReporterTest.java        |   8 +-
 .../plugin/surefire/util/ImmutableMapTest.java  |  86 --------
 .../apache/maven/surefire/JUnit4SuiteTest.java  |   4 +-
 .../surefire/booter/ForkingRunListener.java     |  24 +--
 .../surefire/report/CategorizedReportEntry.java |  20 +-
 .../maven/surefire/report/RunListener.java      |   4 +-
 .../surefire/report/SimpleReportEntry.java      |  36 +++-
 .../surefire/report/TestSetReportEntry.java     |  35 ++++
 .../surefire/util/internal/ImmutableMap.java    | 134 ++++++++++++
 .../surefire/util/internal/ObjectUtils.java     |   2 +-
 .../java/org/apache/maven/JUnit4SuiteTest.java  |   4 +-
 .../util/internal/ImmutableMapTest.java         |  86 ++++++++
 .../jiras/Surefire1364SystemPropertiesIT.java   | 203 +++++++++++++++++++
 .../src/test/resources/surefire-1364/pom.xml    | 156 ++++++++++++++
 .../surefire-1364/src/test/java/FirstTest.java  |  30 +++
 .../surefire-1364/src/test/java/SecondTest.java |  30 +++
 .../surefire-1364/src/test/java/ThirdTest.java  |  30 +++
 .../maven/surefire/junit4/MockReporter.java     |   5 +-
 .../maven/surefire/junit/JUnit3Provider.java    |  16 +-
 .../maven/surefire/junit/JUnitTestSetTest.java  |  15 +-
 .../maven/surefire/junit4/JUnit4Provider.java   |   4 +-
 .../junitcore/ClassesParallelRunListener.java   |   2 +-
 .../junitcore/ConcurrentRunListener.java        |   5 +-
 .../junitcore/MethodsParallelRunListener.java   |   2 +-
 .../junitcore/NonConcurrentRunListener.java     |  27 ++-
 .../maven/surefire/junitcore/TestSet.java       |  32 ++-
 .../apache/maven/surefire/testng/TestSuite.java |   8 +-
 37 files changed, 946 insertions(+), 379 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
index d3cca28..28e7ff0 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
@@ -19,13 +19,6 @@ package org.apache.maven.plugin.surefire;
  * under the License.
  */
 
-import java.io.File;
-import java.io.PrintStream;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.concurrent.ConcurrentHashMap;
-
 import org.apache.maven.plugin.surefire.report.ConsoleOutputFileReporter;
 import org.apache.maven.plugin.surefire.report.DirectConsoleOutput;
 import org.apache.maven.plugin.surefire.report.FileReporter;
@@ -35,6 +28,11 @@ import org.apache.maven.plugin.surefire.report.WrappedReportEntry;
 import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
 
 import javax.annotation.Nonnull;
+import java.io.File;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import static org.apache.maven.plugin.surefire.report.ConsoleReporter.BRIEF;
 import static org.apache.maven.plugin.surefire.report.ConsoleReporter.PLAIN;
@@ -79,8 +77,6 @@ public final class StartupReportConfiguration
 
     private final String xsdSchemaLocation;
 
-    private final Properties testVmSystemProperties = new Properties();
-
     private final Map<String, Map<String, List<WrappedReportEntry>>> testClassMethodRunHistory
         = new ConcurrentHashMap<String, Map<String, List<WrappedReportEntry>>>();
 
@@ -217,11 +213,6 @@ public final class StartupReportConfiguration
         return statisticsFile;
     }
 
-    public Properties getTestVmSystemProperties()
-    {
-        return testVmSystemProperties;
-    }
-
     public boolean isTrimStackTrace()
     {
         return trimStackTrace;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
index 8ff6768..cd4ae2b 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java
@@ -21,7 +21,7 @@ package org.apache.maven.plugin.surefire.booterclient;
 
 import org.apache.maven.plugin.surefire.AbstractSurefireMojo;
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
-import org.apache.maven.plugin.surefire.util.ImmutableMap;
+import org.apache.maven.surefire.util.internal.ImmutableMap;
 import org.apache.maven.plugin.surefire.util.Relocator;
 import org.apache.maven.shared.utils.StringUtils;
 import org.apache.maven.surefire.booter.Classpath;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
index 7619d0b..a2a5095 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
@@ -57,7 +57,6 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Map;
-import java.util.Properties;
 import java.util.Queue;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.Callable;
@@ -270,8 +269,7 @@ public class ForkStarter
         TestLessInputStreamBuilder builder = new TestLessInputStreamBuilder();
         PropertiesWrapper props = new PropertiesWrapper( providerProperties );
         TestLessInputStream stream = builder.build();
-        Properties sysProps = startupReportConfiguration.getTestVmSystemProperties();
-        ForkClient forkClient = new ForkClient( forkedReporterFactory, sysProps, stream, log );
+        ForkClient forkClient = new ForkClient( forkedReporterFactory, stream, log );
         Thread shutdown = createImmediateShutdownHookThread( builder, providerConfiguration.getShutdown() );
         ScheduledFuture<?> ping = triggerPingTimerForShutdown( builder );
         try
@@ -347,10 +345,7 @@ public class ForkStarter
                     {
                         DefaultReporterFactory reporter = new DefaultReporterFactory( startupReportConfiguration, log );
                         defaultReporterFactories.add( reporter );
-
-                        Properties vmProps = startupReportConfiguration.getTestVmSystemProperties();
-
-                        ForkClient forkClient = new ForkClient( reporter, vmProps, testProvidingInputStream, log )
+                        ForkClient forkClient = new ForkClient( reporter, testProvidingInputStream, log )
                         {
                             @Override
                             protected void stopOnNextTest()
@@ -413,9 +408,8 @@ public class ForkStarter
                         DefaultReporterFactory forkedReporterFactory =
                             new DefaultReporterFactory( startupReportConfiguration, log );
                         defaultReporterFactories.add( forkedReporterFactory );
-                        Properties vmProps = startupReportConfiguration.getTestVmSystemProperties();
-                        ForkClient forkClient = new ForkClient( forkedReporterFactory, vmProps,
-                                                                      builder.getImmediateCommands(), log )
+                        ForkClient forkClient =
+                                new ForkClient( forkedReporterFactory, builder.getImmediateCommands(), log )
                         {
                             @Override
                             protected void stopOnNextTest()

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
index b94fba6..17da4df 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
@@ -27,22 +27,26 @@ import org.apache.maven.surefire.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.report.StackTraceWriter;
+import org.apache.maven.surefire.report.TestSetReportEntry;
 
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
 import java.io.StringReader;
 import java.nio.ByteBuffer;
-import java.util.Properties;
+import java.util.Collections;
+import java.util.Map;
 import java.util.Queue;
 import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.atomic.AtomicLong;
 
 import static java.lang.Integer.decode;
 import static java.lang.System.currentTimeMillis;
+import static java.util.Collections.unmodifiableMap;
 import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_BYE;
 import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_CONSOLE;
 import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_DEBUG;
@@ -83,7 +87,7 @@ public class ForkClient
 
     private final DefaultReporterFactory defaultReporterFactory;
 
-    private final Properties testVmSystemProperties;
+    private final Map<String, String> testVmSystemProperties = new ConcurrentHashMap<String, String>();
 
     private final NotifiableTestStream notifiableTestStream;
 
@@ -108,11 +112,10 @@ public class ForkClient
     // prevents from printing same warning
     private boolean printedErrorStream;
 
-    public ForkClient( DefaultReporterFactory defaultReporterFactory, Properties testVmSystemProperties,
+    public ForkClient( DefaultReporterFactory defaultReporterFactory,
                        NotifiableTestStream notifiableTestStream, ConsoleLogger log )
     {
         this.defaultReporterFactory = defaultReporterFactory;
-        this.testVmSystemProperties = testVmSystemProperties;
         this.notifiableTestStream = notifiableTestStream;
         this.log = log;
     }
@@ -210,7 +213,7 @@ public class ForkClient
             case BOOTERCODE_TESTSET_COMPLETED:
                 testsInProgress.clear();
 
-                getTestSetReporter().testSetCompleted( createReportEntry( remaining ) );
+                getTestSetReporter().testSetCompleted( createReportEntry( remaining, testVmSystemProperties ) );
                 break;
             case BOOTERCODE_TEST_STARTING:
                 ReportEntry reportEntry = createReportEntry( remaining );
@@ -254,10 +257,7 @@ public class ForkClient
                 StringBuilder value = new StringBuilder();
                 unescapeString( key, remaining.substring( 0, keyEnd ) );
                 unescapeString( value, remaining.substring( keyEnd + 1 ) );
-                synchronized ( testVmSystemProperties )
-                {
-                    testVmSystemProperties.put( key.toString(), value.toString() );
-                }
+                testVmSystemProperties.put( key.toString(), value.toString() );
                 break;
             case BOOTERCODE_STDOUT:
                 writeTestOutput( remaining, true );
@@ -360,7 +360,12 @@ public class ForkClient
         return unescape( remaining );
     }
 
-    private ReportEntry createReportEntry( String untokenized )
+    private TestSetReportEntry createReportEntry( String untokenized )
+    {
+        return createReportEntry( untokenized, Collections.<String, String>emptyMap() );
+    }
+
+    private TestSetReportEntry createReportEntry( String untokenized, Map<String, String> systemProperties )
     {
         StringTokenizer tokens = new StringTokenizer( untokenized, "," );
         try
@@ -374,7 +379,7 @@ public class ForkClient
             final StackTraceWriter stackTraceWriter =
                     tokens.hasMoreTokens() ? deserializeStackTraceWriter( tokens ) : null;
 
-            return reportEntry( source, name, group, stackTraceWriter, elapsed, message );
+            return reportEntry( source, name, group, stackTraceWriter, elapsed, message, systemProperties );
         }
         catch ( RuntimeException e )
         {
@@ -403,6 +408,11 @@ public class ForkClient
         return stringBuffer.toString();
     }
 
+    public final Map<String, String> getTestVmSystemProperties()
+    {
+        return unmodifiableMap( testVmSystemProperties );
+    }
+
     /**
      * Used when getting reporters on the plugin side of a fork.
      * Used by testing purposes only. May not be volatile variable.

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java
index aa858f2..60c6dfe 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java
@@ -39,7 +39,7 @@ import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
+import java.util.Map.Entry;
 import java.util.StringTokenizer;
 
 import static org.apache.maven.plugin.surefire.report.DefaultReporterFactory.TestResultType;
@@ -131,10 +131,10 @@ public class StatelessXmlReporter
 
             createTestSuiteElement( ppw, testSetReportEntry, testSetStats, testSetReportEntry.elapsedTimeAsString() );
 
-            showProperties( ppw );
+            showProperties( ppw, testSetReportEntry.getSystemProperties() );
 
             // Iterate through all the test methods in the test class
-            for ( Map.Entry<String, List<WrappedReportEntry>> entry : methodRunHistoryMap.entrySet() )
+            for ( Entry<String, List<WrappedReportEntry>> entry : methodRunHistoryMap.entrySet() )
             {
                 List<WrappedReportEntry> methodEntryList = entry.getValue();
                 if ( methodEntryList == null )
@@ -458,31 +458,26 @@ public class StatelessXmlReporter
      *
      * @param xmlWriter The test suite to report to
      */
-    private static void showProperties( XMLWriter xmlWriter )
+    private static void showProperties( XMLWriter xmlWriter, Map<String, String> systemProperties )
     {
         xmlWriter.startElement( "properties" );
-
-        Properties systemProperties = System.getProperties();
-
-        if ( systemProperties != null )
+        for ( final Entry<String, String> entry : systemProperties.entrySet() )
         {
-            for ( final String key : systemProperties.stringPropertyNames() )
-            {
-                String value = systemProperties.getProperty( key );
+            final String key = entry.getKey();
+            String value = entry.getValue();
 
-                if ( value == null )
-                {
-                    value = "null";
-                }
+            if ( value == null )
+            {
+                value = "null";
+            }
 
-                xmlWriter.startElement( "property" );
+            xmlWriter.startElement( "property" );
 
-                xmlWriter.addAttribute( "name", key );
+            xmlWriter.addAttribute( "name", key );
 
-                xmlWriter.addAttribute( "value", extraEscape( value, true ) );
+            xmlWriter.addAttribute( "value", extraEscape( value, true ) );
 
-                xmlWriter.endElement();
-            }
+            xmlWriter.endElement();
         }
         xmlWriter.endElement();
     }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
index eea8a7e..e10b8e8 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
@@ -29,6 +29,7 @@ import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.RunListener;
+import org.apache.maven.surefire.report.TestSetReportEntry;
 
 import static org.apache.maven.plugin.surefire.report.ReportEntryType.ERROR;
 import static org.apache.maven.plugin.surefire.report.ReportEntryType.FAILURE;
@@ -144,7 +145,7 @@ public class TestSetRunListener
     }
 
     @Override
-    public void testSetStarting( ReportEntry report )
+    public void testSetStarting( TestSetReportEntry report )
     {
         detailsForThis.testSetStart();
         consoleReporter.testSetStarting( report );
@@ -158,7 +159,7 @@ public class TestSetRunListener
     }
 
     @Override
-    public void testSetCompleted( ReportEntry report )
+    public void testSetCompleted( TestSetReportEntry report )
     {
         final WrappedReportEntry wrap = wrapTestSet( report );
         final List<String> testResults =
@@ -262,11 +263,11 @@ public class TestSetRunListener
         return new WrappedReportEntry( other, reportEntryType, estimatedElapsed, testStdOut, testStdErr );
     }
 
-    private WrappedReportEntry wrapTestSet( ReportEntry other )
+    private WrappedReportEntry wrapTestSet( TestSetReportEntry other )
     {
         return new WrappedReportEntry( other, null, other.getElapsed() != null
             ? other.getElapsed()
-            : detailsForThis.getElapsedSinceTestSetStart(), testStdOut, testStdErr );
+            : detailsForThis.getElapsedSinceTestSetStart(), testStdOut, testStdErr, other.getSystemProperties() );
     }
 
     public void close()

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java
index 439fdec..402fda0 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java
@@ -21,14 +21,19 @@ package org.apache.maven.plugin.surefire.report;
 
 import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.StackTraceWriter;
+import org.apache.maven.surefire.report.TestSetReportEntry;
 
+import java.util.Collections;
+import java.util.Map;
+
+import static java.util.Collections.unmodifiableMap;
 import static org.apache.maven.surefire.util.internal.StringUtils.NL;
 
 /**
  * @author Kristian Rosenvold
  */
 public class WrappedReportEntry
-    implements ReportEntry
+    implements TestSetReportEntry
 {
     private final ReportEntry original;
 
@@ -40,15 +45,26 @@ public class WrappedReportEntry
 
     private final Utf8RecodingDeferredFileOutputStream stdErr;
 
+    private final Map<String, String> systemProperties;
+
     public WrappedReportEntry( ReportEntry original, ReportEntryType reportEntryType, Integer estimatedElapsed,
                                Utf8RecodingDeferredFileOutputStream stdout,
-                               Utf8RecodingDeferredFileOutputStream stdErr )
+                               Utf8RecodingDeferredFileOutputStream stdErr,
+                               Map<String, String> systemProperties )
     {
         this.original = original;
         this.reportEntryType = reportEntryType;
         this.elapsed = estimatedElapsed;
         this.stdout = stdout;
         this.stdErr = stdErr;
+        this.systemProperties = unmodifiableMap( systemProperties );
+    }
+
+    public WrappedReportEntry( ReportEntry original, ReportEntryType reportEntryType, Integer estimatedElapsed,
+                               Utf8RecodingDeferredFileOutputStream stdout,
+                               Utf8RecodingDeferredFileOutputStream stdErr )
+    {
+        this( original, reportEntryType, estimatedElapsed, stdout, stdErr, Collections.<String, String>emptyMap() );
     }
 
     @Override
@@ -171,4 +187,10 @@ public class WrappedReportEntry
     {
         return original.getNameWithGroup();
     }
+
+    @Override
+    public Map<String, String> getSystemProperties()
+    {
+        return systemProperties;
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/ImmutableMap.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/ImmutableMap.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/ImmutableMap.java
deleted file mode 100644
index 576194d..0000000
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/ImmutableMap.java
+++ /dev/null
@@ -1,133 +0,0 @@
-package org.apache.maven.plugin.surefire.util;
-
-/*
- * 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.
- */
-
-import java.util.AbstractMap;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Copies input map in {@link #ImmutableMap(Map) constructor}, and Entries are linked and thread-safe.
- * The map is immutable.
- *
- * @param <K> key
- * @param <V> value
- * @since 2.20
- */
-public final class ImmutableMap<K, V>
-        extends AbstractMap<K, V>
-{
-    private final Node<K, V> first;
-
-    public ImmutableMap( Map<K, V> map )
-    {
-        Node<K, V> first = null;
-        Node<K, V> previous = null;
-        for ( Entry<K, V> e : map.entrySet() )
-        {
-            Node<K, V> node = new Node<K, V>( e.getKey(), e.getValue() );
-            if ( first == null )
-            {
-                first = node;
-            }
-            else
-            {
-                previous.next = node;
-            }
-            previous = node;
-        }
-        this.first = first;
-    }
-
-    @Override
-    public Set<Entry<K, V>> entrySet()
-    {
-        Set<Entry<K, V>> entries = new LinkedHashSet<Entry<K, V>>();
-        Node<K, V> node = first;
-        while ( node != null )
-        {
-            entries.add( node );
-            node = node.next;
-        }
-        return Collections.<Entry<K, V>>unmodifiableSet( entries );
-    }
-
-    static final class Node<K, V>
-            implements Entry<K, V>
-    {
-        final K key;
-        final V value;
-        volatile Node<K, V> next;
-
-        Node( K key, V value )
-        {
-            this.key = key;
-            this.value = value;
-        }
-
-        @Override
-        public K getKey()
-        {
-            return key;
-        }
-
-        @Override
-        public V getValue()
-        {
-            return value;
-        }
-
-        @Override
-        public V setValue( V value )
-        {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean equals( Object o )
-        {
-            if ( this == o )
-            {
-                return true;
-            }
-
-            if ( o == null || getClass() != o.getClass() )
-            {
-                return false;
-            }
-
-            Node<?, ?> node = (Node<?, ?>) o;
-
-            return getKey() != null ? getKey().equals( node.getKey() ) : node.getKey() == null
-                           && getValue() != null ? getValue().equals( node.getValue() ) : node.getValue() == null;
-
-        }
-
-        @Override
-        public int hashCode()
-        {
-            int result = getKey() != null ? getKey().hashCode() : 0;
-            result = 31 * result + ( getValue() != null ? getValue().hashCode() : 0 );
-            return result;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java
index 19356f5..12b2087 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkingRunListenerTest.java
@@ -34,16 +34,16 @@ import org.apache.maven.surefire.report.ReporterException;
 import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.report.SimpleReportEntry;
 import org.apache.maven.surefire.report.StackTraceWriter;
+import org.apache.maven.surefire.report.TestSetReportEntry;
+import org.hamcrest.MatcherAssert;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.PrintStream;
 import java.nio.charset.Charset;
 import java.util.List;
-import java.util.Properties;
 import java.util.StringTokenizer;
 
-import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.Matchers.is;
 
@@ -94,7 +94,7 @@ public class ForkingRunListenerTest
         throws ReporterException, IOException
     {
         final StandardTestRun standardTestRun = new StandardTestRun();
-        ReportEntry expected = createDefaultReportEntry();
+        TestSetReportEntry expected = createDefaultReportEntry();
         standardTestRun.run().testSetStarting( expected );
         standardTestRun.assertExpected( MockReporter.SET_STARTING, expected );
     }
@@ -103,7 +103,7 @@ public class ForkingRunListenerTest
         throws ReporterException, IOException
     {
         final StandardTestRun standardTestRun = new StandardTestRun();
-        ReportEntry expected = createDefaultReportEntry();
+        TestSetReportEntry expected = createDefaultReportEntry();
         standardTestRun.run().testSetCompleted( expected );
         standardTestRun.assertExpected( MockReporter.SET_COMPLETED, expected );
     }
@@ -221,27 +221,24 @@ public class ForkingRunListenerTest
         createForkingRunListener( defaultChannel );
 
         TestSetMockReporterFactory providerReporterFactory = new TestSetMockReporterFactory();
-        final Properties testVmSystemProperties = new Properties();
         NullConsoleLogger log = new NullConsoleLogger();
-        ForkClient forkStreamClient = new ForkClient( providerReporterFactory, testVmSystemProperties,
-                                                      new MockNotifiableTestStream(), log );
+        ForkClient forkStreamClient = new ForkClient( providerReporterFactory, new MockNotifiableTestStream(), log );
 
-        forkStreamClient.consumeMultiLineContent( content.toString( "utf-8" ) );
+        forkStreamClient.consumeMultiLineContent( content.toString( "UTF-8" ) );
 
-        assertThat( testVmSystemProperties.size(), is( greaterThan( 1 ) ) );
+        MatcherAssert.assertThat( forkStreamClient.getTestVmSystemProperties().size(), is( greaterThan( 1 ) ) );
     }
 
     public void testMultipleEntries()
         throws ReporterException, IOException
     {
-
         final StandardTestRun standardTestRun = new StandardTestRun();
         standardTestRun.run();
 
         reset();
         RunListener forkingReporter = createForkingRunListener( defaultChannel );
 
-        ReportEntry reportEntry = createDefaultReportEntry();
+        TestSetReportEntry reportEntry = createDefaultReportEntry();
         forkingReporter.testSetStarting( reportEntry );
         forkingReporter.testStarting( reportEntry );
         forkingReporter.testSucceeded( reportEntry );
@@ -249,10 +246,9 @@ public class ForkingRunListenerTest
 
         TestSetMockReporterFactory providerReporterFactory = new TestSetMockReporterFactory();
         NullConsoleLogger log = new NullConsoleLogger();
-        ForkClient forkStreamClient = new ForkClient( providerReporterFactory, new Properties(),
-                                                      new MockNotifiableTestStream(), log );
+        ForkClient forkStreamClient = new ForkClient( providerReporterFactory, new MockNotifiableTestStream(), log );
 
-        forkStreamClient.consumeMultiLineContent( content.toString( "utf-8" ) );
+        forkStreamClient.consumeMultiLineContent( content.toString( "UTF-8" ) );
 
         final MockReporter reporter = (MockReporter) forkStreamClient.getReporter();
         final List<String> events = reporter.getEvents();
@@ -276,20 +272,19 @@ public class ForkingRunListenerTest
                 .testSkipped( secondExpected );
 
         TestSetMockReporterFactory providerReporterFactory = new TestSetMockReporterFactory();
-        Properties vmProps = new Properties();
         NotifiableTestStream notifiableTestStream = new MockNotifiableTestStream();
         NullConsoleLogger log = new NullConsoleLogger();
 
-        ForkClient forkStreamClient = new ForkClient( providerReporterFactory, vmProps, notifiableTestStream, log );
-        forkStreamClient.consumeMultiLineContent( content.toString( "utf-8" ) );
+        ForkClient forkStreamClient = new ForkClient( providerReporterFactory, notifiableTestStream, log );
+        forkStreamClient.consumeMultiLineContent( content.toString( "UTF-8" ) );
 
         MockReporter reporter = (MockReporter) forkStreamClient.getReporter();
         Assert.assertEquals( MockReporter.TEST_STARTING, reporter.getFirstEvent() );
         Assert.assertEquals( expected, reporter.getFirstData() );
         Assert.assertEquals( 1, reporter.getEvents().size() );
 
-        forkStreamClient = new ForkClient( providerReporterFactory, vmProps, notifiableTestStream, log );
-        forkStreamClient.consumeMultiLineContent( anotherContent.toString( "utf-8" ) );
+        forkStreamClient = new ForkClient( providerReporterFactory, notifiableTestStream, log );
+        forkStreamClient.consumeMultiLineContent( anotherContent.toString( "UTF-8" ) );
         MockReporter reporter2 = (MockReporter) forkStreamClient.getReporter();
         Assert.assertEquals( MockReporter.TEST_SKIPPED, reporter2.getFirstEvent() );
         Assert.assertEquals( secondExpected, reporter2.getFirstData() );
@@ -357,7 +352,7 @@ public class ForkingRunListenerTest
         {
             TestSetMockReporterFactory providerReporterFactory = new TestSetMockReporterFactory();
             NullConsoleLogger log = new NullConsoleLogger();
-            final ForkClient forkStreamClient = new ForkClient( providerReporterFactory, new Properties(),
+            final ForkClient forkStreamClient = new ForkClient( providerReporterFactory,
                                                                 new MockNotifiableTestStream(), log );
             forkStreamClient.consumeMultiLineContent( content.toString( ) );
             reporter = (MockReporter) forkStreamClient.getReporter();

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
index bfc8faf..150d443 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockReporter.java
@@ -19,13 +19,15 @@ package org.apache.maven.plugin.surefire.booterclient;
  * under the License.
  */
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
 import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.RunListener;
+import org.apache.maven.surefire.report.TestSetReportEntry;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Internal tests use only.
@@ -66,14 +68,14 @@ public class MockReporter
     private final AtomicInteger testFailed = new AtomicInteger();
 
     @Override
-    public void testSetStarting( ReportEntry report )
+    public void testSetStarting( TestSetReportEntry report )
     {
         events.add( SET_STARTING );
         data.add( report );
     }
 
     @Override
-    public void testSetCompleted( ReportEntry report )
+    public void testSetCompleted( TestSetReportEntry report )
     {
         events.add( SET_COMPLETED );
         data.add( report );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporterTest.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporterTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporterTest.java
index c5b7a1e..445eaa8 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporterTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporterTest.java
@@ -38,6 +38,7 @@ import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import static org.apache.maven.surefire.util.internal.ObjectUtils.systemProps;
 import static org.apache.maven.surefire.util.internal.StringUtils.UTF_8;
 
 @SuppressWarnings( "ResultOfMethodCallIgnored" )
@@ -90,7 +91,7 @@ public class StatelessXmlReporterTest
 
         ReportEntry reportEntry = new SimpleReportEntry( getClass().getName(), getClass().getName(), 12 );
         WrappedReportEntry testSetReportEntry =
-            new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS, 12, null, null );
+            new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS, 12, null, null, systemProps() );
         stats.testSucceeded( testSetReportEntry );
         reporter.testSetCompleted( testSetReportEntry, stats );
 
@@ -105,7 +106,7 @@ public class StatelessXmlReporterTest
     {
         ReportEntry reportEntry = new SimpleReportEntry( getClass().getName(), TEST_ONE, 12 );
         WrappedReportEntry testSetReportEntry =
-            new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS, 12, null, null );
+            new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS, 12, null, null, systemProps() );
         expectedReportFile = new File( reportDir, "TEST-" + TEST_ONE + ".xml" );
 
         stats.testSucceeded( testSetReportEntry );
@@ -177,7 +178,7 @@ public class StatelessXmlReporterTest
         ReportEntry reportEntry = new SimpleReportEntry( getClass().getName(), TEST_ONE, 12 );
 
         WrappedReportEntry testSetReportEntry =
-            new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS, 12, null, null );
+            new WrappedReportEntry( reportEntry, ReportEntryType.SUCCESS, 12, null, null, systemProps() );
         expectedReportFile = new File( reportDir, "TEST-" + TEST_ONE + ".xml" );
 
         stats.testSucceeded( testSetReportEntry );
@@ -294,5 +295,4 @@ public class StatelessXmlReporterTest
     {
 
     }
-
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/util/ImmutableMapTest.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/util/ImmutableMapTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/util/ImmutableMapTest.java
deleted file mode 100644
index 8fcf2bb..0000000
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/util/ImmutableMapTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package org.apache.maven.plugin.surefire.util;
-
-/*
- * 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.
- */
-
-import org.apache.maven.plugin.surefire.util.ImmutableMap.Node;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import static org.hamcrest.Matchers.hasItem;
-import static org.hamcrest.Matchers.hasSize;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
-/**
- * @since 2.20
- */
-public class ImmutableMapTest
-{
-    private ImmutableMap<String, String> map;
-
-    @Before
-    public void setUp() throws Exception
-    {
-        Map<String, String> backingMap = new HashMap<String, String>();
-        backingMap.put( "a", "1" );
-        backingMap.put( "x", null );
-        backingMap.put( "b", "2" );
-        backingMap.put( "c", "3" );
-        backingMap.put( "", "" );
-        backingMap.put( null, "1" );
-        map = new ImmutableMap<String, String>( backingMap );
-    }
-
-    @Test
-    public void testEntrySet() throws Exception
-    {
-        Set<Entry<String, String>> entries = map.entrySet();
-        assertThat( entries, hasSize( 6 ) );
-        assertThat( entries, hasItem( new Node<String, String>( "a", "1" ) ) );
-        assertThat( entries, hasItem( new Node<String, String>( "x", null ) ) );
-        assertThat( entries, hasItem( new Node<String, String>( "b", "2" ) ) );
-        assertThat( entries, hasItem( new Node<String, String>( "c", "3" ) ) );
-        assertThat( entries, hasItem( new Node<String, String>( "", "" ) ) );
-        assertThat( entries, hasItem( new Node<String, String>( null, "1" ) ) );
-    }
-
-    @Test
-    public void testGetter()
-    {
-        assertThat( map.size(), is( 6 ) );
-        assertThat( map.get( "a" ), is( "1" ) );
-        assertThat( map.get( "x" ), is( (String) null ) );
-        assertThat( map.get( "b" ), is( "2" ) );
-        assertThat( map.get( "c" ), is( "3" ) );
-        assertThat( map.get( "" ), is( "" ) );
-        assertThat( map.get( null ), is( "1" ) );
-    }
-
-    @Test( expected = UnsupportedOperationException.class )
-    public void shouldNotModifyEntries()
-    {
-        map.entrySet().clear();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
index 9fb45bf..f7cec19 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java
@@ -36,7 +36,6 @@ import org.apache.maven.plugin.surefire.report.WrappedReportEntryTest;
 import org.apache.maven.plugin.surefire.runorder.RunEntryStatisticsMapTest;
 import org.apache.maven.plugin.surefire.util.DependenciesScannerTest;
 import org.apache.maven.plugin.surefire.util.DirectoryScannerTest;
-import org.apache.maven.plugin.surefire.util.ImmutableMapTest;
 import org.apache.maven.plugin.surefire.util.SpecificFileFilterTest;
 import org.apache.maven.surefire.report.ConsoleOutputFileReporterTest;
 import org.apache.maven.surefire.report.FileReporterTest;
@@ -73,8 +72,7 @@ import org.junit.runners.Suite;
     TestLessInputStreamBuilderTest.class,
     SPITest.class,
     SurefireReflectorTest.class,
-    ImmutableMapTest.class,
-    SurefireHelperTest.class,
+    SurefireHelperTest.class
 } )
 @RunWith( Suite.class )
 public class JUnit4SuiteTest

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java
index d2c112b..55395c0 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ForkingRunListener.java
@@ -19,9 +19,6 @@ package org.apache.maven.surefire.booter;
  * under the License.
  */
 
-import java.io.PrintStream;
-import java.util.Properties;
-
 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
 import org.apache.maven.plugin.surefire.log.api.ConsoleLoggerUtils;
 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
@@ -31,9 +28,15 @@ import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.report.SafeThrowable;
 import org.apache.maven.surefire.report.SimpleReportEntry;
 import org.apache.maven.surefire.report.StackTraceWriter;
+import org.apache.maven.surefire.report.TestSetReportEntry;
+
+import java.io.PrintStream;
+import java.util.Map.Entry;
 
 import static java.lang.Integer.toHexString;
 import static java.nio.charset.Charset.defaultCharset;
+import static org.apache.maven.surefire.util.internal.ObjectUtils.systemProps;
+import static org.apache.maven.surefire.util.internal.ObjectUtils.useNonNull;
 import static org.apache.maven.surefire.util.internal.StringUtils.encodeStringForForkCommunication;
 import static org.apache.maven.surefire.util.internal.StringUtils.escapeBytesToPrintable;
 import static org.apache.maven.surefire.util.internal.StringUtils.escapeToPrintable;
@@ -132,13 +135,13 @@ public class ForkingRunListener
     }
 
     @Override
-    public void testSetStarting( ReportEntry report )
+    public void testSetStarting( TestSetReportEntry report )
     {
         encodeAndWriteToTarget( toString( BOOTERCODE_TESTSET_STARTING, report, testSetChannelId ) );
     }
 
     @Override
-    public void testSetCompleted( ReportEntry report )
+    public void testSetCompleted( TestSetReportEntry report )
     {
         encodeAndWriteToTarget( toString( BOOTERCODE_TESTSET_COMPLETED, report, testSetChannelId ) );
     }
@@ -187,15 +190,10 @@ public class ForkingRunListener
 
     void sendProps()
     {
-        Properties systemProperties = System.getProperties();
-
-        if ( systemProperties != null )
+        for ( Entry<String, String> entry : systemProps().entrySet() )
         {
-            for ( final String key : systemProperties.stringPropertyNames() )
-            {
-                String value = systemProperties.getProperty( key );
-                encodeAndWriteToTarget( toPropertyString( key, value == null ? "null" : value ) );
-            }
+            String value = entry.getValue();
+            encodeAndWriteToTarget( toPropertyString( entry.getKey(), useNonNull( value, "null" ) ) );
         }
     }
 

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/surefire-api/src/main/java/org/apache/maven/surefire/report/CategorizedReportEntry.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/report/CategorizedReportEntry.java b/surefire-api/src/main/java/org/apache/maven/surefire/report/CategorizedReportEntry.java
index c357cba..47b0c48 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/report/CategorizedReportEntry.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/report/CategorizedReportEntry.java
@@ -19,6 +19,9 @@ package org.apache.maven.surefire.report;
  * under the License.
  */
 
+import java.util.Collections;
+import java.util.Map;
+
 /**
  * @author Kristian Rosenvold
  */
@@ -47,16 +50,23 @@ public class CategorizedReportEntry
     public CategorizedReportEntry( String source, String name, String group, StackTraceWriter stackTraceWriter,
                                    Integer elapsed, String message )
     {
-        super( source, name, stackTraceWriter, elapsed, message );
+        this( source, name, group, stackTraceWriter, elapsed, message, Collections.<String, String>emptyMap() );
+    }
+
+    public CategorizedReportEntry( String source, String name, String group, StackTraceWriter stackTraceWriter,
+                                   Integer elapsed, String message, Map<String, String> systemProperties )
+    {
+        super( source, name, stackTraceWriter, elapsed, message, systemProperties );
         this.group = group;
     }
 
-    public static ReportEntry reportEntry( String source, String name, String group, StackTraceWriter stackTraceWriter,
-                                           Integer elapsed, String message )
+    public static TestSetReportEntry reportEntry( String source, String name, String group,
+                                                  StackTraceWriter stackTraceWriter, Integer elapsed, String message,
+                                                  Map<String, String> systemProperties )
     {
         return group != null
-            ? new CategorizedReportEntry( source, name, group, stackTraceWriter, elapsed, message )
-            : new SimpleReportEntry( source, name, stackTraceWriter, elapsed, message );
+            ? new CategorizedReportEntry( source, name, group, stackTraceWriter, elapsed, message, systemProperties )
+            : new SimpleReportEntry( source, name, stackTraceWriter, elapsed, message, systemProperties );
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/surefire-api/src/main/java/org/apache/maven/surefire/report/RunListener.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/report/RunListener.java b/surefire-api/src/main/java/org/apache/maven/surefire/report/RunListener.java
index 22cda61..32c0abd 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/report/RunListener.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/report/RunListener.java
@@ -34,7 +34,7 @@ public interface RunListener
      * @param report the report entry describing the testset
      * @throws ReporterException When reporting fails
      */
-    void testSetStarting( ReportEntry report );
+    void testSetStarting( TestSetReportEntry report );
 
     /**
      * Indicates end of a given test-set
@@ -42,7 +42,7 @@ public interface RunListener
      * @param report the report entry describing the testset
      * @throws ReporterException When reporting fails
      */
-    void testSetCompleted( ReportEntry report );
+    void testSetCompleted( TestSetReportEntry report );
 
     /**
      * Event fired when a test is about to start

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/surefire-api/src/main/java/org/apache/maven/surefire/report/SimpleReportEntry.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/report/SimpleReportEntry.java b/surefire-api/src/main/java/org/apache/maven/surefire/report/SimpleReportEntry.java
index 0dd4264..1013f39 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/report/SimpleReportEntry.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/report/SimpleReportEntry.java
@@ -19,12 +19,19 @@ package org.apache.maven.surefire.report;
  * under the License.
  */
 
+import org.apache.maven.surefire.util.internal.ImmutableMap;
+
+import java.util.Collections;
+import java.util.Map;
+
 /**
  * @author Kristian Rosenvold
  */
 public class SimpleReportEntry
-    implements ReportEntry
+    implements TestSetReportEntry
 {
+    private final Map<String, String> systemProperties;
+
     private final String source;
 
     private final String name;
@@ -45,6 +52,11 @@ public class SimpleReportEntry
         this( source, name, null, null );
     }
 
+    public SimpleReportEntry( String source, String name, Map<String, String> systemProperties )
+    {
+        this( source, name, null, null, systemProperties );
+    }
+
     private SimpleReportEntry( String source, String name, StackTraceWriter stackTraceWriter )
     {
         this( source, name, stackTraceWriter, null );
@@ -57,11 +69,11 @@ public class SimpleReportEntry
 
     public SimpleReportEntry( String source, String name, String message )
     {
-        this( source, name, null, null, message );
+        this( source, name, null, null, message, Collections.<String, String>emptyMap() );
     }
 
     protected SimpleReportEntry( String source, String name, StackTraceWriter stackTraceWriter, Integer elapsed,
-                                 String message )
+                                 String message, Map<String, String> systemProperties )
     {
         if ( source == null )
         {
@@ -81,13 +93,19 @@ public class SimpleReportEntry
         this.message = message;
 
         this.elapsed = elapsed;
-    }
 
+        this.systemProperties = new ImmutableMap<String, String>( systemProperties );
+    }
 
     public SimpleReportEntry( String source, String name, StackTraceWriter stackTraceWriter, Integer elapsed )
     {
-        //noinspection ThrowableResultOfMethodCallIgnored
-        this( source, name, stackTraceWriter, elapsed, safeGetMessage( stackTraceWriter ) );
+        this( source, name, stackTraceWriter, elapsed, Collections.<String, String>emptyMap() );
+    }
+
+    public SimpleReportEntry( String source, String name, StackTraceWriter stackTraceWriter, Integer elapsed,
+                              Map<String, String> systemProperties )
+    {
+        this( source, name, stackTraceWriter, elapsed, safeGetMessage( stackTraceWriter ), systemProperties );
     }
 
     public static SimpleReportEntry assumption( String source, String name, String message )
@@ -193,6 +211,12 @@ public class SimpleReportEntry
         return getName();
     }
 
+    @Override
+    public Map<String, String> getSystemProperties()
+    {
+        return systemProperties;
+    }
+
     private boolean isElapsedTimeEqual( SimpleReportEntry en )
     {
         return elapsed != null ? elapsed.equals( en.elapsed ) : en.elapsed == null;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/surefire-api/src/main/java/org/apache/maven/surefire/report/TestSetReportEntry.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/report/TestSetReportEntry.java b/surefire-api/src/main/java/org/apache/maven/surefire/report/TestSetReportEntry.java
new file mode 100644
index 0000000..02d8669
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/report/TestSetReportEntry.java
@@ -0,0 +1,35 @@
+package org.apache.maven.surefire.report;
+
+/*
+ * 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.
+ */
+
+import java.util.Map;
+
+/**
+ * Describes test-set when started and finished.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @see RunListener#testSetStarting(TestSetReportEntry)
+ * @see RunListener#testSetCompleted(TestSetReportEntry)
+ * @since 2.20.1
+ */
+public interface TestSetReportEntry extends ReportEntry
+{
+    Map<String, String> getSystemProperties();
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ImmutableMap.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ImmutableMap.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ImmutableMap.java
new file mode 100644
index 0000000..003d884
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ImmutableMap.java
@@ -0,0 +1,134 @@
+package org.apache.maven.surefire.util.internal;
+
+/*
+ * 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.
+ */
+
+import java.util.AbstractMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static java.util.Collections.unmodifiableSet;
+
+/**
+ * Copies input map in {@link #ImmutableMap(Map) constructor}, and Entries are linked and thread-safe.
+ * The map is immutable with linear list of entries.
+ *
+ * @param <K> key
+ * @param <V> value
+ * @since 2.20
+ */
+public final class ImmutableMap<K, V>
+        extends AbstractMap<K, V>
+{
+    private final Node<K, V> first;
+
+    public ImmutableMap( Map<K, V> map )
+    {
+        Node<K, V> first = null;
+        Node<K, V> previous = null;
+        for ( Entry<K, V> e : map.entrySet() )
+        {
+            Node<K, V> node = new Node<K, V>( e.getKey(), e.getValue() );
+            if ( first == null )
+            {
+                first = node;
+            }
+            else
+            {
+                previous.next = node;
+            }
+            previous = node;
+        }
+        this.first = first;
+    }
+
+    @Override
+    public Set<Entry<K, V>> entrySet()
+    {
+        Set<Entry<K, V>> entries = new LinkedHashSet<Entry<K, V>>();
+        Node<K, V> node = first;
+        while ( node != null )
+        {
+            entries.add( node );
+            node = node.next;
+        }
+        return unmodifiableSet( entries );
+    }
+
+    static final class Node<K, V>
+            implements Entry<K, V>
+    {
+        final K key;
+        final V value;
+        volatile Node<K, V> next;
+
+        Node( K key, V value )
+        {
+            this.key = key;
+            this.value = value;
+        }
+
+        @Override
+        public K getKey()
+        {
+            return key;
+        }
+
+        @Override
+        public V getValue()
+        {
+            return value;
+        }
+
+        @Override
+        public V setValue( V value )
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean equals( Object o )
+        {
+            if ( this == o )
+            {
+                return true;
+            }
+
+            if ( o == null || getClass() != o.getClass() )
+            {
+                return false;
+            }
+
+            Node<?, ?> node = (Node<?, ?>) o;
+
+            return getKey() != null ? getKey().equals( node.getKey() ) : node.getKey() == null
+                           && getValue() != null ? getValue().equals( node.getValue() ) : node.getValue() == null;
+
+        }
+
+        @Override
+        public int hashCode()
+        {
+            int result = getKey() != null ? getKey().hashCode() : 0;
+            result = 31 * result + ( getValue() != null ? getValue().hashCode() : 0 );
+            return result;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ObjectUtils.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ObjectUtils.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ObjectUtils.java
index 996c3be..e957ae2 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ObjectUtils.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/ObjectUtils.java
@@ -1 +1 @@
-package org.apache.maven.surefire.util.internal;

/*
 * 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.
 */

/**
 * Similar to Java 7 java.util.Objects.
 *
 * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
 * @since 2
 .20
 */
public final class ObjectUtils
{
    private ObjectUtils()
    {
        throw new IllegalStateException( "no instantiable constructor" );
    }

    public static <T> T useNonNull( T target, T fallback )
    {
        return isNull( target ) ? fallback : target;
    }

    /*
    * In JDK7 use java.util.Objects instead.
    * todo
    * */
    public static boolean isNull( Object target )
    {
        return target == null;
    }

    /*
    * In JDK7 use java.util.Objects instead.
    * todo
    * */
    public static boolean nonNull( Object target )
    {
        return !isNull( target );
    }

    /*
    * In JDK7 use java.util.Objects instead.
    * todo
    * */
    public static <T> T requireNonNull( T obj, String message )
    {
        if ( isNull( obj ) )
        {
            throw new NullPointerException( message );
        }
        return obj;
    }

    /*
    * In JDK7 use java.util.Objects instead.
    * todo
    * */
    public static <T> T requireNonNul
 l( T obj )
    {
        return requireNonNull( obj, null );
    }
}
\ No newline at end of file
+package org.apache.maven.surefire.util.internal;

/*
 * 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.
 */

import java.lang.management.ManagementFactory;
import java.util.Map;

/**
 * Similar to Java 7 java.util.Objects.
 *
 * @author <a href=
 "mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
 * @since 2.20
 */
public final class ObjectUtils
{
    private ObjectUtils()
    {
        throw new IllegalStateException( "no instantiable constructor" );
    }

    public static <T> T useNonNull( T target, T fallback )
    {
        return isNull( target ) ? fallback : target;
    }

    /*
    * In JDK7 use java.util.Objects instead.
    * todo
    * */
    public static boolean isNull( Object target )
    {
        return target == null;
    }

    /*
    * In JDK7 use java.util.Objects instead.
    * todo
    * */
    public static boolean nonNull( Object target )
    {
        return !isNull( target );
    }

    /*
    * In JDK7 use java.util.Objects instead.
    * todo
    * */
    public static <T> T requireNonNull( T obj, String message )
    {
        if ( isNull( obj ) )
        {
            throw new NullPointerException( message );
        }
        return obj;
    }

    /*
    * In JDK7 use java.util.Obje
 cts instead.
    * todo
    * */
    public static <T> T requireNonNull( T obj )
    {
        return requireNonNull( obj, null );
    }

    public static Map<String, String> systemProps()
    {
        return ManagementFactory.getRuntimeMXBean().getSystemProperties();
    }
}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
index 22378bf..da6e6e4 100644
--- a/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/JUnit4SuiteTest.java
@@ -39,6 +39,7 @@ import org.apache.maven.surefire.util.TestsToRunTest;
 import org.apache.maven.surefire.util.UrlUtilsTest;
 import org.apache.maven.surefire.util.internal.ByteBufferTest;
 import org.apache.maven.surefire.util.internal.ConcurrencyUtilsTest;
+import org.apache.maven.surefire.util.internal.ImmutableMapTest;
 import org.apache.maven.surefire.util.internal.StringUtilsTest;
 import org.apache.maven.surefire.util.internal.SystemUtilsTest;
 import org.junit.runner.RunWith;
@@ -70,7 +71,8 @@ import org.junit.runners.Suite;
     UrlUtilsTest.class,
     SpecificTestClassFilterTest.class,
     FundamentalFilterTest.class,
-    SystemUtilsTest.class
+    SystemUtilsTest.class,
+    ImmutableMapTest.class
 } )
 @RunWith( Suite.class )
 public class JUnit4SuiteTest

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ImmutableMapTest.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ImmutableMapTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ImmutableMapTest.java
new file mode 100644
index 0000000..b104cc3
--- /dev/null
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/util/internal/ImmutableMapTest.java
@@ -0,0 +1,86 @@
+package org.apache.maven.surefire.util.internal;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.surefire.util.internal.ImmutableMap.Node;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @since 2.20
+ */
+public class ImmutableMapTest
+{
+    private ImmutableMap<String, String> map;
+
+    @Before
+    public void setUp() throws Exception
+    {
+        Map<String, String> backingMap = new HashMap<String, String>();
+        backingMap.put( "a", "1" );
+        backingMap.put( "x", null );
+        backingMap.put( "b", "2" );
+        backingMap.put( "c", "3" );
+        backingMap.put( "", "" );
+        backingMap.put( null, "1" );
+        map = new ImmutableMap<String, String>( backingMap );
+    }
+
+    @Test
+    public void testEntrySet() throws Exception
+    {
+        Set<Entry<String, String>> entries = map.entrySet();
+        assertThat( entries, hasSize( 6 ) );
+        assertThat( entries, hasItem( new Node<String, String>( "a", "1" ) ) );
+        assertThat( entries, hasItem( new Node<String, String>( "x", null ) ) );
+        assertThat( entries, hasItem( new Node<String, String>( "b", "2" ) ) );
+        assertThat( entries, hasItem( new Node<String, String>( "c", "3" ) ) );
+        assertThat( entries, hasItem( new Node<String, String>( "", "" ) ) );
+        assertThat( entries, hasItem( new Node<String, String>( null, "1" ) ) );
+    }
+
+    @Test
+    public void testGetter()
+    {
+        assertThat( map.size(), is( 6 ) );
+        assertThat( map.get( "a" ), is( "1" ) );
+        assertThat( map.get( "x" ), is( (String) null ) );
+        assertThat( map.get( "b" ), is( "2" ) );
+        assertThat( map.get( "c" ), is( "3" ) );
+        assertThat( map.get( "" ), is( "" ) );
+        assertThat( map.get( null ), is( "1" ) );
+    }
+
+    @Test( expected = UnsupportedOperationException.class )
+    public void shouldNotModifyEntries()
+    {
+        map.entrySet().clear();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1364SystemPropertiesIT.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1364SystemPropertiesIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1364SystemPropertiesIT.java
new file mode 100644
index 0000000..d13b0f2
--- /dev/null
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1364SystemPropertiesIT.java
@@ -0,0 +1,203 @@
+package org.apache.maven.surefire.its.jiras;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.surefire.its.fixture.OutputValidator;
+import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase;
+import org.apache.maven.surefire.its.fixture.SurefireLauncher;
+import org.junit.Test;
+
+/**
+ * Report XML should contain system properties of forked JVM.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.20.1
+ */
+public class Surefire1364SystemPropertiesIT
+        extends SurefireJUnit4IntegrationTestCase
+{
+    @Test
+    public void junit3Forked()
+    {
+        SurefireLauncher launcher = unpack( "surefire-1364" );
+        OutputValidator validator = launcher.setForkJvm()
+                                            .activateProfile( "junit3" )
+                                            .forkMode( "once" )
+                                            .executeTest()
+                                            .verifyErrorFree( 2 );
+
+        validator.getSurefireReportsXmlFile( "TEST-FirstTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+
+        validator.getSurefireReportsXmlFile( "TEST-SecondTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+    }
+
+    @Test
+    public void junit3InProcess()
+    {
+        SurefireLauncher launcher = unpack( "surefire-1364" );
+        OutputValidator validator = launcher.setForkJvm()
+                                            .activateProfile( "junit3" )
+                                            .forkMode( "never" )
+                                            .executeTest()
+                                            .verifyErrorFree( 2 );
+
+        validator.getSurefireReportsXmlFile( "TEST-FirstTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+
+        validator.getSurefireReportsXmlFile( "TEST-SecondTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+    }
+
+    @Test
+    public void junit4Forked()
+    {
+        SurefireLauncher launcher = unpack( "surefire-1364" );
+        OutputValidator validator = launcher.setForkJvm()
+                                            .forkMode( "once" )
+                                            .executeTest()
+                                            .verifyErrorFree( 2 );
+
+        validator.getSurefireReportsXmlFile( "TEST-FirstTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+
+        validator.getSurefireReportsXmlFile( "TEST-SecondTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+    }
+
+    @Test
+    public void junit4InProcess()
+    {
+        SurefireLauncher launcher = unpack( "surefire-1364" );
+        OutputValidator validator = launcher.setForkJvm()
+                                            .forkMode( "never" )
+                                            .executeTest()
+                                            .verifyErrorFree( 2 );
+
+        validator.getSurefireReportsXmlFile( "TEST-FirstTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+
+        validator.getSurefireReportsXmlFile( "TEST-SecondTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+    }
+
+    @Test
+    public void junit47Forked()
+    {
+        SurefireLauncher launcher = unpack( "surefire-1364" );
+        OutputValidator validator = launcher.setForkJvm()
+                                            .activateProfile( "junit47" )
+                                            .forkMode( "once" )
+                                            .executeTest()
+                                            .verifyErrorFree( 2 );
+
+        validator.getSurefireReportsXmlFile( "TEST-FirstTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+
+        validator.getSurefireReportsXmlFile( "TEST-SecondTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+    }
+
+    @Test
+    public void junit47InProcess()
+    {
+        SurefireLauncher launcher = unpack( "surefire-1364" );
+        OutputValidator validator = launcher.setForkJvm()
+                                            .activateProfile( "junit47" )
+                                            .forkMode( "never" )
+                                            .executeTest()
+                                            .verifyErrorFree( 2 );
+
+        validator.getSurefireReportsXmlFile( "TEST-FirstTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+
+        validator.getSurefireReportsXmlFile( "TEST-SecondTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+    }
+
+    @Test
+    public void junit47ForkedParallel()
+    {
+        SurefireLauncher launcher = unpack( "surefire-1364" );
+        OutputValidator validator = launcher.setForkJvm()
+                                            .activateProfile( "junit47" )
+                                            .forkMode( "once" )
+                                            .parallelClasses()
+                                            .threadCount( 2 )
+                                            .disablePerCoreThreadCount()
+                                            .executeTest()
+                                            .verifyErrorFree( 2 );
+
+        validator.getSurefireReportsXmlFile( "TEST-FirstTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+
+        validator.getSurefireReportsXmlFile( "TEST-SecondTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+    }
+
+    @Test
+    public void junit47InProcessParallel()
+    {
+        SurefireLauncher launcher = unpack( "surefire-1364" );
+        OutputValidator validator = launcher.setForkJvm()
+                                            .activateProfile( "junit47" )
+                                            .forkMode( "never" )
+                                            .parallelClasses()
+                                            .threadCount( 2 )
+                                            .disablePerCoreThreadCount()
+                                            .executeTest()
+                                            .verifyErrorFree( 2 );
+
+        validator.getSurefireReportsXmlFile( "TEST-FirstTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+
+        validator.getSurefireReportsXmlFile( "TEST-SecondTest.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+    }
+
+    @Test
+    public void testNg()
+    {
+        SurefireLauncher launcher = unpack( "surefire-1364" );
+        OutputValidator validator = launcher.setForkJvm()
+                                            .activateProfile( "testng" )
+                                            .forkMode( "once" )
+                                            .executeTest()
+                                            .verifyErrorFree( 3 );
+
+        validator.getSurefireReportsXmlFile( "TEST-TestSuite.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+    }
+
+    @Test
+    public void testNgInProcess()
+    {
+        SurefireLauncher launcher = unpack( "surefire-1364" );
+        OutputValidator validator = launcher.setForkJvm()
+                                            .activateProfile( "testng" )
+                                            .forkMode( "never" )
+                                            .executeTest()
+                                            .verifyErrorFree( 3 );
+
+        validator.getSurefireReportsXmlFile( "TEST-TestSuite.xml" )
+                .assertContainsText( "<property name=\"forkedProp\" value=\"forkedValue1\"/>" );
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/99471032/surefire-integration-tests/src/test/resources/surefire-1364/pom.xml
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/surefire-1364/pom.xml b/surefire-integration-tests/src/test/resources/surefire-1364/pom.xml
new file mode 100644
index 0000000..f93565a
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/surefire-1364/pom.xml
@@ -0,0 +1,156 @@
+<?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>
+
+    <parent>
+        <groupId>org.apache.maven.surefire</groupId>
+        <artifactId>it-parent</artifactId>
+        <version>1.0</version>
+    </parent>
+
+    <groupId>org.apache.maven.plugins.surefire</groupId>
+    <artifactId>surefire-1364</artifactId>
+    <version>1.0</version>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <maven.compiler.source>1.6</maven.compiler.source>
+        <maven.compiler.target>1.6</maven.compiler.target>
+        <forkedMode>once</forkedMode>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <configuration>
+                        <testIncludes>
+                            <testInclude>FirstTest.java</testInclude>
+                            <testInclude>SecondTest.java</testInclude>
+                        </testIncludes>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <configuration>
+                        <forkMode>${forkedMode}</forkMode>
+                        <systemPropertyVariables>
+                            <forkedProp>forkedValue${surefire.forkNumber}</forkedProp>
+                        </systemPropertyVariables>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+    <profiles>
+        <profile>
+            <id>junit3</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <dependencies>
+                                <dependency>
+                                    <groupId>org.apache.maven.surefire</groupId>
+                                    <artifactId>surefire-junit3</artifactId>
+                                    <version>${surefire.version}</version>
+                                </dependency>
+                            </dependencies>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+        <profile>
+            <id>junit47</id>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <dependencies>
+                                <dependency>
+                                    <groupId>org.apache.maven.surefire</groupId>
+                                    <artifactId>surefire-junit47</artifactId>
+                                    <version>${surefire.version}</version>
+                                </dependency>
+                            </dependencies>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+        <profile>
+            <id>testng</id>
+            <dependencies>
+                <dependency>
+                    <groupId>org.testng</groupId>
+                    <artifactId>testng</artifactId>
+                    <version>6.8.21</version>
+                    <scope>test</scope>
+                </dependency>
+            </dependencies>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-compiler-plugin</artifactId>
+                            <configuration>
+                                <testIncludes>
+                                    <testInclude>*.java</testInclude>
+                                </testIncludes>
+                            </configuration>
+                        </plugin>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <dependencies>
+                                <dependency>
+                                    <groupId>org.apache.maven.surefire</groupId>
+                                    <artifactId>surefire-testng</artifactId>
+                                    <version>${surefire.version}</version>
+                                </dependency>
+                            </dependencies>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+    </profiles>
+</project>