Skip to content

Commit 7c776a4

Browse files
authored
Merge pull request #51298 from holly-cummins/tolerate-surefire-354
Dispose of the FacadeClassLoader at the end of every test plan so surefire reruns are tolerated
2 parents 2d6c672 + 6bf4f0a commit 7c776a4

File tree

5 files changed

+56
-4
lines changed

5 files changed

+56
-4
lines changed

integration-tests/maven/src/test/resources-filtered/projects/test-flaky-test-multiple-profiles/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@
8989
</execution>
9090
</executions>
9191
</plugin>
92+
<!-- Always use the same version of surefire as the main Quarkus build -->
93+
<plugin>
94+
<groupId>org.apache.maven.plugins</groupId>
95+
<artifactId>maven-surefire-plugin</artifactId>
96+
<version>${version.surefire.plugin}</version>
97+
</plugin>
9298
</plugins>
9399
</build>
94100
<profiles>

integration-tests/maven/src/test/resources-filtered/projects/test-flaky-test-single-profile/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@
8989
</execution>
9090
</executions>
9191
</plugin>
92+
<!-- Always use the same version of surefire as the main Quarkus build -->
93+
<plugin>
94+
<groupId>org.apache.maven.plugins</groupId>
95+
<artifactId>maven-surefire-plugin</artifactId>
96+
<version>${version.surefire.plugin}</version>
97+
</plugin>
9298
</plugins>
9399
</build>
94100
<profiles>

test-framework/junit5/src/main/java/io/quarkus/test/junit/classloading/FacadeClassLoader.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,9 @@ public FacadeClassLoader(ClassLoader parent, boolean isAuxiliaryApplication, Cur
267267

268268
@Override
269269
public Class<?> loadClass(String name) throws ClassNotFoundException {
270-
log.debugf("Facade classloader loading %s", name);
270+
// Debuggers, beware: If the same class is loaded with the same FacadeClassLoader instance using Class.forName(), a cached copy will be returned,
271+
// and nothing will be logged here; it will look like classloading didn't happen, but the problem is actually a stale instance
272+
log.debugf("Facade classloader loading %s for the first time\n", name);
271273

272274
if (peekingClassLoader == null) {
273275
throw new RuntimeException("Attempted to load classes with a closed classloader: " + this);

test-framework/junit5/src/main/java/io/quarkus/test/junit/launcher/CustomLauncherInterceptor.java

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
import org.junit.platform.launcher.LauncherDiscoveryRequest;
55
import org.junit.platform.launcher.LauncherSession;
66
import org.junit.platform.launcher.LauncherSessionListener;
7+
import org.junit.platform.launcher.TestExecutionListener;
8+
import org.junit.platform.launcher.TestPlan;
79

810
import io.quarkus.test.junit.classloading.FacadeClassLoader;
911

10-
public class CustomLauncherInterceptor implements LauncherDiscoveryListener, LauncherSessionListener {
12+
public class CustomLauncherInterceptor
13+
implements LauncherDiscoveryListener, LauncherSessionListener, TestExecutionListener {
1114

1215
private static FacadeClassLoader facadeLoader = null;
1316
// Also use a static variable to store a 'first' starting state that we can reset to
@@ -93,7 +96,6 @@ public void launcherDiscoveryStarted(LauncherDiscoveryRequest request) {
9396
System.setProperty("java.util.concurrent.ForkJoinPool.common.threadFactory",
9497
"io.quarkus.bootstrap.forkjoin.QuarkusForkJoinWorkerThreadFactory");
9598
}
96-
9799
}
98100

99101
private void adjustContextClassLoader() {
@@ -108,7 +110,6 @@ private void adjustContextClassLoader() {
108110

109111
@Override
110112
public void launcherDiscoveryFinished(LauncherDiscoveryRequest request) {
111-
112113
if (!isProductionModeTests()) {
113114
// We need to support two somewhat incompatible scenarios.
114115
// If there are user extensions present which implement `ExecutionCondition`, and they call config in `evaluateExecutionCondition`,
@@ -129,7 +130,10 @@ public void launcherDiscoveryFinished(LauncherDiscoveryRequest request) {
129130

130131
@Override
131132
public void launcherSessionClosed(LauncherSession session) {
133+
clearContextClassloader();
134+
}
132135

136+
private static void clearContextClassloader() {
133137
try {
134138
// Tidy up classloaders we created, but not ones created upstream
135139
// Also make sure to reset the TCCL so we don't leave a closed classloader on the thread
@@ -150,4 +154,37 @@ public void launcherSessionClosed(LauncherSession session) {
150154
throw new RuntimeException("Failed to close custom classloader", e);
151155
}
152156
}
157+
158+
/**
159+
* Called when the execution of the {@link TestPlan} has started,
160+
* <em>before</em> any test has been executed.
161+
*
162+
* <p>
163+
* Called from the same thread as {@link #testPlanExecutionFinished(TestPlan)}.
164+
*
165+
* In continuous testing, the test plan listener seems not to be called, perhaps because of a different execution model.
166+
*
167+
* @param testPlan describes the tree of tests about to be executed
168+
*/
169+
public void testPlanExecutionStarted(TestPlan testPlan) {
170+
// Do nothing, but have the method here for symmetry :)
171+
}
172+
173+
/**
174+
* Called when the execution of the {@link TestPlan} has finished,
175+
* <em>after</em> all tests have been executed.
176+
*
177+
* <p>
178+
* Called from the same thread as {@link #testPlanExecutionStarted(TestPlan)}.
179+
*
180+
* If tests failed and are rerun by surefire, the same session will be used for all runs, so we need to get rid of the
181+
* FacadeClassLoader associated with the previous run, since its app will be closed and its classloaders will all be stale.
182+
*
183+
* In continuous testing, the test plan listener seems not to be called, perhaps because of a different execution model.
184+
*
185+
* @param testPlan describes the tree of tests that have been executed
186+
*/
187+
public void testPlanExecutionFinished(TestPlan testPlan) {
188+
clearContextClassloader();
189+
}
153190
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
io.quarkus.test.junit.launcher.ExecutionListener
2+
io.quarkus.test.junit.launcher.CustomLauncherInterceptor

0 commit comments

Comments
 (0)