Repository: maven-surefire
Updated Branches:
  refs/heads/SUREFIRE-1302_2 f2d972424 -> b9034a3bb (forced update)


[SUREFIRE-1302] Surefire does not wait long enough for the forked VM and 
assumes it to be dead


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

Branch: refs/heads/SUREFIRE-1302_2
Commit: b9034a3bbb32a27a2d7b66e4d516c2dac61fce29
Parents: dd518d2
Author: Tibor17 <tibordig...@apache.org>
Authored: Sat Jun 10 10:17:39 2017 +0200
Committer: Tibor17 <tibordig...@apache.org>
Committed: Sun Jun 18 01:48:23 2017 +0200

----------------------------------------------------------------------
 maven-failsafe-plugin/pom.xml                   |  17 --
 maven-surefire-common/pom.xml                   |  10 +-
 .../maven/plugin/surefire/SurefireHelper.java   |   2 +-
 .../surefire/report/FileReporterUtils.java      |  11 +-
 .../maven/plugin/surefire/util/ScannerUtil.java |   2 +-
 .../plugin/surefire/SurefireHelperTest.java     |   2 +-
 .../booterclient/ForkConfigurationTest.java     |   4 +-
 maven-surefire-plugin/pom.xml                   |  17 --
 maven-surefire-report-plugin/pom.xml            |   4 -
 pom.xml                                         |  23 +-
 surefire-api/pom.xml                            |  10 +-
 .../maven/surefire/booter/CommandReader.java    |   2 +-
 surefire-booter/pom.xml                         |  15 +
 .../maven/surefire/booter/ForkedBooter.java     |  67 ++++-
 .../maven/surefire/booter/PpidChecker.java      | 282 +++++++++++++++++++
 .../maven/surefire/booter/ProcessInfo.java      |  62 ++++
 .../maven/surefire/booter/JUnit4SuiteTest.java  |   3 +-
 .../maven/surefire/booter/PpidCheckerTest.java  | 127 +++++++++
 ...urefire1295AttributeJvmCrashesToTestsIT.java |   7 +-
 19 files changed, 595 insertions(+), 72 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/maven-failsafe-plugin/pom.xml
----------------------------------------------------------------------
diff --git a/maven-failsafe-plugin/pom.xml b/maven-failsafe-plugin/pom.xml
index f42e682..ec48929 100644
--- a/maven-failsafe-plugin/pom.xml
+++ b/maven-failsafe-plugin/pom.xml
@@ -45,27 +45,10 @@
 
   <dependencies>
     <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-plugin-api</artifactId>
-    </dependency>
-    <dependency>
       <groupId>org.apache.maven.surefire</groupId>
       <artifactId>maven-surefire-common</artifactId>
     </dependency>
     <dependency>
-      <groupId>org.apache.maven.surefire</groupId>
-      <artifactId>surefire-api</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.maven.shared</groupId>
-      <artifactId>maven-shared-utils</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.maven.plugin-tools</groupId>
-      <artifactId>maven-plugin-annotations</artifactId>
-      <scope>compile</scope>
-    </dependency>
-    <dependency>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-surefire-plugin</artifactId>
       <version>${project.version}</version>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/maven-surefire-common/pom.xml
----------------------------------------------------------------------
diff --git a/maven-surefire-common/pom.xml b/maven-surefire-common/pom.xml
index 4064bc2..670db71 100644
--- a/maven-surefire-common/pom.xml
+++ b/maven-surefire-common/pom.xml
@@ -87,12 +87,13 @@
       <artifactId>maven-toolchain</artifactId>
     </dependency>
     <dependency>
-      <groupId>org.apache.commons</groupId>
-      <artifactId>commons-lang3</artifactId>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
     </dependency>
     <dependency>
       <groupId>com.google.code.findbugs</groupId>
       <artifactId>jsr305</artifactId>
+      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.apache.maven.shared</groupId>
@@ -192,6 +193,7 @@
                   <include>org.apache.maven.shared:maven-shared-utils</include>
                   
<include>org.apache.maven.shared:maven-common-artifact-filters</include>
                   <include>commons-io:commons-io</include>
+                  <include>commons-lang:commons-lang</include>
                 </includes>
               </artifactSet>
               <relocations>
@@ -203,6 +205,10 @@
                   <pattern>org.apache.commons.io</pattern>
                   
<shadedPattern>org.apache.maven.surefire.shade.org.apache.commons.io</shadedPattern>
                 </relocation>
+                <relocation>
+                  <pattern>org.apache.commons.lang</pattern>
+                  
<shadedPattern>org.apache.maven.surefire.shade.org.apache.commons.lang</shadedPattern>
+                </relocation>
               </relocations>
             </configuration>
           </execution>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireHelper.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireHelper.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireHelper.java
index dd29cb4..32b06f9 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireHelper.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireHelper.java
@@ -36,7 +36,7 @@ import java.util.Collection;
 import java.util.List;
 
 import static java.util.Collections.unmodifiableList;
-import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS;
+import static org.apache.commons.lang.SystemUtils.IS_OS_WINDOWS;
 import static 
org.apache.maven.surefire.booter.DumpErrorSingleton.DUMPSTREAM_FILE_EXT;
 import static 
org.apache.maven.surefire.booter.DumpErrorSingleton.DUMP_FILE_EXT;
 import static 
org.apache.maven.surefire.cli.CommandLineOption.LOGGING_LEVEL_DEBUG;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporterUtils.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporterUtils.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporterUtils.java
index 36bc269..60a75ad 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporterUtils.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporterUtils.java
@@ -19,6 +19,8 @@ package org.apache.maven.plugin.surefire.report;
  * under the License.
  */
 
+import static org.apache.commons.lang.SystemUtils.IS_OS_WINDOWS;
+
 /**
  * Utils class for file-based reporters
  *
@@ -45,13 +47,6 @@ public final class FileReporterUtils
 
     private static String getOSSpecificIllegalChars()
     {
-        if ( System.getProperty( "os.name" ).toLowerCase().startsWith( "win" ) 
)
-        {
-            return "\\/:*?\"<>|\0";
-        }
-        else
-        {
-            return "/\0";
-        }
+        return IS_OS_WINDOWS ? "\\/:*?\"<>|\0" : "/\0";
     }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/ScannerUtil.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/ScannerUtil.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/ScannerUtil.java
index 9989176..90dd49d 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/ScannerUtil.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/ScannerUtil.java
@@ -19,7 +19,7 @@ package org.apache.maven.plugin.surefire.util;
  * under the License.
  */
 
-import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang.StringUtils;
 import javax.annotation.Nonnull;
 
 final class ScannerUtil

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireHelperTest.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireHelperTest.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireHelperTest.java
index c00f7f9..bf6b8c6 100644
--- 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireHelperTest.java
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/SurefireHelperTest.java
@@ -26,7 +26,7 @@ import java.util.List;
 
 import static java.util.Collections.addAll;
 import static java.util.Collections.singleton;
-import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS;
+import static org.apache.commons.lang.SystemUtils.IS_OS_WINDOWS;
 import static 
org.apache.maven.plugin.surefire.SurefireHelper.escapeToPlatformPath;
 import static org.fest.assertions.Assertions.assertThat;
 import static org.junit.Assume.assumeTrue;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
index 4f62670..89b7c27 100644
--- 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/ForkConfigurationTest.java
@@ -25,8 +25,8 @@ import java.util.Collections;
 import java.util.Properties;
 
 import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang3.RandomStringUtils;
-import org.apache.commons.lang3.SystemUtils;
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.commons.lang.SystemUtils;
 import org.apache.maven.shared.utils.StringUtils;
 import org.apache.maven.shared.utils.cli.Commandline;
 import org.apache.maven.surefire.booter.Classpath;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/maven-surefire-plugin/pom.xml
----------------------------------------------------------------------
diff --git a/maven-surefire-plugin/pom.xml b/maven-surefire-plugin/pom.xml
index 62ec4a7..2a186e3 100644
--- a/maven-surefire-plugin/pom.xml
+++ b/maven-surefire-plugin/pom.xml
@@ -45,26 +45,9 @@
 
   <dependencies>
     <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-plugin-api</artifactId>
-    </dependency>
-    <dependency>
       <groupId>org.apache.maven.surefire</groupId>
       <artifactId>maven-surefire-common</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.apache.maven.surefire</groupId>
-      <artifactId>surefire-api</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-toolchain</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.maven.plugin-tools</groupId>
-      <artifactId>maven-plugin-annotations</artifactId>
-      <scope>compile</scope>
-    </dependency>
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/maven-surefire-report-plugin/pom.xml
----------------------------------------------------------------------
diff --git a/maven-surefire-report-plugin/pom.xml 
b/maven-surefire-report-plugin/pom.xml
index a4fe7e2..93a2e80 100644
--- a/maven-surefire-report-plugin/pom.xml
+++ b/maven-surefire-report-plugin/pom.xml
@@ -48,10 +48,6 @@
 
   <dependencies>
     <dependency>
-      <groupId>org.apache.maven.surefire</groupId>
-      <artifactId>surefire-logger-api</artifactId>
-    </dependency>
-    <dependency>
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-project</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 962a5bf..bba1454 100644
--- a/pom.xml
+++ b/pom.xml
@@ -91,6 +91,10 @@
     <mavenVersion>2.2.1</mavenVersion>
     <!-- <shadedVersion>2.12.4</shadedVersion> commented out due to 
https://issues.apache.org/jira/browse/MRELEASE-799 -->
     <mavenPluginPluginVersion>3.3</mavenPluginPluginVersion>
+    <commonsLangVersion>2.4</commonsLangVersion>
+    <commonsLang3Version>3.1</commonsLang3Version>
+    <commonsIoVersion>2.2</commonsIoVersion>
+    <mavenSharedUtilsVersion>0.9</mavenSharedUtilsVersion>
     
<maven.surefire.scm.devConnection>scm:git:https://git-wip-us.apache.org/repos/asf/maven-surefire.git</maven.surefire.scm.devConnection>
     <maven.site.path>surefire-archives/surefire-LATEST</maven.site.path>
   </properties>
@@ -105,12 +109,17 @@
       <dependency>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-lang3</artifactId>
-        <version>3.1</version>
+        <version>${commonsLang3Version}</version>
       </dependency>
       <dependency>
         <groupId>commons-io</groupId>
         <artifactId>commons-io</artifactId>
-        <version>2.2</version>
+        <version>${commonsIoVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>commons-lang</groupId>
+        <artifactId>commons-lang</artifactId>
+        <version>${commonsLangVersion}</version>
       </dependency>
       <dependency>
         <groupId>org.apache.maven.surefire</groupId>
@@ -215,7 +224,13 @@
       <dependency>
         <groupId>org.apache.maven.shared</groupId>
         <artifactId>maven-shared-utils</artifactId>
-        <version>0.9</version>
+        <version>${mavenSharedUtilsVersion}</version>
+        <exclusions>
+          <exclusion>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+          </exclusion>
+        </exclusions>
       </dependency>
       <dependency>
         <groupId>org.apache.maven.shared</groupId>
@@ -377,7 +392,7 @@
         </plugin>
         <plugin>
           <artifactId>maven-shade-plugin</artifactId>
-          <version>1.5</version>
+          <version>3.0.0</version>
         </plugin>
         <plugin>
           <artifactId>maven-plugin-plugin</artifactId>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/surefire-api/pom.xml
----------------------------------------------------------------------
diff --git a/surefire-api/pom.xml b/surefire-api/pom.xml
index 7e407d2..96c5be3 100644
--- a/surefire-api/pom.xml
+++ b/surefire-api/pom.xml
@@ -40,6 +40,11 @@
       <groupId>org.apache.maven.shared</groupId>
       <artifactId>maven-shared-utils</artifactId>
     </dependency>
+    <dependency>
+      <groupId>com.google.code.findbugs</groupId>
+      <artifactId>jsr305</artifactId>
+      <scope>provided</scope>
+    </dependency>
   </dependencies>
 
   <build>
@@ -74,7 +79,6 @@
               <artifactSet>
                 <includes>
                   <include>org.apache.maven.shared:maven-shared-utils</include>
-                  <include>commons-lang:commons-lang</include>
                 </includes>
               </artifactSet>
               <relocations>
@@ -82,10 +86,6 @@
                   <pattern>org.apache.maven.shared</pattern>
                   
<shadedPattern>org.apache.maven.surefire.shade.org.apache.maven.shared</shadedPattern>
                 </relocation>
-                <relocation>
-                  <pattern>org.apache.commons.lang</pattern>
-                  
<shadedPattern>org.apache.maven.surefire.shade.org.apache.commons.lang</shadedPattern>
-                </relocation>
               </relocations>
             </configuration>
           </execution>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
----------------------------------------------------------------------
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
index ed7d4fa..49179c3 100644
--- 
a/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
@@ -493,7 +493,7 @@ public final class CommandReader
                 {
                     Runtime.getRuntime().halt( 1 );
                 }
-                // else is default: should not happen; otherwise you missed 
enum case
+                // else is default: other than Shutdown.DEFAULT should not 
happen; otherwise you missed enum case
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/surefire-booter/pom.xml
----------------------------------------------------------------------
diff --git a/surefire-booter/pom.xml b/surefire-booter/pom.xml
index b79cceb..307f77d 100644
--- a/surefire-booter/pom.xml
+++ b/surefire-booter/pom.xml
@@ -36,6 +36,10 @@
       <groupId>org.apache.maven.surefire</groupId>
       <artifactId>surefire-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+    </dependency>
   </dependencies>
 
   <build>
@@ -54,6 +58,12 @@
           <includes>
             <include>**/JUnit4SuiteTest.java</include>
           </includes>
+          <systemProperties>
+            <property>
+              <name>surefire.ppid</name>
+              <value>$PPID</value>
+            </property>
+          </systemProperties>
         </configuration>
       </plugin>
       <plugin>
@@ -69,11 +79,16 @@
               <minimizeJar>true</minimizeJar>
               <artifactSet>
                 <includes>
+                  <include>org.apache.maven.shared:maven-shared-utils</include>
                   <include>commons-lang:commons-lang</include>
                 </includes>
               </artifactSet>
               <relocations>
                 <relocation>
+                  <pattern>org.apache.maven.shared</pattern>
+                  
<shadedPattern>org.apache.maven.surefire.shade.org.apache.maven.shared</shadedPattern>
+                </relocation>
+                <relocation>
                   <pattern>org.apache.commons.lang</pattern>
                   
<shadedPattern>org.apache.maven.surefire.shade.org.apache.commons.lang</shadedPattern>
                 </relocation>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
----------------------------------------------------------------------
diff --git 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
index 1e3863e..ec67faa 100644
--- 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
+++ 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
@@ -37,7 +37,9 @@ import java.lang.reflect.InvocationTargetException;
 import java.security.AccessControlException;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.Semaphore;
@@ -207,12 +209,51 @@ public final class ForkedBooter
         COMMAND_READER.addShutdownListener( createExitHandler() );
         AtomicBoolean pingDone = new AtomicBoolean( true );
         COMMAND_READER.addNoopListener( createPingHandler( pingDone ) );
-        Runnable pingJob = createPingJob( pingDone );
         ScheduledExecutorService pingScheduler = createPingScheduler();
-        pingScheduler.scheduleAtFixedRate( pingJob, 0, 
PING_TIMEOUT_IN_SECONDS, SECONDS );
+        Future<PpidChecker> checker = pingScheduler.submit( 
createProcessCheckerJob() );
+        pingScheduler.scheduleWithFixedDelay( processCheckerJob( checker ), 
0L, 1L, SECONDS );
+        pingScheduler.scheduleAtFixedRate( createPingJob( pingDone, checker ), 
0L, PING_TIMEOUT_IN_SECONDS, SECONDS );
         return pingScheduler;
     }
 
+    private static Runnable processCheckerJob( final Future<PpidChecker> 
processChecker )
+    {
+        return new Runnable()
+        {
+            @Override
+            public void run()
+            {
+                if ( processChecker.isDone() && !processChecker.isCancelled() )
+                {
+                    try
+                    {
+                        PpidChecker checker = processChecker.get();
+                        if ( checker.canUse() && 
!checker.isParentProcessAlive() )
+                        {
+                            kill();
+                        }
+                    }
+                    catch ( Exception e )
+                    {
+                        // nothing to do
+                    }
+                }
+            }
+        };
+    }
+
+    private static Callable<PpidChecker> createProcessCheckerJob()
+    {
+        return new Callable<PpidChecker>()
+        {
+            @Override
+            public PpidChecker call() throws Exception
+            {
+                return new PpidChecker();
+            }
+        };
+    }
+
     private static CommandListener createPingHandler( final AtomicBoolean 
pingDone )
     {
         return new CommandListener()
@@ -246,13 +287,17 @@ public final class ForkedBooter
         };
     }
 
-    private static Runnable createPingJob( final AtomicBoolean pingDone  )
+    private static Runnable createPingJob( final AtomicBoolean pingDone, final 
Future<PpidChecker> processChecker  )
     {
         return new Runnable()
         {
             @Override
             public void run()
             {
+                if ( canUseNewPingMechanism( processChecker ) )
+                {
+                    return;
+                }
                 boolean hasPing = pingDone.getAndSet( false );
                 if ( !hasPing )
                 {
@@ -262,6 +307,18 @@ public final class ForkedBooter
         };
     }
 
+    private static boolean canUseNewPingMechanism( Future<PpidChecker> 
processChecker )
+    {
+        try
+        {
+            return ( !processChecker.isDone() || processChecker.get().canUse() 
) && !processChecker.isCancelled();
+        }
+        catch ( Exception e )
+        {
+            return false;
+        }
+    }
+
     private static void encodeAndWriteToOutput( String string, PrintStream out 
)
     {
         byte[] encodeBytes = encodeStringForForkCommunication( string );
@@ -357,8 +414,8 @@ public final class ForkedBooter
     {
         ThreadFactory threadFactory = newDaemonThreadFactory( "ping-" + 
PING_TIMEOUT_IN_SECONDS + "s" );
         ScheduledThreadPoolExecutor executor = new 
ScheduledThreadPoolExecutor( 1, threadFactory );
-        executor.setMaximumPoolSize( 1 );
-        executor.prestartCoreThread();
+        executor.setKeepAliveTime( 3L, SECONDS );
+        executor.setMaximumPoolSize( 3 );
         return executor;
     }
 

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PpidChecker.java
----------------------------------------------------------------------
diff --git 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PpidChecker.java
 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PpidChecker.java
new file mode 100644
index 0000000..24c29bf
--- /dev/null
+++ 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/PpidChecker.java
@@ -0,0 +1,282 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * 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.shared.utils.io.IOUtil;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.management.ManagementFactory;
+import java.util.Locale;
+import java.util.StringTokenizer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static org.apache.commons.lang.SystemUtils.IS_OS_UNIX;
+import static org.apache.commons.lang.SystemUtils.IS_OS_WINDOWS;
+import static 
org.apache.maven.surefire.booter.ProcessInfo.INVALID_PROCESS_INFO;
+
+/**
+ * Recognizes PPID. Determines lifetime of parent process.
+ *
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.20.1
+ */
+final class PpidChecker
+{
+    private static final long MINUTES_TO_SECONDS = 60L;
+
+    private static final long HOURS_TO_SECONDS = 60L * MINUTES_TO_SECONDS;
+
+    private static final long DAYS_TO_SECONDS = 24L * HOURS_TO_SECONDS;
+
+    private static final String WMIC_PPID = "ParentProcessId";
+
+    private static final String WMIC_CREATION_DATE = "CreationDate";
+
+    private static final String WINDOWS_CMD =
+            "wmic process where (ProcessId=%s) get " + WMIC_CREATION_DATE + ", 
" + WMIC_PPID;
+
+    private static final String UNIX_CMD = "ps -o etime -p %s";
+
+    private static final Pattern UNIX_CMD_OUT_PATTERN =
+            Pattern.compile( 
"^((([\\d]+)-)?([\\d]{2}))?[:]?([\\d]{2}):([\\d]{2})$" );
+
+    private static final Pattern NUMBER_PATTERN = Pattern.compile( "^[\\d]+$" 
);
+
+    private final ProcessInfo parentProcessInfo;
+
+    PpidChecker()
+    {
+        ProcessInfo parentProcess = INVALID_PROCESS_INFO;
+        if ( IS_OS_WINDOWS )
+        {
+            String pid = pid();
+            if ( pid != null )
+            {
+                ProcessInfo currentProcessInfo = windows( pid );
+                String ppid = currentProcessInfo.getPPID();
+                parentProcess = currentProcessInfo.isValid() ? windows( ppid ) 
: INVALID_PROCESS_INFO;
+            }
+        }
+        else if ( IS_OS_UNIX )
+        {
+            parentProcess = unix( System.getProperty( "surefire.ppid" ) );
+        }
+        parentProcessInfo = parentProcess.isValid() ? parentProcess : 
INVALID_PROCESS_INFO;
+    }
+
+    boolean canUse()
+    {
+        return parentProcessInfo.isValid();
+    }
+
+    boolean isParentProcessAlive()
+    {
+        if ( !canUse() )
+        {
+            throw new IllegalStateException();
+        }
+
+        if ( IS_OS_WINDOWS )
+        {
+            ProcessInfo pp = windows( parentProcessInfo.getPID() );
+            // let's compare creation time, should be same unless killed or 
PPID is reused by OS into another process
+            return pp.isValid() && parentProcessInfo.getTime().equals( 
pp.getTime() );
+        }
+        else if ( IS_OS_UNIX )
+        {
+            ProcessInfo pp = unix( parentProcessInfo.getPID() );
+            // let's compare elapsed time, should be greater or equal if 
parent process is the same and still alive
+            return pp.isValid() && (Long) pp.getTime() >= (Long) 
parentProcessInfo.getTime();
+        }
+
+        throw new IllegalStateException();
+    }
+
+    static long fromDays( Matcher matcher )
+    {
+        String s = matcher.group( 3 );
+        return s == null ? 0L : DAYS_TO_SECONDS * Byte.parseByte( s );
+    }
+
+    static long fromHours( Matcher matcher )
+    {
+        String s = matcher.group( 4 );
+        return s == null ? 0L : HOURS_TO_SECONDS * Byte.parseByte( s );
+    }
+
+    private static long fromMinutes( Matcher matcher )
+    {
+        String s = matcher.group( 5 );
+        return s == null ? 0L : MINUTES_TO_SECONDS * Byte.parseByte( s );
+    }
+
+    private static long fromSeconds( Matcher matcher )
+    {
+        String s = matcher.group( 6 );
+        return s == null ? 0L : Byte.parseByte( s );
+    }
+
+    // http://manpages.ubuntu.com/manpages/precise/en/man1/ps.1.html
+    // 
https://www.freebsd.org/cgi/man.cgi?query=ps&manpath=SuSE+Linux/i386+11.3
+
+    // http://manpages.ubuntu.com/manpages/xenial/man1/ps.1.html
+    // etime       ELAPSED   elapsed time since the process was started, in
+    //             the form [[DD-]hh:]mm:ss.
+
+    static ProcessInfo unix( String pid )
+    {
+        String[] cmd = { "/bin/sh", "-c", String.format( Locale.ROOT, 
UNIX_CMD, pid ) };
+        ProcessBuilder probuilder = new ProcessBuilder( cmd );
+        Process p = null;
+        BufferedReader reader = null;
+        ProcessInfo processInfo = INVALID_PROCESS_INFO;
+        try
+        {
+            p = probuilder.start();
+            reader = new BufferedReader( new InputStreamReader( 
p.getInputStream() ) );
+            for ( String line = reader.readLine(); line != null; line = 
reader.readLine() )
+            {
+                line = line.trim();
+                if ( !line.isEmpty() )
+                {
+                    Matcher matcher = UNIX_CMD_OUT_PATTERN.matcher( line );
+                    if ( matcher.matches() )
+                    {
+                        long pidUptime = fromDays( matcher )
+                                                 + fromHours( matcher )
+                                                 + fromMinutes( matcher )
+                                                 + fromSeconds( matcher );
+                        processInfo = new ProcessInfo( pid, pidUptime, null );
+                    }
+                }
+            }
+            p.waitFor();
+            return processInfo;
+        }
+        catch ( IOException e )
+        {
+            return processInfo;
+        }
+        catch ( InterruptedException e )
+        {
+            return processInfo;
+        }
+        finally
+        {
+            IOUtil.close( reader );
+            if ( p != null )
+            {
+                p.destroy();
+            }
+        }
+    }
+
+    static String pid()
+    {
+        String processName = ManagementFactory.getRuntimeMXBean().getName();
+        if ( processName != null && processName.contains( "@" ) )
+        {
+            String pid = processName.substring( 0, processName.indexOf( '@' ) 
).trim();
+            if ( NUMBER_PATTERN.matcher( pid ).matches() )
+            {
+                return pid;
+            }
+        }
+        return null;
+    }
+
+    static ProcessInfo windows( String pid )
+    {
+        Process p = null;
+        BufferedReader reader = null;
+        ProcessInfo processInfo = INVALID_PROCESS_INFO;
+        try
+        {
+            String[] cmd = { "CMD", "/A/C", String.format( Locale.ROOT, 
WINDOWS_CMD, pid ) };
+            ProcessBuilder probuilder = new ProcessBuilder( cmd );
+            p = probuilder.start();
+            reader = new BufferedReader( new InputStreamReader( 
p.getInputStream() ) );
+            boolean hasHeader = false;
+            boolean isStartTimestampFirst = false;
+            for ( String line = reader.readLine(); line != null; line = 
reader.readLine() )
+            {
+                line = line.trim();
+
+                if ( line.isEmpty() )
+                {
+                    continue;
+                }
+
+                if ( hasHeader )
+                {
+                    StringTokenizer args = new StringTokenizer( line );
+                    if ( args.countTokens() == 2 )
+                    {
+                        if ( isStartTimestampFirst )
+                        {
+                            String startTimestamp = args.nextToken();
+                            String ppid = args.nextToken();
+                            processInfo = new ProcessInfo( pid, 
startTimestamp, ppid );
+                        }
+                        else
+                        {
+                            String ppid = args.nextToken();
+                            String startTimestamp = args.nextToken();
+                            processInfo = new ProcessInfo( pid, 
startTimestamp, ppid );
+                        }
+                    }
+                }
+                else
+                {
+                    StringTokenizer args = new StringTokenizer( line );
+                    if ( args.countTokens() == 2 )
+                    {
+                        String arg0 = args.nextToken();
+                        String arg1 = args.nextToken();
+                        isStartTimestampFirst = WMIC_CREATION_DATE.equals( 
arg0 );
+                        hasHeader = isStartTimestampFirst || WMIC_PPID.equals( 
arg0 );
+                        hasHeader &= WMIC_CREATION_DATE.equals( arg1 ) || 
WMIC_PPID.equals( arg1 );
+                    }
+                }
+            }
+            p.waitFor();
+            return processInfo;
+        }
+        catch ( IOException e )
+        {
+            return processInfo;
+        }
+        catch ( InterruptedException e )
+        {
+            return processInfo;
+        }
+        finally
+        {
+            IOUtil.close( reader );
+            if ( p != null )
+            {
+                p.destroy();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessInfo.java
----------------------------------------------------------------------
diff --git 
a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessInfo.java
 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessInfo.java
new file mode 100644
index 0000000..addee1d
--- /dev/null
+++ 
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProcessInfo.java
@@ -0,0 +1,62 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * 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.
+ */
+
+/**
+ * PID, PPID, elapsed time (Unix) or start time (Windows).
+ *
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.20.1
+ */
+final class ProcessInfo
+{
+    static final ProcessInfo INVALID_PROCESS_INFO = new ProcessInfo( null, 
null, null );
+
+    private final String pid;
+    private final Object time;
+    private final String ppid;
+
+    ProcessInfo( String pid, Object time, String ppid )
+    {
+        this.pid = pid;
+        this.time = time;
+        this.ppid = ppid;
+    }
+
+    boolean isValid()
+    {
+        return pid != null && time != null;
+    }
+
+    String getPID()
+    {
+        return pid;
+    }
+
+    Object getTime()
+    {
+        return time;
+    }
+
+    String getPPID()
+    {
+        return ppid;
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
----------------------------------------------------------------------
diff --git 
a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
 
b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
index 2bdcf21..b08423f 100644
--- 
a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
+++ 
b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
@@ -34,7 +34,8 @@ import org.junit.runners.Suite;
     ClasspathTest.class,
     CommandReaderTest.class,
     PropertiesWrapperTest.class,
-    SurefireReflectorTest.class
+    SurefireReflectorTest.class,
+    PpidCheckerTest.class
 } )
 @RunWith( Suite.class )
 public class JUnit4SuiteTest

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/PpidCheckerTest.java
----------------------------------------------------------------------
diff --git 
a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/PpidCheckerTest.java
 
b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/PpidCheckerTest.java
new file mode 100644
index 0000000..ee7ecee
--- /dev/null
+++ 
b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/PpidCheckerTest.java
@@ -0,0 +1,127 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * 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.junit.Test;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.commons.lang.SystemUtils.IS_OS_UNIX;
+import static org.apache.commons.lang.SystemUtils.IS_OS_WINDOWS;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+/**
+ * Testing {@link PpidChecker} on a platform.
+ *
+ * @author <a href="mailto:tibordig...@apache.org";>Tibor Digana (tibor17)</a>
+ * @since 2.20.1
+ */
+public class PpidCheckerTest
+{
+    @Test
+    public void shouldHavePid()
+    {
+        String pid = PpidChecker.pid();
+
+        assertThat( pid )
+                .isNotNull();
+
+        assertThat( pid )
+                .matches( "^[\\d]+$" );
+    }
+
+    @Test
+    public void shouldHavePpidAsWindows()
+    {
+        assumeTrue( IS_OS_WINDOWS );
+
+        ProcessInfo processInfo = PpidChecker.windows( PpidChecker.pid() );
+
+        assertThat( processInfo )
+                .isNotNull();
+
+        assertThat( processInfo.getPID() )
+                .isNotNull();
+
+        assertThat( processInfo.getPID() )
+                .matches( "^[\\d]+$" );
+
+        assertThat( processInfo.getTime() )
+                .isNotNull();
+
+        processInfo = PpidChecker.windows( processInfo.getPID() );
+
+        assertThat( processInfo.getPID() )
+                .isNotNull();
+
+        assertThat( processInfo.getPID() )
+                .matches( "^[\\d]+$" );
+
+        assertThat( processInfo.getTime() )
+                .isNotNull();
+    }
+
+    @Test
+    public void shouldHavePpidAsUnix()
+    {
+        assumeTrue( IS_OS_UNIX );
+
+        ProcessInfo processInfo = PpidChecker.unix( PpidChecker.pid() );
+
+        assertThat( processInfo )
+                .isNotNull();
+
+        assertThat( processInfo.getPID() )
+                .isNotNull();
+
+        assertThat( processInfo.getPID() )
+                .matches( "^[\\d]+$" );
+
+        assertThat( processInfo.getTime() )
+                .isNotNull();
+
+        processInfo = PpidChecker.unix( processInfo.getPID() );
+
+        assertThat( processInfo.getPID() )
+                .isNotNull();
+
+        assertThat( processInfo.getPID() )
+                .matches( "^[\\d]+$" );
+
+        assertThat( processInfo.getTime() )
+                .isNotNull();
+    }
+
+    @Test
+    public void shouldFindAliveParentProcess()
+            throws InterruptedException
+    {
+        PpidChecker checker = new PpidChecker();
+
+        assertThat( checker.canUse() )
+                .isTrue();
+
+        TimeUnit.MILLISECONDS.sleep( 100L );
+
+        assertThat( checker.isParentProcessAlive() )
+                .isTrue();
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/b9034a3b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1295AttributeJvmCrashesToTestsIT.java
----------------------------------------------------------------------
diff --git 
a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1295AttributeJvmCrashesToTestsIT.java
 
b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1295AttributeJvmCrashesToTestsIT.java
index 1fa88f6..62c37f0 100644
--- 
a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1295AttributeJvmCrashesToTestsIT.java
+++ 
b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1295AttributeJvmCrashesToTestsIT.java
@@ -27,8 +27,10 @@ import org.junit.Before;
 import org.junit.Test;
 
 import java.util.Iterator;
-import java.util.Locale;
 
+import static org.apache.commons.lang.SystemUtils.IS_OS_LINUX;
+import static org.apache.commons.lang.SystemUtils.IS_OS_MAC;
+import static org.apache.commons.lang.SystemUtils.IS_OS_MAC_OSX;
 import static org.fest.assertions.Assertions.assertThat;
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
@@ -46,8 +48,7 @@ public class Surefire1295AttributeJvmCrashesToTestsIT
     @Before
     public void skipWindows()
     {
-        String os = System.getProperty( "os.name" ).toLowerCase( Locale.ROOT );
-        assumeTrue( os.equals( "mac os x" ) || os.equals( "linux" ) /*||  
os.contains( "windows" )*/ );
+        assumeTrue( IS_OS_LINUX || IS_OS_MAC || IS_OS_MAC_OSX );
     }
 
     @Test

Reply via email to