Skip to content

Commit 9c611ce

Browse files
committed
Fix shutdown hooks for JUnit5 for JDK24+. Previously, all tests would exit with System.exit or Runtime.exit was called because the shutdown hook was not removed at test teardown.
1 parent 320003f commit 9c611ce

File tree

3 files changed

+71
-13
lines changed

3 files changed

+71
-13
lines changed

java/src/com/github/bazel_contrib/contrib_rules_jvm/junit5/JUnit5Runner.java

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,19 +87,13 @@ private static SystemExitToggle getSystemExitToggle() {
8787
System.err.println("Failed to load Java 17 system exit override: " + e.getMessage());
8888
}
8989

90-
// Install a shutdown hook so people can track down what went wrong
91-
// if a test calls `System.exit`
92-
Thread shutdownHook = SystemExitDetectingShutdownHook.newShutdownHook(System.err);
93-
Runtime.getRuntime().addShutdownHook(shutdownHook);
94-
9590
// Fall through
96-
}
91+
System.err.println(
92+
"Unable to create a mechanism to prevent `System.exit` being called. "
93+
+ "Tests may cause `bazel test` to exit prematurely.");
9794

98-
System.err.println(
99-
"Unable to create a mechanism to prevent `System.exit` being called. "
100-
+ "Tests may cause `bazel test` to exit prematurely.");
101-
102-
return new NullSystemExitToggle();
95+
return new NullSystemExitToggle();
96+
}
10397
}
10498

10599
private static void detectJUnit5Classes() {
Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
11
package com.github.bazel_contrib.contrib_rules_jvm.junit5;
22

3+
import java.util.Optional;
4+
35
public class NullSystemExitToggle implements SystemExitToggle {
6+
7+
private Optional<Thread> shutdownHook;
8+
9+
public NullSystemExitToggle() {
10+
this.shutdownHook = Optional.empty();
11+
}
12+
413
@Override
514
public void prevent() {
6-
// No-op
15+
if (!shutdownHook.isEmpty()) {
16+
throw new RuntimeException("SystemExitDetectingShutdownHook already added");
17+
}
18+
// Install a shutdown hook so people can track down what went wrong
19+
// if a test calls `System.exit`
20+
Thread shutdownHook = SystemExitDetectingShutdownHook.newShutdownHook(System.err);
21+
this.shutdownHook = Optional.of(shutdownHook);
22+
Runtime.getRuntime().addShutdownHook(shutdownHook);
723
}
824

925
@Override
1026
public void allow() {
11-
// No-op
27+
if (shutdownHook.isEmpty()) {
28+
throw new RuntimeException("SystemExitDetectingShutdownHook never added");
29+
}
30+
Runtime.getRuntime().removeShutdownHook(shutdownHook.get());
1231
}
1332
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.github.bazel_contrib.contrib_rules_jvm.junit5;
2+
3+
import static org.junit.jupiter.api.Assertions.assertThrows;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
public class NullSystemExitToggleTest {
8+
9+
@Test
10+
public void prevent_alreadyPrevented_throwsRuntimeException() {
11+
// Arrange.
12+
NullSystemExitToggle toggle = new NullSystemExitToggle();
13+
14+
// Act and Assert.
15+
toggle.prevent();
16+
assertThrows(RuntimeException.class, () -> toggle.prevent());
17+
18+
// Cleanup.
19+
toggle.allow();
20+
}
21+
22+
23+
@Test
24+
public void allow_alreadyAllowed_throwsRuntimeException() {
25+
// Arrange.
26+
NullSystemExitToggle toggle = new NullSystemExitToggle();
27+
28+
// Act and Assert.
29+
assertThrows(RuntimeException.class, () -> toggle.allow());
30+
}
31+
32+
@Test
33+
public void normalFlow_success() {
34+
// Arrange.
35+
NullSystemExitToggle toggle = new NullSystemExitToggle();
36+
37+
// Act and (implicit) Assert.
38+
try {
39+
toggle.prevent();
40+
} finally {
41+
toggle.allow();
42+
}
43+
}
44+
45+
}

0 commit comments

Comments
 (0)