Skip to content

Commit 59e096d

Browse files
authored
[SUREFIRE-2095] Fork crash doesn't fail build with -Dmaven.test.failure.ignore=true when run with failsafe (#545)
Verify goal should fail when test summary indicates a SurefireBooterForkException occurred
1 parent 2c8fe40 commit 59e096d

File tree

8 files changed

+353
-2
lines changed

8 files changed

+353
-2
lines changed

maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/VerifyMojo.java

+39-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.apache.maven.plugins.annotations.Parameter;
3434
import org.apache.maven.surefire.api.cli.CommandLineOption;
3535
import org.apache.maven.surefire.api.suite.RunResult;
36+
import org.apache.maven.surefire.booter.SurefireBooterForkException;
3637
import org.codehaus.plexus.logging.Logger;
3738

3839
import java.io.File;
@@ -198,10 +199,27 @@ public void execute()
198199
throw new MojoExecutionException( e.getMessage(), e );
199200
}
200201

201-
reportExecution( this, summary, getConsoleLogger(), null );
202+
reportExecution( this, summary, getConsoleLogger(), getBooterForkException( summary ) );
202203
}
203204
}
204205

206+
private Exception getBooterForkException( RunResult summary )
207+
{
208+
String firstForkExceptionFailureMessage =
209+
String.format( "%s: " , SurefireBooterForkException.class.getName() );
210+
if ( summary.getFailure() != null && summary.getFailure().contains( firstForkExceptionFailureMessage ) )
211+
{
212+
return new SurefireBooterForkException(
213+
summary.getFailure().substring( firstForkExceptionFailureMessage.length() ) );
214+
}
215+
return null;
216+
}
217+
218+
void setLogger( Logger logger )
219+
{
220+
this.logger = logger;
221+
}
222+
205223
private PluginConsoleLogger getConsoleLogger()
206224
{
207225
if ( consoleLogger == null )
@@ -359,6 +377,16 @@ public void setReportsDirectory( File reportsDirectory )
359377
this.reportsDirectory = reportsDirectory;
360378
}
361379

380+
public File getSummaryFile()
381+
{
382+
return summaryFile;
383+
}
384+
385+
public void setSummaryFile( File summaryFile )
386+
{
387+
this.summaryFile = summaryFile;
388+
}
389+
362390
@Override
363391
public boolean getFailIfNoTests()
364392
{
@@ -383,6 +411,16 @@ public void setFailOnFlakeCount( int failOnFlakeCount )
383411
this.failOnFlakeCount = failOnFlakeCount;
384412
}
385413

414+
public MavenSession getSession()
415+
{
416+
return session;
417+
}
418+
419+
public void setSession( MavenSession session )
420+
{
421+
this.session = session;
422+
}
423+
386424
private boolean existsSummaryFile()
387425
{
388426
return summaryFile != null && summaryFile.isFile();

maven-failsafe-plugin/src/test/java/org/apache/maven/plugin/failsafe/JUnit4SuiteTest.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
@SuiteClasses( {
3535
IntegrationTestMojoTest.class,
3636
MarshallerUnmarshallerTest.class,
37-
RunResultTest.class
37+
RunResultTest.class,
38+
VerifyMojoTest.class
3839
} )
3940
@RunWith( Suite.class )
4041
public class JUnit4SuiteTest
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package org.apache.maven.plugin.failsafe;
2+
3+
/*
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing,
15+
* software distributed under the License is distributed on an
16+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
* KIND, either express or implied. See the License for the
18+
* specific language governing permissions and limitations
19+
* under the License.
20+
*/
21+
22+
import static org.mockito.Mockito.mock;
23+
import static org.mockito.Mockito.when;
24+
25+
import java.io.File;
26+
import java.io.UnsupportedEncodingException;
27+
import java.net.URL;
28+
import java.net.URLDecoder;
29+
30+
import org.apache.maven.execution.MavenExecutionRequest;
31+
import org.apache.maven.execution.MavenSession;
32+
import org.apache.maven.plugin.MojoExecutionException;
33+
import org.apache.maven.plugin.MojoFailureException;
34+
import org.codehaus.plexus.logging.Logger;
35+
import org.junit.Before;
36+
import org.junit.Rule;
37+
import org.junit.Test;
38+
import org.junit.rules.TemporaryFolder;
39+
40+
/**
41+
*/
42+
public class VerifyMojoTest
43+
{
44+
private VerifyMojo mojo;
45+
46+
@Rule
47+
public TemporaryFolder tempFolder = new TemporaryFolder();
48+
49+
@Before
50+
public void init() throws UnsupportedEncodingException
51+
{
52+
mojo = new VerifyMojo();
53+
mojo.setTestClassesDirectory( tempFolder.getRoot() );
54+
mojo.setReportsDirectory( getTestBaseDir() );
55+
}
56+
57+
private void setupExecuteMocks()
58+
{
59+
Logger logger = mock( Logger.class );
60+
when( logger.isErrorEnabled() ).thenReturn( true );
61+
when( logger.isWarnEnabled() ).thenReturn( true );
62+
when( logger.isInfoEnabled() ).thenReturn( true );
63+
when( logger.isDebugEnabled() ).thenReturn( false );
64+
mojo.setLogger( logger );
65+
66+
MavenSession session = mock( MavenSession.class );
67+
MavenExecutionRequest request = mock ( MavenExecutionRequest.class );
68+
when( request.isShowErrors() ).thenReturn( true );
69+
when( request.getReactorFailureBehavior() ).thenReturn( null );
70+
when( session.getRequest() ).thenReturn( request );
71+
mojo.setSession( session );
72+
}
73+
74+
private File getTestBaseDir()
75+
throws UnsupportedEncodingException
76+
{
77+
URL resource = getClass().getResource( "/verify-mojo" );
78+
// URLDecoder.decode necessary for JDK 1.5+, where spaces are escaped to %20
79+
return new File( URLDecoder.decode( resource.getPath(), "UTF-8" ) ).getAbsoluteFile();
80+
}
81+
82+
@Test( expected = MojoExecutionException.class )
83+
public void executeForForkError() throws MojoExecutionException, MojoFailureException, UnsupportedEncodingException
84+
{
85+
setupExecuteMocks();
86+
mojo.setSummaryFile( new File( getTestBaseDir(), "failsafe-summary-booter-fork-error.xml" ) );
87+
mojo.execute();
88+
}
89+
90+
@Test( expected = MojoExecutionException.class )
91+
public void executeForForkErrorTestFailureIgnore() throws MojoExecutionException, MojoFailureException,
92+
UnsupportedEncodingException
93+
{
94+
setupExecuteMocks();
95+
mojo.setSummaryFile( new File( getTestBaseDir(), "failsafe-summary-booter-fork-error.xml" ) );
96+
mojo.setTestFailureIgnore( true );
97+
mojo.execute();
98+
}
99+
100+
@Test
101+
public void executeForPassingTests() throws MojoExecutionException, MojoFailureException,
102+
UnsupportedEncodingException
103+
{
104+
setupExecuteMocks();
105+
mojo.setSummaryFile( new File( getTestBaseDir(), "failsafe-summary-success.xml" ) );
106+
mojo.execute();
107+
}
108+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<failsafe-summary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://maven.apache.org/surefire/maven-surefire-plugin/xsd/failsafe-summary.xsd" result="254" timeout="false">
3+
<completed>0</completed>
4+
<errors>0</errors>
5+
<failures>0</failures>
6+
<skipped>0</skipped>
7+
<failureMessage>org.apache.maven.surefire.booter.SurefireBooterForkException: The forked VM terminated without properly saying goodbye. VM crash or System.exit called?
8+
Command was /bin/sh -c cd &apos;/Users/aaron.braunstein/git/apache/maven-surefire/surefire-its/target/TestClass_testMethod&apos; &amp;&amp; &apos;/Library/Java/JavaVirtualMachines/temurin-11.jdk/Contents/Home/bin/java&apos; &apos;-Dfile.encoding=UTF-8&apos; &apos;-Duser.language=en&apos; &apos;-XFakeUnrecognizedOptionThatWillCrashJVM&apos; &apos;-Duser.region=US&apos; &apos;-showversion&apos; &apos;-Xmx6g&apos; &apos;-Xms2g&apos; &apos;-XX:+PrintGCDetails&apos; &apos;-jar&apos; &apos;/Users/aaron.braunstein/git/apache/maven-surefire/surefire-its/target/TestClass_testMethod/target/surefire/surefirebooter-20220606220315261_3.jar&apos; &apos;/Users/aaron.braunstein/git/apache/maven-surefire/surefire-its/target/TestClass_testMethod/target/surefire&apos; &apos;2022-06-06T22-03-11_910-jvmRun1&apos; &apos;surefire-20220606220315261_1tmp&apos; &apos;surefire_0-20220606220315261_2tmp&apos;
9+
Error occurred in starting fork, check output in log
10+
Process Exit Code: 1
11+
at org.apache.maven.plugin.surefire.booterclient.ForkStarter.fork(ForkStarter.java:714)
12+
at org.apache.maven.plugin.surefire.booterclient.ForkStarter.run(ForkStarter.java:311)
13+
at org.apache.maven.plugin.surefire.booterclient.ForkStarter.run(ForkStarter.java:268)
14+
at org.apache.maven.plugin.surefire.AbstractSurefireMojo.executeProvider(AbstractSurefireMojo.java:1334)
15+
at org.apache.maven.plugin.surefire.AbstractSurefireMojo.executeAfterPreconditionsChecked(AbstractSurefireMojo.java:1167)
16+
at org.apache.maven.plugin.surefire.AbstractSurefireMojo.execute(AbstractSurefireMojo.java:931)
17+
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137)
18+
at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute(MojoExecutor.java:301)
19+
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:211)
20+
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:165)
21+
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:157)
22+
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:121)
23+
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81)
24+
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:56)
25+
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:127)
26+
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:294)
27+
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:192)
28+
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:105)
29+
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:960)
30+
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:293)
31+
at org.apache.maven.cli.MavenCli.main(MavenCli.java:196)
32+
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
33+
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
34+
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
35+
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
36+
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:282)
37+
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:225)
38+
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:406)
39+
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:347)
40+
</failureMessage>
41+
</failsafe-summary>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<failsafe-summary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://maven.apache.org/surefire/maven-surefire-plugin/xsd/failsafe-summary.xsd" result="254" timeout="false">
3+
<completed>1</completed>
4+
<errors>0</errors>
5+
<failures>0</failures>
6+
<skipped>0</skipped>
7+
<failureMessage xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
8+
</failsafe-summary>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.apache.maven.surefire.its.jiras;
2+
3+
/*
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing,
15+
* software distributed under the License is distributed on an
16+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
* KIND, either express or implied. See the License for the
18+
* specific language governing permissions and limitations
19+
* under the License.
20+
*/
21+
22+
import org.apache.maven.it.VerificationException;
23+
import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase;
24+
import org.junit.Test;
25+
26+
import static org.hamcrest.Matchers.containsString;
27+
import static org.hamcrest.Matchers.is;
28+
29+
/**
30+
* Test https://issues.apache.org/jira/browse/SUREFIRE-2095
31+
*
32+
*/
33+
public class Surefire2095FailsafeJvmCrashShouldNotBeIgnoredIT
34+
extends SurefireJUnit4IntegrationTestCase
35+
{
36+
@Test
37+
public void mavenShouldFail() throws VerificationException
38+
{
39+
// Run failsafe with testFailureIgnore=true and an unknown JVM option that will cause a crash
40+
unpack( "surefire-2095-failsafe-jvm-crash" )
41+
.maven()
42+
.withFailure()
43+
.debugLogging()
44+
.executeVerify()
45+
.assertThatLogLine( containsString( "BUILD SUCCESS" ), is( 0 ) )
46+
.verifyTextInLog( "BUILD FAILURE" );
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Licensed to the Apache Software Foundation (ASF) under one
4+
~ or more contributor license agreements. See the NOTICE file
5+
~ distributed with this work for additional information
6+
~ regarding copyright ownership. The ASF licenses this file
7+
~ to you under the Apache License, Version 2.0 (the
8+
~ "License"); you may not use this file except in compliance
9+
~ with the License. You may obtain a copy of the License at
10+
~
11+
~ http://www.apache.org/licenses/LICENSE-2.0
12+
~
13+
~ Unless required by applicable law or agreed to in writing,
14+
~ software distributed under the License is distributed on an
15+
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
~ KIND, either express or implied. See the License for the
17+
~ specific language governing permissions and limitations
18+
~ under the License.
19+
-->
20+
21+
<project xmlns="http://maven.apache.org/POM/4.0.0"
22+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
23+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
24+
<modelVersion>4.0.0</modelVersion>
25+
26+
<groupId>org.apache.maven.plugins.surefire</groupId>
27+
<artifactId>SUREFIRE-2095</artifactId>
28+
<version>1.0-SNAPSHOT</version>
29+
<name>SUREFIRE-2095</name>
30+
31+
<properties>
32+
<maven.compiler.source>1.8</maven.compiler.source>
33+
<maven.compiler.target>1.8</maven.compiler.target>
34+
</properties>
35+
36+
<build>
37+
<plugins>
38+
<plugin>
39+
<groupId>org.apache.maven.plugins</groupId>
40+
<artifactId>maven-failsafe-plugin</artifactId>
41+
<version>${surefire.version}</version>
42+
<configuration>
43+
<argLine>-Dfile.encoding=UTF-8 -Duser.language=en -XFakeUnrecognizedOptionThatWillCrashJVM -Duser.region=US -showversion -Xmx6g -Xms2g -XX:+PrintGCDetails</argLine>
44+
<testFailureIgnore>true</testFailureIgnore>
45+
</configuration>
46+
<executions>
47+
<execution>
48+
<id>integration-test</id>
49+
<goals>
50+
<goal>integration-test</goal>
51+
</goals>
52+
</execution>
53+
<execution>
54+
<id>verify</id>
55+
<goals>
56+
<goal>verify</goal>
57+
</goals>
58+
</execution>
59+
</executions>
60+
</plugin>
61+
</plugins>
62+
</build>
63+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
public class PojoIT
21+
{
22+
private static int calls;
23+
24+
public void setUp()
25+
{
26+
System.out.println( "setUp called " + ++calls );
27+
}
28+
29+
public void tearDown()
30+
{
31+
System.out.println( "tearDown called " + calls );
32+
}
33+
34+
public void testSuccess()
35+
{
36+
assert true;
37+
}
38+
39+
public void testFailure()
40+
{
41+
assert false;
42+
}
43+
44+
}

0 commit comments

Comments
 (0)