Initial check-in with two inspections working.

This commit is contained in:
2019-03-10 18:19:46 +01:00
parent 39ba01adec
commit 80104004d0
34 changed files with 1063 additions and 2 deletions
@@ -0,0 +1,10 @@
package de.platon42.intellij.jupiter;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface AddLocalJarToModule {
Class[] value();
}
@@ -0,0 +1,170 @@
package de.platon42.intellij.jupiter;
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.ModifiableRootModel;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.PsiTestUtil;
import com.intellij.testFramework.UsefulTestCase;
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
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.Parameter;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.logging.Logger;
import java.util.stream.Stream;
public class LightCodeInsightExtension implements ParameterResolver, AfterTestExecutionCallback {
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()));
}
private static class LightCodeInsightFixtureTestCaseWrapper extends LightCodeInsightFixtureTestCase {
private final ExtensionContext extensionContext;
private LightCodeInsightFixtureTestCaseWrapper(ExtensionContext extensionContext) {
this.extensionContext = extensionContext;
}
@Override
public void setUp() throws Exception {
super.setUp();
}
@Override
public void tearDown() throws Exception {
super.tearDown();
UsefulTestCase.clearFields(this);
if (myFixture != null && getProject() != null && !getProject().isDisposed()) {
Disposer.dispose(getProject());
}
}
@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 methodOrClassAnnotation = getMethodOrClassAnnotation(AddLocalJarToModule.class);
if (methodOrClassAnnotation != null) {
Stream.of(methodOrClassAnnotation.value()).forEach(it -> addJarContaining(model, it));
}
}
};
}
protected 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(
myFixture.getModule(),
model,
jarPath.getFileName().toString().replace(".jar", ""),
jarPath.getParent().toString(),
jarPath.getFileName().toString()
);
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Class URL malformed", e);
}
}
@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;
}
}
}
@@ -0,0 +1,11 @@
package de.platon42.intellij.jupiter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyFixture {
}
@@ -0,0 +1,11 @@
package de.platon42.intellij.jupiter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTestCase {
}
@@ -0,0 +1,10 @@
package de.platon42.intellij.jupiter;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface TestDataPath {
String value();
}
@@ -0,0 +1,10 @@
package de.platon42.intellij.jupiter;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface TestDataSubPath {
String value();
}
@@ -0,0 +1,16 @@
package de.platon42.intellij.jupiter;
import com.intellij.pom.java.LanguageLevel;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface TestJdk {
LanguageLevel value();
boolean annotations() default false;
boolean useInternal() default false;
}
@@ -0,0 +1,44 @@
package de.platon42.intellij.jupiter;
import com.intellij.testFramework.EdtTestUtilKt;
import com.intellij.testFramework.TestLoggerFactory;
import com.intellij.testFramework.fixtures.IdeaTestExecutionPolicy;
import org.jetbrains.annotations.NotNull;
// See https://github.com/junit-team/junit5/issues/157
public class WorkaroundUntilJupiterGetsExecutorProvider {
protected static void runTest(Runnable test) throws Throwable {
Throwable[] throwables = new Throwable[1];
Runnable runnable = () -> {
try {
TestLoggerFactory.onTestStarted();
test.run();
TestLoggerFactory.onTestFinished(true);
} catch (Throwable e) {
TestLoggerFactory.onTestFinished(false);
e.fillInStackTrace();
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;
});
}
}
}
@@ -0,0 +1,18 @@
package de.platon42.intellij.playground;
import java.util.ArrayList;
import static org.assertj.core.api.Assertions.assertThat;
public class Playground {
private void sizeOfList() {
assertThat(new ArrayList<>().size()).isEqualTo(1);
}
private void sizeOfArray() {
assertThat(new String[1].length).isLessThanOrEqualTo(1);
assertThat(new String[1]).hasSameSizeAs(new Object());
assertThat("").isEqualTo(null);
}
}
@@ -0,0 +1,67 @@
package de.platon42.intellij.plugins.cajon
import com.intellij.pom.java.LanguageLevel
import com.intellij.testFramework.TestLoggerFactory
import com.intellij.testFramework.fixtures.IdeaTestExecutionPolicy
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
import com.intellij.testFramework.runInEdtAndWait
import de.platon42.intellij.jupiter.AddLocalJarToModule
import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.TestDataPath
import de.platon42.intellij.jupiter.TestJdk
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.extension.ExtendWith
import java.lang.reflect.InvocationTargetException
@ExtendWith(LightCodeInsightExtension::class)
@TestDataPath("src/test/resources")
@TestJdk(LanguageLevel.JDK_1_8, annotations = true, useInternal = true)
@AddLocalJarToModule(Assertions::class)
abstract class AbstractCajonTest {
// See https://github.com/junit-team/junit5/issues/157
protected fun runTest(body: () -> Unit) {
val throwables = arrayOfNulls<Throwable>(1)
invokeTestRunnable {
try {
TestLoggerFactory.onTestStarted()
body()
TestLoggerFactory.onTestFinished(true)
} catch (e: InvocationTargetException) {
TestLoggerFactory.onTestFinished(false)
e.fillInStackTrace()
throwables[0] = e.targetException
} catch (e: IllegalAccessException) {
TestLoggerFactory.onTestFinished(false)
e.fillInStackTrace()
throwables[0] = e
} catch (e: Throwable) {
TestLoggerFactory.onTestFinished(false)
throwables[0] = e
}
}
if (throwables[0] != null) {
throw throwables[0]!!
}
}
private fun invokeTestRunnable(runnable: () -> Unit) {
val policy = IdeaTestExecutionPolicy.current()
if (policy != null && !policy.runInDispatchThread()) {
runnable()
} else {
runInEdtAndWait {
runnable()
}
}
}
protected fun executeQuickFixes(myFixture: JavaCodeInsightTestFixture, expectedFixes: Int) {
val quickfixes = myFixture.getAllQuickFixes()
assertThat(quickfixes).hasSize(expectedFixes)
quickfixes.forEach(myFixture::launchAction)
}
}
@@ -0,0 +1,21 @@
package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
import de.platon42.intellij.jupiter.MyFixture
import de.platon42.intellij.jupiter.TestDataSubPath
import de.platon42.intellij.plugins.cajon.AbstractCajonTest
import org.junit.jupiter.api.Test
internal class AssertThatObjectIsNotNullInspectionTest : AbstractCajonTest() {
@Test
@TestDataSubPath("inspections/ObjectIsNotNull")
internal fun assertThat_with_isNotEqualTo_null_can_use_isNotNull(@MyFixture myFixture: JavaCodeInsightTestFixture) {
runTest {
myFixture.enableInspections(AssertThatObjectIsNotNullInspection::class.java)
myFixture.configureByFile("ObjectIsNotNullBefore.java")
executeQuickFixes(myFixture, 3)
myFixture.checkResultByFile("ObjectIsNotNullAfter.java")
}
}
}
@@ -0,0 +1,21 @@
package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
import de.platon42.intellij.jupiter.MyFixture
import de.platon42.intellij.jupiter.TestDataSubPath
import de.platon42.intellij.plugins.cajon.AbstractCajonTest
import org.junit.jupiter.api.Test
internal class AssertThatObjectIsNullInspectionTest : AbstractCajonTest() {
@Test
@TestDataSubPath("inspections/ObjectIsNull")
internal fun assertThat_with_isEqualTo_null_can_use_isNull(@MyFixture myFixture: JavaCodeInsightTestFixture) {
runTest {
myFixture.enableInspections(AssertThatObjectIsNullInspection::class.java)
myFixture.configureByFile("ObjectIsNullBefore.java")
executeQuickFixes(myFixture, 3)
myFixture.checkResultByFile("ObjectIsNullAfter.java")
}
}
}
@@ -0,0 +1,10 @@
import static org.assertj.core.api.Assertions.assertThat;
public class ObjectIsNotNull {
private void objectIsNotNull() {
assertThat("").isNotNull();
assertThat("").as("nah").isNotNull();
assertThat(new Object).isNotNull();
}
}
@@ -0,0 +1,10 @@
import static org.assertj.core.api.Assertions.assertThat;
public class ObjectIsNotNull {
private void objectIsNotNull() {
assertThat("").isNotEqualTo(null);
assertThat("").as("nah").isNotEqualTo(null);
assertThat(new Object).isNotEqualTo(null);
}
}
@@ -0,0 +1,10 @@
import static org.assertj.core.api.Assertions.assertThat;
public class ObjectIsNull {
private void objectIsNull() {
assertThat("").isNull();
assertThat("").as("nah").isNull();
assertThat(new Object).isNull();
}
}
@@ -0,0 +1,10 @@
import static org.assertj.core.api.Assertions.assertThat;
public class ObjectIsNull {
private void objectIsNull() {
assertThat("").isEqualTo(null);
assertThat("").as("nah").isEqualTo(null);
assertThat(new Object).isEqualTo(null);
}
}