
infra.test.classpath.ModifiedClassPathExtension Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2017 - 2024 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see [https://www.gnu.org/licenses/]
*/
package infra.test.classpath;
import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.InvocationInterceptor;
import org.junit.jupiter.api.extension.ReflectiveInvocationContext;
import org.junit.platform.engine.discovery.DiscoverySelectors;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.TestPlan;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
import org.junit.platform.launcher.listeners.TestExecutionSummary;
import java.lang.reflect.Method;
import java.net.URLClassLoader;
import infra.util.CollectionUtils;
/**
* A custom {@link Extension} that runs tests using a modified class path. Entries are
* excluded from the class path using {@link ClassPathExclusions @ClassPathExclusions} and
* overridden using {@link ClassPathOverrides @ClassPathOverrides} on the test class. For
* an unchanged copy of the class path {@link ForkedClassPath @ForkedClassPath} can be
* used. A class loader is created with the customized class path and is used both to load
* the test class and as the thread context class loader while the test is being run.
*
* @author Christoph Dreis
*/
class ModifiedClassPathExtension implements InvocationInterceptor {
@Override
public void interceptBeforeAllMethod(Invocation invocation,
ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable {
intercept(invocation, extensionContext);
}
@Override
public void interceptBeforeEachMethod(Invocation invocation,
ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable {
intercept(invocation, extensionContext);
}
@Override
public void interceptAfterEachMethod(Invocation invocation,
ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable {
intercept(invocation, extensionContext);
}
@Override
public void interceptAfterAllMethod(Invocation invocation,
ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable {
intercept(invocation, extensionContext);
}
@Override
public void interceptTestMethod(Invocation invocation, ReflectiveInvocationContext invocationContext,
ExtensionContext extensionContext) throws Throwable {
interceptMethod(invocation, invocationContext, extensionContext);
}
@Override
public void interceptTestTemplateMethod(Invocation invocation,
ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable {
interceptMethod(invocation, invocationContext, extensionContext);
}
private void interceptMethod(Invocation invocation, ReflectiveInvocationContext invocationContext,
ExtensionContext extensionContext) throws Throwable {
if (isModifiedClassPathClassLoader(extensionContext)) {
invocation.proceed();
return;
}
Class> testClass = extensionContext.getRequiredTestClass();
Method testMethod = invocationContext.getExecutable();
URLClassLoader modifiedClassLoader = ModifiedClassPathClassLoader.get(
testClass, testMethod, invocationContext.getArguments());
if (modifiedClassLoader == null) {
invocation.proceed();
return;
}
invocation.skip();
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(modifiedClassLoader);
try {
runTest(extensionContext.getUniqueId());
}
finally {
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
}
private void runTest(String testId) throws Throwable {
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(DiscoverySelectors.selectUniqueId(testId)).build();
Launcher launcher = LauncherFactory.create();
TestPlan testPlan = launcher.discover(request);
SummaryGeneratingListener listener = new SummaryGeneratingListener();
launcher.registerTestExecutionListeners(listener);
launcher.execute(testPlan);
TestExecutionSummary summary = listener.getSummary();
if (CollectionUtils.isNotEmpty(summary.getFailures())) {
throw summary.getFailures().get(0).getException();
}
}
private void intercept(Invocation invocation, ExtensionContext extensionContext) throws Throwable {
if (isModifiedClassPathClassLoader(extensionContext)) {
invocation.proceed();
return;
}
invocation.skip();
}
private boolean isModifiedClassPathClassLoader(ExtensionContext extensionContext) {
Class> testClass = extensionContext.getRequiredTestClass();
ClassLoader classLoader = testClass.getClassLoader();
return classLoader.getClass().getName().equals(ModifiedClassPathClassLoader.class.getName());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy