[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 <andreas.gud...@gmail.com> Authored: Sat Jan 5 21:13:12 2013 +0100 Committer: Kristian Rosenvold <krosenv...@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 ); + } + + } +}