You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by kr...@apache.org on 2013/01/21 20:08:11 UTC

[1/2] git commit: [SUREFIRE-946] prevent hanging main process if forked process was killed (softly)

[SUREFIRE-946] prevent hanging main process if forked process was killed (softly)


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

Branch: refs/heads/master
Commit: 463f37b30dcc59de7c8550774c4dff19c96112fa
Parents: 9cd2acb
Author: agudian <an...@gmail.com>
Authored: Sat Jan 5 21:13:12 2013 +0100
Committer: Kristian Rosenvold <kr...@apache.org>
Committed: Mon Jan 21 18:10:36 2013 +0100

----------------------------------------------------------------------
 .../plugin/surefire/booterclient/ForkStarter.java  |   20 +-
 .../lazytestprovider/TestProvidingInputStream.java |  248 +++++++-------
 .../surefire/booterclient/output/ForkClient.java   |   12 -
 pom.xml                                            |    2 +-
 .../maven/surefire/booter/ForkingRunListener.java  |    2 -
 .../apache/maven/surefire/booter/ForkedBooter.java |   74 +----
 .../maven/surefire/its/CrashDetectionIT.java       |    5 +
 .../maven/surefire/its/fixture/MavenLauncher.java  |    6 +-
 .../surefire/its/fixture/SurefireLauncher.java     |    7 +-
 ...Surefire946KillMainProcessInReusableForkIT.java |   71 ++++
 .../test/java/junit44/environment/BasicTest.java   |    8 +-
 .../pom.xml                                        |   64 ++++
 .../test/java/junit44/environment/Basic01Test.java |   26 ++
 .../test/java/junit44/environment/Basic02Test.java |   26 ++
 .../test/java/junit44/environment/Basic03Test.java |   26 ++
 .../test/java/junit44/environment/Basic04Test.java |   26 ++
 .../test/java/junit44/environment/Basic05Test.java |   26 ++
 .../test/java/junit44/environment/Basic06Test.java |   26 ++
 .../test/java/junit44/environment/Basic07Test.java |   26 ++
 .../test/java/junit44/environment/Basic08Test.java |   26 ++
 .../test/java/junit44/environment/Basic09Test.java |   26 ++
 .../test/java/junit44/environment/Basic10Test.java |   26 ++
 .../surefire-946-self-destruct-plugin/pom.xml      |   51 +++
 .../surefire/selfdestruct/SelfDestructMojo.java    |  142 ++++++++
 24 files changed, 761 insertions(+), 211 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/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 892c6a9..164f8c7 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
@@ -86,7 +86,7 @@ public class ForkStarter
      * Closes an InputStream
      */
     private final class InputStreamCloser
-        extends Thread
+        implements Runnable
     {
         private InputStream testProvidingInputStream;
 
@@ -95,8 +95,7 @@ public class ForkStarter
             this.testProvidingInputStream = testProvidingInputStream;
         }
 
-        @Override
-        public void run()
+        public synchronized void run()
         {
             if ( testProvidingInputStream != null )
             {
@@ -108,6 +107,7 @@ public class ForkStarter
                 {
                     // ignore
                 }
+                testProvidingInputStream = null;
             }
         }
     }
@@ -412,16 +412,18 @@ public class ForkStarter
                                                  startupConfiguration.getClassLoaderConfiguration(),
                                                  startupConfiguration.isShadefire(), threadNumber );
 
-        final InputStreamCloser inputStreamCloserHook;
+        final InputStreamCloser inputStreamCloser;
+        final Thread inputStreamCloserHook;
         if ( testProvidingInputStream != null )
         {
             testProvidingInputStream.setFlushReceiverProvider( cli );
-
-            inputStreamCloserHook = new InputStreamCloser( testProvidingInputStream );
+            inputStreamCloser = new InputStreamCloser( testProvidingInputStream );
+            inputStreamCloserHook = new Thread( inputStreamCloser );
             addShutDownHook( inputStreamCloserHook );
         }
         else
         {
+            inputStreamCloser = null;
             inputStreamCloserHook = null;
         }
 
@@ -446,7 +448,7 @@ public class ForkStarter
             final int timeout = forkedProcessTimeoutInSeconds > 0 ? forkedProcessTimeoutInSeconds : 0;
             final int result =
                 CommandLineUtils.executeCommandLine( cli, testProvidingInputStream, threadedStreamConsumer,
-                                                     threadedStreamConsumer, timeout );
+                                                     threadedStreamConsumer, timeout, inputStreamCloser );
             if ( result != RunResult.SUCCESS )
             {
                 throw new SurefireBooterForkException( "Error occurred in starting fork, check output in log" );
@@ -465,10 +467,10 @@ public class ForkStarter
         finally
         {
             threadedStreamConsumer.close();
-            if ( inputStreamCloserHook != null )
+            if ( inputStreamCloser != null )
             {
+                inputStreamCloser.run();
                 removeShutdownHook( inputStreamCloserHook );
-                inputStreamCloserHook.run();
             }
             if ( runResult == null )
             {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
index 4f5f6a4..df14d35 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
@@ -1,125 +1,125 @@
-package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
-
-/*
- * 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.io.IOException;
-import java.io.InputStream;
-import java.util.Queue;
-import java.util.concurrent.Semaphore;
-
-/**
- * An {@link InputStream} that, when read, provides test class names out of a queue.
- * <p/>
- * The Stream provides only one test at a time, but only after {@link #provideNewTest()} has been invoked.
- * <p/>
- * After providing each test class name, followed by a newline character, a flush is performed on the
- * {@link FlushReceiver} provided by the {@link FlushReceiverProvider} that can be set using
- * {@link #setFlushReceiverProvider(FlushReceiverProvider)}.
- * 
- * @author Andreas Gudian
- */
-public class TestProvidingInputStream
-    extends InputStream
-{
-    private final Queue<String> testItemQueue;
-
-    private byte[] currentBuffer;
-
-    private int currentPos;
-
-    private Semaphore semaphore = new Semaphore( 0 );
-
-    private FlushReceiverProvider flushReceiverProvider;
-
-    private volatile boolean closed = false;
-
-    /**
-     * C'tor
-     * 
-     * @param testItemQueue source of the tests to be read from this stream
-     */
-    public TestProvidingInputStream( Queue<String> testItemQueue )
-    {
-        this.testItemQueue = testItemQueue;
-    }
-
-    /**
-     * @param flushReceiverProvider the provider for a flush receiver.
-     */
-    public void setFlushReceiverProvider( FlushReceiverProvider flushReceiverProvider )
-    {
-        this.flushReceiverProvider = flushReceiverProvider;
-    }
-
-    @Override
-    public synchronized int read()
-        throws IOException
-    {
-        if ( null == currentBuffer )
-        {
-            if ( null != flushReceiverProvider && null != flushReceiverProvider.getFlushReceiver() )
-            {
-                flushReceiverProvider.getFlushReceiver().flush();
-            }
-
-            semaphore.acquireUninterruptibly();
-
-            if ( closed )
-            {
-                return -1;
-            }
-
-            String currentElement = testItemQueue.poll();
-            if ( null != currentElement )
-            {
-                currentBuffer = currentElement.getBytes();
-                currentPos = 0;
-            }
-            else
-            {
-                return -1;
-            }
-        }
-
-        if ( currentPos < currentBuffer.length )
-        {
-            return ( currentBuffer[currentPos++] & 0xff );
-        }
-        else
-        {
-            currentBuffer = null;
-            return ( '\n' & 0xff );
-        }
-    }
-
-    /**
-     * Signal that a new test is to be provided.
-     */
-    public void provideNewTest()
-    {
-        semaphore.release();
-    }
-
-    public void close()
-    {
-        closed = true;
-        semaphore.release();
-    }
+package org.apache.maven.plugin.surefire.booterclient.lazytestprovider;
+
+/*
+ * 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.io.IOException;
+import java.io.InputStream;
+import java.util.Queue;
+import java.util.concurrent.Semaphore;
+
+/**
+ * An {@link InputStream} that, when read, provides test class names out of a queue.
+ * <p/>
+ * The Stream provides only one test at a time, but only after {@link #provideNewTest()} has been invoked.
+ * <p/>
+ * After providing each test class name, followed by a newline character, a flush is performed on the
+ * {@link FlushReceiver} provided by the {@link FlushReceiverProvider} that can be set using
+ * {@link #setFlushReceiverProvider(FlushReceiverProvider)}.
+ * 
+ * @author Andreas Gudian
+ */
+public class TestProvidingInputStream
+    extends InputStream
+{
+    private final Queue<String> testItemQueue;
+
+    private byte[] currentBuffer;
+
+    private int currentPos;
+
+    private Semaphore semaphore = new Semaphore( 0 );
+
+    private FlushReceiverProvider flushReceiverProvider;
+
+    private boolean closed = false;
+
+    /**
+     * C'tor
+     * 
+     * @param testItemQueue source of the tests to be read from this stream
+     */
+    public TestProvidingInputStream( Queue<String> testItemQueue )
+    {
+        this.testItemQueue = testItemQueue;
+    }
+
+    /**
+     * @param flushReceiverProvider the provider for a flush receiver.
+     */
+    public void setFlushReceiverProvider( FlushReceiverProvider flushReceiverProvider )
+    {
+        this.flushReceiverProvider = flushReceiverProvider;
+    }
+
+    @Override
+    public synchronized int read()
+        throws IOException
+    {
+        if ( null == currentBuffer )
+        {
+            if ( null != flushReceiverProvider && null != flushReceiverProvider.getFlushReceiver() )
+            {
+                flushReceiverProvider.getFlushReceiver().flush();
+            }
+
+            semaphore.acquireUninterruptibly();
+
+            if ( closed )
+            {
+                return -1;
+            }
+
+            String currentElement = testItemQueue.poll();
+            if ( null != currentElement )
+            {
+                currentBuffer = currentElement.getBytes();
+                currentPos = 0;
+            }
+            else
+            {
+                return -1;
+            }
+        }
+
+        if ( currentPos < currentBuffer.length )
+        {
+            return ( currentBuffer[currentPos++] & 0xff );
+        }
+        else
+        {
+            currentBuffer = null;
+            return ( '\n' & 0xff );
+        }
+    }
+
+    /**
+     * Signal that a new test is to be provided.
+     */
+    public void provideNewTest()
+    {
+        semaphore.release();
+    }
+
+    public void close()
+    {
+        closed = true;
+        semaphore.release();
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/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 932f148..fa2f41d 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
@@ -156,11 +156,7 @@ public class ForkClient
                 case ForkingRunListener.BOOTERCODE_ERROR:
                     errorInFork = deserializeStackStraceWriter( new StringTokenizer( remaining, "," ) );
                     break;
-                case ForkingRunListener.BOOTERCODE_CRASH:
-                    closeTestProvidingInputStream();
-                    break;
                 case ForkingRunListener.BOOTERCODE_BYE:
-                    closeTestProvidingInputStream();
                     saidGoodBye = true;
                     break;
                 default:
@@ -177,14 +173,6 @@ public class ForkClient
         }
     }
 
-    private void closeTestProvidingInputStream()
-    {
-        if ( null != testProvidingInputStream )
-        {
-            testProvidingInputStream.close();
-        }
-    }
-
     public void consumeMultiLineContent( String s )
         throws IOException
     {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 0fda14a..22ab636 100644
--- a/pom.xml
+++ b/pom.xml
@@ -241,7 +241,7 @@
       <dependency>
         <groupId>org.apache.maven.shared</groupId>
         <artifactId>maven-shared-utils</artifactId>
-        <version>0.2</version>
+        <version>0.3-SNAPSHOT</version>
       </dependency>
       <dependency>
         <groupId>org.apache.maven.shared</groupId>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/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 6ec7223..fa4e9cf 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
@@ -69,8 +69,6 @@ public class ForkingRunListener
 
     public static final byte BOOTERCODE_TEST_SKIPPED = (byte) '9';
 
-    public static final byte BOOTERCODE_CRASH = (byte) 'C';
-
     public static final byte BOOTERCODE_TEST_ASSUMPTIONFAILURE = (byte) 'G';
 
     public static final byte BOOTERCODE_CONSOLE = (byte) 'H';

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/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 5f711f4..c06297a 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
@@ -21,7 +21,6 @@ package org.apache.maven.surefire.booter;
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintStream;
 import java.lang.reflect.InvocationTargetException;
@@ -36,7 +35,7 @@ import org.apache.maven.surefire.util.LazyTestsToRun;
  * <p/>
  * Deals with deserialization of the booter wire-level protocol
  * <p/>
- * 
+ *
  * @author Jason van Zyl
  * @author Emmanuel Venisse
  * @author Kristian Rosenvold
@@ -44,58 +43,10 @@ import org.apache.maven.surefire.util.LazyTestsToRun;
 public class ForkedBooter
 {
 
-    private final static long SYSTEM_EXIT_TIMEOUT = 30 * 1000;
-
-    private final static String GOODBYE_MESSAGE = ( (char) ForkingRunListener.BOOTERCODE_BYE ) + ",0,BYE!";
-
-    private final static String CRASH_MESSAGE = ( (char) ForkingRunListener.BOOTERCODE_CRASH ) + ",0,F!";
-
-    private static boolean sayGoodbye = false;
-
-    private static final class GoodbyeReporterAndStdStreamCloser
-        implements Runnable
-    {
-
-        private InputStream originalIn;
-
-        private PrintStream originalOut;
-
-        public GoodbyeReporterAndStdStreamCloser()
-        {
-            this.originalIn = System.in;
-            this.originalOut = System.out;
-        }
-
-        public void run()
-        {
-            try
-            {
-                originalIn.close();
-            }
-            catch ( IOException ignore )
-            {
-            }
-
-            if ( sayGoodbye )
-            {
-                originalOut.println( GOODBYE_MESSAGE );
-            }
-            else
-            {
-                originalOut.println( CRASH_MESSAGE );
-            }
-
-            originalOut.flush();
-            originalOut.close();
-        }
-    }
-
     /**
      * This method is invoked when Surefire is forked - this method parses and organizes the arguments passed to it and
-     * then calls the Surefire class' run method.
-     * <p/>
-     * The system exit code will be 1 if an exception is thrown.
-     * 
+     * then calls the Surefire class' run method. <p/> The system exit code will be 1 if an exception is thrown.
+     *
      * @param args Commandline arguments
      * @throws Throwable Upon throwables
      */
@@ -103,9 +54,6 @@ public class ForkedBooter
         throws Throwable
     {
         final PrintStream originalOut = System.out;
-
-        Runtime.getRuntime().addShutdownHook( new Thread( new GoodbyeReporterAndStdStreamCloser() ) );
-
         try
         {
             if ( args.length > 1 )
@@ -123,8 +71,8 @@ public class ForkedBooter
             boolean readTestsFromInputStream = providerConfiguration.isReadTestsFromInStream();
 
             final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
-            final ClassLoader testClassLoader =
-                classpathConfiguration.createForkingTestClassLoader( startupConfiguration.isManifestOnlyJarRequestedAndUsable() );
+            final ClassLoader testClassLoader = classpathConfiguration.createForkingTestClassLoader(
+                startupConfiguration.isManifestOnlyJarRequestedAndUsable() );
 
             startupConfiguration.writeSurefireTestClasspathProperty();
 
@@ -144,7 +92,8 @@ public class ForkedBooter
 
             try
             {
-                runSuitesInProcess( testSet, testClassLoader, startupConfiguration, providerConfiguration, originalOut );
+                runSuitesInProcess( testSet, testClassLoader, startupConfiguration, providerConfiguration,
+                                    originalOut );
             }
             catch ( InvocationTargetException t )
             {
@@ -162,8 +111,10 @@ public class ForkedBooter
                 ForkingRunListener.encode( stringBuffer, stackTraceWriter, false );
                 originalOut.println( ( (char) ForkingRunListener.BOOTERCODE_ERROR ) + ",0," + stringBuffer.toString() );
             }
-
-            sayGoodbye = true;
+            // Say bye.
+            originalOut.println( ( (char) ForkingRunListener.BOOTERCODE_BYE ) + ",0,BYE!" );
+            originalOut.flush();
+            // noinspection CallToSystemExit
             exit( 0 );
         }
         catch ( Throwable t )
@@ -176,12 +127,15 @@ public class ForkedBooter
         }
     }
 
+    private final static long SYSTEM_EXIT_TIMEOUT = 30 * 1000;
+
     private static void exit( final int returnCode )
     {
         launchLastDitchDaemonShutdownThread( returnCode );
         System.exit( returnCode );
     }
 
+
     private static RunResult runSuitesInProcess( Object testSet, ClassLoader testsClassLoader,
                                                  StartupConfiguration startupConfiguration,
                                                  ProviderConfiguration providerConfiguration,

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/CrashDetectionIT.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/CrashDetectionIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/CrashDetectionIT.java
index 31e3e01..88e50f6 100644
--- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/CrashDetectionIT.java
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/CrashDetectionIT.java
@@ -36,4 +36,9 @@ public class CrashDetectionIT
     {
         unpack( "crash-detection" ).forkOncePerThread().threadCount( 1 ).maven().withFailure().executeTest();
     }
+
+    public void testHardCrashInReusableFork()
+    {
+        unpack( "crash-detection" ).forkOncePerThread().threadCount( 1 ).addGoal( "-DkillHard=true" ).maven().withFailure().executeTest();
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/MavenLauncher.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/MavenLauncher.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/MavenLauncher.java
index bbf910c..cc745de 100755
--- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/MavenLauncher.java
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/MavenLauncher.java
@@ -377,6 +377,10 @@ public class MavenLauncher
         return validator;
     }
 
+    public void setForkJvm( boolean forkJvm ) {
+        getVerifier().setForkJvm( forkJvm );
+    }
+
     private Verifier getVerifier()
     {
         if ( verifier == null )
@@ -392,7 +396,7 @@ public class MavenLauncher
         }
         return verifier;
     }
-
+    
     private File simpleExtractResources( Class<?> cl, String resourcePath )
     {
         if ( !resourcePath.startsWith( "/" ) )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java
index 84ea8b9..135216e 100755
--- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java
@@ -370,10 +370,15 @@ public class SurefireLauncher
         return "org.apache.maven.plugins:maven-surefire-report-plugin:" + getSurefireVersion() + ":" + goal;
     }
 
-
     public SurefireLauncher setTestToRun( String basicTest )
     {
         mavenLauncher.sysProp( "test", basicTest );
         return this;
     }
+
+    public SurefireLauncher setForkJvm( boolean forkJvm )
+    {
+        mavenLauncher.setForkJvm( true );
+        return this;
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire946KillMainProcessInReusableForkIT.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire946KillMainProcessInReusableForkIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire946KillMainProcessInReusableForkIT.java
new file mode 100644
index 0000000..60bcfb7
--- /dev/null
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire946KillMainProcessInReusableForkIT.java
@@ -0,0 +1,71 @@
+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.SurefireJUnit4IntegrationTestCase;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class Surefire946KillMainProcessInReusableForkIT
+    extends SurefireJUnit4IntegrationTestCase
+{
+
+    // there are 10 test classes that each would wait 2 seconds.
+    private static final int TEST_SLEEP_TIME = 2000;
+
+    @BeforeClass
+    public static void installSelfdestructPlugin()
+        throws Exception
+    {
+        unpack( Surefire946KillMainProcessInReusableForkIT.class, "surefire-946-self-destruct-plugin", "plugin" ).executeInstall();
+    }
+
+    @Test( timeout = 30000 )
+    public void testHalt()
+        throws Exception
+    {
+        doTest( "halt" );
+    }
+
+    @Test( timeout = 30000 )
+    public void testExit()
+        throws Exception
+    {
+        doTest( "exit" );
+    }
+
+    @Test( timeout = 30000 )
+    public void testInterrupt()
+        throws Exception
+    {
+        doTest( "interrupt" );
+    }
+
+    private void doTest( String method )
+    {
+        unpack( "surefire-946-killMainProcessInReusableFork" )
+            .sysProp( "selfdestruct.timeoutInMillis", "5000" )
+            .sysProp( "selfdestruct.method", method )
+            .sysProp( "testSleepTime", String.valueOf( TEST_SLEEP_TIME ) )
+            .addGoal( "org.apache.maven.plugins.surefire:maven-selfdestruct-plugin:selfdestruct" )
+            .setForkJvm( true )
+            .forkOncePerThread().threadCount( 1 ).maven().withFailure().executeTest();
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/resources/crash-detection/src/test/java/junit44/environment/BasicTest.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/crash-detection/src/test/java/junit44/environment/BasicTest.java b/surefire-integration-tests/src/test/resources/crash-detection/src/test/java/junit44/environment/BasicTest.java
index d6f0155..c2157dc 100644
--- a/surefire-integration-tests/src/test/resources/crash-detection/src/test/java/junit44/environment/BasicTest.java
+++ b/surefire-integration-tests/src/test/resources/crash-detection/src/test/java/junit44/environment/BasicTest.java
@@ -15,7 +15,13 @@ public class BasicTest
 
     @AfterClass
     public static void killTheVm(){
-        System.exit( 0 );
+        if ( Boolean.getBoolean( "killHard" ))
+        {
+            Runtime.getRuntime().halt( 0 );
+        }
+        else {
+            System.exit( 0 );
+        }
     }
 
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/pom.xml
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/pom.xml b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/pom.xml
new file mode 100644
index 0000000..eb0fe59
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/pom.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<groupId>org.apache.maven.plugins.surefire</groupId>
+	<artifactId>surefire-946</artifactId>
+	<version>1.0-SNAPSHOT</version>
+	<name>Tests killing the main maven process when using reusable forks</name>
+
+	<dependencies>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.4</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+				<plugin>
+					<groupId>org.apache.maven.plugins.surefire</groupId>
+					<artifactId>maven-selfdestruct-plugin</artifactId>
+					<version>0.1</version>
+					<configuration>
+						<timeoutInMillis>${selfdestruct.timeoutInMillis}</timeoutInMillis>
+						<method>${selfdestruct.method}</method>
+					</configuration>
+				</plugin>
+			<plugin>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<version>${surefire.version}</version>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<configuration>
+					<source>1.5</source>
+					<target>1.5</target>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic01Test.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic01Test.java b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic01Test.java
new file mode 100644
index 0000000..efc294f
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic01Test.java
@@ -0,0 +1,26 @@
+package junit44.environment;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+
+public class Basic01Test
+{
+
+    @Test
+    public void testNothing()
+    {
+    }
+
+    @AfterClass
+    public static void waitSomeTimeAround()
+    {
+        try
+        {
+            Thread.sleep( Integer.getInteger( "testSleepTime", 2000 ) );
+        }
+        catch ( InterruptedException ignored )
+        {
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic02Test.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic02Test.java b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic02Test.java
new file mode 100644
index 0000000..6bc29c7
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic02Test.java
@@ -0,0 +1,26 @@
+package junit44.environment;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+
+public class Basic02Test
+{
+
+    @Test
+    public void testNothing()
+    {
+    }
+
+    @AfterClass
+    public static void waitSomeTimeAround()
+    {
+        try
+        {
+            Thread.sleep( Integer.getInteger( "testSleepTime", 2000 ) );
+        }
+        catch ( InterruptedException ignored )
+        {
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic03Test.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic03Test.java b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic03Test.java
new file mode 100644
index 0000000..6d4941b
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic03Test.java
@@ -0,0 +1,26 @@
+package junit44.environment;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+
+public class Basic03Test
+{
+
+    @Test
+    public void testNothing()
+    {
+    }
+
+    @AfterClass
+    public static void waitSomeTimeAround()
+    {
+        try
+        {
+            Thread.sleep( Integer.getInteger( "testSleepTime", 2000 ) );
+        }
+        catch ( InterruptedException ignored )
+        {
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic04Test.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic04Test.java b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic04Test.java
new file mode 100644
index 0000000..d3b947c
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic04Test.java
@@ -0,0 +1,26 @@
+package junit44.environment;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+
+public class Basic04Test
+{
+
+    @Test
+    public void testNothing()
+    {
+    }
+
+    @AfterClass
+    public static void waitSomeTimeAround()
+    {
+        try
+        {
+            Thread.sleep( Integer.getInteger( "testSleepTime", 2000 ) );
+        }
+        catch ( InterruptedException ignored )
+        {
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic05Test.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic05Test.java b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic05Test.java
new file mode 100644
index 0000000..a3b604e
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic05Test.java
@@ -0,0 +1,26 @@
+package junit44.environment;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+
+public class Basic05Test
+{
+
+    @Test
+    public void testNothing()
+    {
+    }
+
+    @AfterClass
+    public static void waitSomeTimeAround()
+    {
+        try
+        {
+            Thread.sleep( Integer.getInteger( "testSleepTime", 2000 ) );
+        }
+        catch ( InterruptedException ignored )
+        {
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic06Test.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic06Test.java b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic06Test.java
new file mode 100644
index 0000000..ea5b951
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic06Test.java
@@ -0,0 +1,26 @@
+package junit44.environment;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+
+public class Basic06Test
+{
+
+    @Test
+    public void testNothing()
+    {
+    }
+
+    @AfterClass
+    public static void waitSomeTimeAround()
+    {
+        try
+        {
+            Thread.sleep( Integer.getInteger( "testSleepTime", 2000 ) );
+        }
+        catch ( InterruptedException ignored )
+        {
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic07Test.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic07Test.java b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic07Test.java
new file mode 100644
index 0000000..715bc93
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic07Test.java
@@ -0,0 +1,26 @@
+package junit44.environment;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+
+public class Basic07Test
+{
+
+    @Test
+    public void testNothing()
+    {
+    }
+
+    @AfterClass
+    public static void waitSomeTimeAround()
+    {
+        try
+        {
+            Thread.sleep( Integer.getInteger( "testSleepTime", 2000 ) );
+        }
+        catch ( InterruptedException ignored )
+        {
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic08Test.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic08Test.java b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic08Test.java
new file mode 100644
index 0000000..fbb8e00
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic08Test.java
@@ -0,0 +1,26 @@
+package junit44.environment;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+
+public class Basic08Test
+{
+
+    @Test
+    public void testNothing()
+    {
+    }
+
+    @AfterClass
+    public static void waitSomeTimeAround()
+    {
+        try
+        {
+            Thread.sleep( Integer.getInteger( "testSleepTime", 2000 ) );
+        }
+        catch ( InterruptedException ignored )
+        {
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic09Test.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic09Test.java b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic09Test.java
new file mode 100644
index 0000000..89fb37b
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic09Test.java
@@ -0,0 +1,26 @@
+package junit44.environment;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+
+public class Basic09Test
+{
+
+    @Test
+    public void testNothing()
+    {
+    }
+
+    @AfterClass
+    public static void waitSomeTimeAround()
+    {
+        try
+        {
+            Thread.sleep( Integer.getInteger( "testSleepTime", 2000 ) );
+        }
+        catch ( InterruptedException ignored )
+        {
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic10Test.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic10Test.java b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic10Test.java
new file mode 100644
index 0000000..e54edf0
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/surefire-946-killMainProcessInReusableFork/src/test/java/junit44/environment/Basic10Test.java
@@ -0,0 +1,26 @@
+package junit44.environment;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+
+public class Basic10Test
+{
+
+    @Test
+    public void testNothing()
+    {
+    }
+
+    @AfterClass
+    public static void waitSomeTimeAround()
+    {
+        try
+        {
+            Thread.sleep( Integer.getInteger( "testSleepTime", 2000 ) );
+        }
+        catch ( InterruptedException ignored )
+        {
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/resources/surefire-946-self-destruct-plugin/pom.xml
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/surefire-946-self-destruct-plugin/pom.xml b/surefire-integration-tests/src/test/resources/surefire-946-self-destruct-plugin/pom.xml
new file mode 100644
index 0000000..91823a7
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/surefire-946-self-destruct-plugin/pom.xml
@@ -0,0 +1,51 @@
+<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/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.apache.maven.plugins.surefire</groupId>
+  <artifactId>maven-selfdestruct-plugin</artifactId>
+  <version>0.1</version>
+  <packaging>maven-plugin</packaging>
+
+  <name>maven-selfdestruct-plugin Maven Plugin</name>
+  <url>http://maven.apache.org</url>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+      <version>2.0</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>3.8.1</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-plugin-plugin</artifactId>
+        <version>2.5.1</version>
+        <configuration>
+          <goalPrefix>maven-selfdestruct-plugin</goalPrefix>
+        </configuration>
+        <executions>
+          <execution>
+            <id>generated-helpmojo</id>
+            <goals>
+              <goal>helpmojo</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/463f37b3/surefire-integration-tests/src/test/resources/surefire-946-self-destruct-plugin/src/main/java/org/apache/maven/plugins/surefire/selfdestruct/SelfDestructMojo.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/surefire-946-self-destruct-plugin/src/main/java/org/apache/maven/plugins/surefire/selfdestruct/SelfDestructMojo.java b/surefire-integration-tests/src/test/resources/surefire-946-self-destruct-plugin/src/main/java/org/apache/maven/plugins/surefire/selfdestruct/SelfDestructMojo.java
new file mode 100644
index 0000000..33cd588
--- /dev/null
+++ b/surefire-integration-tests/src/test/resources/surefire-946-self-destruct-plugin/src/main/java/org/apache/maven/plugins/surefire/selfdestruct/SelfDestructMojo.java
@@ -0,0 +1,142 @@
+package org.apache.maven.plugins.surefire.selfdestruct;
+
+/*
+ * Copyright 2001-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+
+/**
+ * Goal which terminates the maven process it is executed in after a timeout.
+ * 
+ * @goal selfdestruct
+ * @phase test
+ */
+public class SelfDestructMojo
+    extends AbstractMojo
+{
+    private enum DestructMethod
+    {
+        exit, halt, interrupt;
+    }
+
+    /**
+     * Timeout in milliseconds
+     * 
+     * @parameter
+     */
+    private long timeoutInMillis = 0;
+
+    /**
+     * Method of self-destruction: 'exit' will use System.exit (default), 'halt' will use Runtime.halt, 'interrupt' will
+     * try to call 'taskkill' (windows) or 'kill -INT' (others)
+     * 
+     * @parameter
+     */
+    private String method = "exit";
+
+    public void execute()
+        throws MojoExecutionException
+    {
+
+        DestructMethod destructMethod = DestructMethod.valueOf( method );
+
+        if ( timeoutInMillis > 0 )
+        {
+            getLog().warn( "Self-Destruct in " + timeoutInMillis + " millis using " + destructMethod );
+            Timer timer = new Timer( "", true );
+            timer.schedule( new SelfDestructionTask( destructMethod ), timeoutInMillis );
+        }
+        else
+        {
+            new SelfDestructionTask( destructMethod ).run();
+        }
+    }
+
+    private void selfDestruct( DestructMethod destructMethod )
+    {
+        getLog().warn( "Self-Destructing NOW." );
+        switch ( destructMethod )
+        {
+            case exit:
+                System.exit( 1 );
+            case halt:
+                Runtime.getRuntime().halt( 1 );
+            case interrupt:
+                String name = ManagementFactory.getRuntimeMXBean().getName();
+                int indexOfAt = name.indexOf( '@' );
+                if ( indexOfAt > 0 )
+                {
+                    String pid = name.substring( 0, indexOfAt );
+                    getLog().warn( "Going to kill process with PID " + pid );
+
+                    List<String> args = new ArrayList<String>();
+                    if ( System.getProperty( "os.name" ).startsWith( "Windows" ) )
+                    {
+                        args.add( "taskkill" );
+                        args.add( "/PID" );
+                    }
+                    else
+                    {
+                        args.add( "kill" );
+                        args.add( "-INT" );
+                    }
+                    args.add( pid );
+
+                    try
+                    {
+                        new ProcessBuilder( args ).start();
+                    }
+                    catch ( IOException e )
+                    {
+                        getLog().error( "Unable to spawn process. Killing with System.exit.", e );
+                    }
+                }
+                else
+                {
+                    getLog().warn( "Unable to determine my PID... Using System.exit" );
+                }
+        }
+
+        System.exit( 1 );
+    }
+
+    private class SelfDestructionTask
+        extends TimerTask
+    {
+
+        private DestructMethod destructMethod;
+
+        public SelfDestructionTask( DestructMethod destructMethod )
+        {
+            this.destructMethod = destructMethod;
+        }
+
+        @Override
+        public void run()
+        {
+            selfDestruct( destructMethod );
+        }
+
+    }
+}