260 lines
11 KiB
Java
260 lines
11 KiB
Java
package de.platon42.intellij.jupiter;
|
|
|
|
import com.intellij.jarRepository.JarRepositoryManager;
|
|
import com.intellij.jarRepository.RemoteRepositoryDescription;
|
|
import com.intellij.openapi.Disposable;
|
|
import com.intellij.openapi.module.Module;
|
|
import com.intellij.openapi.projectRoots.Sdk;
|
|
import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
|
|
import com.intellij.openapi.roots.ContentEntry;
|
|
import com.intellij.openapi.roots.DependencyScope;
|
|
import com.intellij.openapi.roots.LibraryOrderEntry;
|
|
import com.intellij.openapi.roots.ModifiableRootModel;
|
|
import com.intellij.openapi.roots.libraries.Library;
|
|
import com.intellij.openapi.roots.libraries.LibraryTable;
|
|
import com.intellij.openapi.roots.libraries.ui.OrderRoot;
|
|
import com.intellij.openapi.util.Disposer;
|
|
import com.intellij.openapi.vfs.LocalFileSystem;
|
|
import com.intellij.openapi.vfs.VirtualFile;
|
|
import com.intellij.testFramework.EdtTestUtilKt;
|
|
import com.intellij.testFramework.LightProjectDescriptor;
|
|
import com.intellij.testFramework.PsiTestUtil;
|
|
import com.intellij.testFramework.TestLoggerFactory;
|
|
import com.intellij.testFramework.fixtures.IdeaTestExecutionPolicy;
|
|
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture;
|
|
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.jetbrains.idea.maven.utils.library.RepositoryLibraryProperties;
|
|
import org.junit.jupiter.api.extension.*;
|
|
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
|
|
import org.junit.jupiter.api.extension.ExtensionContext.Store;
|
|
|
|
import java.lang.annotation.Annotation;
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.Parameter;
|
|
import java.net.URISyntaxException;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
import java.util.logging.Logger;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Stream;
|
|
|
|
public class LightCodeInsightExtension implements ParameterResolver, AfterTestExecutionCallback, InvocationInterceptor {
|
|
|
|
private static final Logger LOG = Logger.getLogger(LightCodeInsightExtension.class.getName());
|
|
|
|
@Override
|
|
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
|
|
Parameter parameter = parameterContext.getParameter();
|
|
return parameter.isAnnotationPresent(MyFixture.class)
|
|
|| parameter.isAnnotationPresent(MyTestCase.class);
|
|
}
|
|
|
|
@Override
|
|
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
|
|
LightCodeInsightFixtureTestCaseWrapper testCase = getWrapper(extensionContext);
|
|
Parameter parameter = parameterContext.getParameter();
|
|
if (parameter.isAnnotationPresent(MyFixture.class)) {
|
|
return testCase.getMyFixture();
|
|
} else if (parameter.isAnnotationPresent(MyTestCase.class)) {
|
|
return testCase;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private LightCodeInsightFixtureTestCaseWrapper getWrapper(ExtensionContext extensionContext) {
|
|
Store store = getStore(extensionContext);
|
|
return (LightCodeInsightFixtureTestCaseWrapper) store.getOrComputeIfAbsent("testCase",
|
|
key -> {
|
|
LightCodeInsightFixtureTestCaseWrapper wrapper = new LightCodeInsightFixtureTestCaseWrapper(extensionContext);
|
|
try {
|
|
wrapper.setUp();
|
|
} catch (Exception e) {
|
|
LOG.severe("Exception during setUp(): " + e);
|
|
throw new IllegalStateException("Exception during setUp()", e);
|
|
}
|
|
return wrapper;
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void afterTestExecution(ExtensionContext context) throws Exception {
|
|
Store store = getStore(context);
|
|
LightCodeInsightFixtureTestCaseWrapper testCase = (LightCodeInsightFixtureTestCaseWrapper) store.get("testCase");
|
|
if (testCase != null) {
|
|
testCase.tearDown();
|
|
}
|
|
}
|
|
|
|
private static Store getStore(ExtensionContext context) {
|
|
return context.getStore(Namespace.create(LightCodeInsightExtension.class, context.getRequiredTestMethod()));
|
|
}
|
|
|
|
@Override
|
|
public void interceptTestMethod(Invocation<Void> invocation, ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {
|
|
Throwable[] throwables = new Throwable[1];
|
|
|
|
Runnable runnable = () -> {
|
|
try {
|
|
TestLoggerFactory.onTestStarted();
|
|
invocation.proceed();
|
|
TestLoggerFactory.onTestFinished(true);
|
|
} catch (Throwable e) {
|
|
TestLoggerFactory.onTestFinished(false);
|
|
throwables[0] = e;
|
|
}
|
|
};
|
|
|
|
invokeTestRunnable(runnable);
|
|
|
|
if (throwables[0] != null) {
|
|
throw throwables[0];
|
|
}
|
|
}
|
|
|
|
private static void invokeTestRunnable(@NotNull Runnable runnable) {
|
|
IdeaTestExecutionPolicy policy = IdeaTestExecutionPolicy.current();
|
|
if (policy != null && !policy.runInDispatchThread()) {
|
|
runnable.run();
|
|
} else {
|
|
EdtTestUtilKt.runInEdtAndWait(() -> {
|
|
runnable.run();
|
|
return null;
|
|
});
|
|
}
|
|
}
|
|
|
|
private static class LightCodeInsightFixtureTestCaseWrapper extends LightJavaCodeInsightFixtureTestCase {
|
|
private final ExtensionContext extensionContext;
|
|
|
|
private LightCodeInsightFixtureTestCaseWrapper(ExtensionContext extensionContext) {
|
|
this.extensionContext = extensionContext;
|
|
}
|
|
|
|
@Override
|
|
public void setUp() throws Exception {
|
|
super.setUp();
|
|
Store store = getStore(extensionContext);
|
|
store.put("disposable", Disposer.newDisposable("LightCodeInsightFixtureTestCaseWrapper"));
|
|
}
|
|
|
|
@Override
|
|
public void tearDown() throws Exception {
|
|
Store store = getStore(extensionContext);
|
|
Disposable disposable = (Disposable) store.get("disposable");
|
|
if (myFixture != null && disposable != null) {
|
|
Disposer.dispose(disposable);
|
|
store.remove("disposable");
|
|
}
|
|
super.tearDown();
|
|
}
|
|
|
|
@NotNull
|
|
@Override
|
|
protected LightProjectDescriptor getProjectDescriptor() {
|
|
TestJdk testJdk = getMethodOrClassAnnotation(TestJdk.class);
|
|
if (testJdk == null) {
|
|
return super.getProjectDescriptor();
|
|
}
|
|
return new ProjectDescriptor(testJdk.value(), testJdk.annotations()) {
|
|
@Override
|
|
public Sdk getSdk() {
|
|
return testJdk.useInternal()
|
|
? JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk()
|
|
: super.getSdk();
|
|
}
|
|
|
|
@Override
|
|
public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) {
|
|
super.configureModule(module, model, contentEntry);
|
|
AddLocalJarToModule localJars = getMethodOrClassAnnotation(AddLocalJarToModule.class);
|
|
if (localJars != null) {
|
|
Stream.of(localJars.value()).forEach(it -> addJarContaining(model, it));
|
|
}
|
|
List<AddMavenDependencyToModule> mavenDependencies = getMethodOrClassAnnotations(AddMavenDependencyToModule.class);
|
|
mavenDependencies.forEach(it -> addFromMaven(model, it.value(), it.includeTransitiveDependencies, it.scope));
|
|
}
|
|
};
|
|
}
|
|
|
|
void addJarContaining(ModifiableRootModel model, Class<?> clazz) {
|
|
try {
|
|
Path jarPath = Paths.get(clazz.getProtectionDomain().getCodeSource().getLocation().toURI());
|
|
|
|
VirtualFile jarFile = LocalFileSystem.getInstance().findFileByIoFile(jarPath.toFile());
|
|
myFixture.allowTreeAccessForFile(jarFile);
|
|
PsiTestUtil.addLibrary(
|
|
model,
|
|
jarPath.getFileName().toString().replace(".jar", ""),
|
|
jarPath.getParent().toString(),
|
|
jarPath.getFileName().toString()
|
|
);
|
|
} catch (URISyntaxException e) {
|
|
throw new IllegalArgumentException("Class URL malformed", e);
|
|
}
|
|
}
|
|
|
|
void addFromMaven(ModifiableRootModel model, String mavenCoordinates,
|
|
boolean includeTransitiveDependencies, DependencyScope dependencyScope) {
|
|
List<RemoteRepositoryDescription> remoteRepositoryDescriptions = RemoteRepositoryDescription.DEFAULT_REPOSITORIES;
|
|
RepositoryLibraryProperties libraryProperties = new RepositoryLibraryProperties(mavenCoordinates, includeTransitiveDependencies);
|
|
Collection<OrderRoot> roots =
|
|
JarRepositoryManager.loadDependenciesModal(model.getProject(), libraryProperties, false, false, null, remoteRepositoryDescriptions);
|
|
LibraryTable.ModifiableModel tableModel = model.getModuleLibraryTable().getModifiableModel();
|
|
Library library = tableModel.createLibrary(mavenCoordinates);
|
|
Library.ModifiableModel libraryModel = library.getModifiableModel();
|
|
if (roots.isEmpty()) {
|
|
throw new IllegalStateException(String.format("No roots for '%s'", mavenCoordinates));
|
|
}
|
|
|
|
for (OrderRoot root : roots) {
|
|
libraryModel.addRoot(root.getFile(), root.getType());
|
|
}
|
|
|
|
LibraryOrderEntry libraryOrderEntry = model.findLibraryOrderEntry(library);
|
|
if (libraryOrderEntry == null) {
|
|
throw new IllegalStateException("Unable to find registered library " + mavenCoordinates);
|
|
}
|
|
libraryOrderEntry.setScope(dependencyScope);
|
|
|
|
libraryModel.commit();
|
|
tableModel.commit();
|
|
}
|
|
|
|
@Override
|
|
protected String getTestDataPath() {
|
|
TestDataPath testDataPath = getMethodOrClassAnnotation(TestDataPath.class);
|
|
if (testDataPath == null) {
|
|
return super.getTestDataPath();
|
|
}
|
|
TestDataSubPath testDataSubPath = getMethodOrClassAnnotation(TestDataSubPath.class);
|
|
if (testDataSubPath == null) {
|
|
return testDataPath.value();
|
|
}
|
|
return Paths.get(testDataPath.value(), testDataSubPath.value()).toString();
|
|
}
|
|
|
|
public JavaCodeInsightTestFixture getMyFixture() {
|
|
return myFixture;
|
|
}
|
|
|
|
private <T extends Annotation> T getMethodOrClassAnnotation(Class<T> clazz) {
|
|
T annotation = extensionContext.getRequiredTestMethod().getAnnotation(clazz);
|
|
if (annotation == null) {
|
|
annotation = extensionContext.getRequiredTestClass().getAnnotation(clazz);
|
|
}
|
|
return annotation;
|
|
}
|
|
|
|
private <T extends Annotation> List<T> getMethodOrClassAnnotations(Class<T> clazz) {
|
|
return Stream.of(extensionContext.getRequiredTestMethod().getAnnotationsByType(clazz),
|
|
extensionContext.getRequiredTestClass().getAnnotationsByType(clazz))
|
|
.flatMap(Arrays::stream)
|
|
.collect(Collectors.toList());
|
|
}
|
|
}
|
|
}
|