From 9e4e9469d4e0125cc8ba3cbca7d5e8b069dea7ce Mon Sep 17 00:00:00 2001 From: Bart van Helvert Date: Sun, 3 Jul 2022 21:27:14 +0200 Subject: [PATCH] [java] IDEA-297284 Convert `MigrateAssertToMatcherAssert` inspection to UAST GitOrigin-RevId: a8f748f607798b4fda2c1df761d559bd9df9bf56 --- .../messages/TypeMigrationBundle.properties | 5 +- .../src/META-INF/TypeMigration.xml | 6 - ...igrateAssertToMatcherAssertInspection.java | 316 ------------------ .../MigrateAssertToMatcherAssertTest.java | 46 --- .../MigrateAssertToMatcherAssert.java | 39 --- .../MigrateAssertToMatcherAssert_after.java | 46 --- .../resources/META-INF/JvmAnalysisPlugin.xml | 5 + .../MigrateAssertToMatcherAssert.html | 0 .../messages/JvmAnalysisBundle.properties | 4 + .../HamcrestAssertionsConverterInspection.kt | 209 ++++++++++++ .../test/junit/HamcrestCommonClassNames.java | 8 + ...mcrestAssertionsConverterInspectionTest.kt | 176 ++++++++++ ...mcrestAssertionsConverterInspectionTest.kt | 187 +++++++++++ ...stAssertionsConverterInspectionTestBase.kt | 25 ++ 14 files changed, 615 insertions(+), 457 deletions(-) delete mode 100644 java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/MigrateAssertToMatcherAssertInspection.java delete mode 100644 java/typeMigration/test/com/intellij/codeInsight/inspections/MigrateAssertToMatcherAssertTest.java delete mode 100644 java/typeMigration/testData/inspections/migrateAssertsToAssertThat/MigrateAssertToMatcherAssert.java delete mode 100644 java/typeMigration/testData/inspections/migrateAssertsToAssertThat/MigrateAssertToMatcherAssert_after.java rename {java/java-impl/src => jvm/jvm-analysis-impl/resources}/inspectionDescriptions/MigrateAssertToMatcherAssert.html (100%) create mode 100644 jvm/jvm-analysis-impl/src/com/intellij/codeInspection/test/junit/HamcrestAssertionsConverterInspection.kt create mode 100644 jvm/jvm-analysis-impl/src/com/intellij/codeInspection/test/junit/HamcrestCommonClassNames.java create mode 100644 jvm/jvm-analysis-java-tests/testSrc/com/intellij/codeInspection/tests/java/test/JavaHamcrestAssertionsConverterInspectionTest.kt create mode 100644 jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/codeInspection/tests/kotlin/test/KotlinHamcrestAssertionsConverterInspectionTest.kt create mode 100644 jvm/jvm-analysis-tests/src/com/intellij/codeInspection/tests/test/HamcrestAssertionsConverterInspectionTestBase.kt diff --git a/java/typeMigration/resources/messages/TypeMigrationBundle.properties b/java/typeMigration/resources/messages/TypeMigrationBundle.properties index 5fa641407def..57f2277a0f38 100644 --- a/java/typeMigration/resources/messages/TypeMigrationBundle.properties +++ b/java/typeMigration/resources/messages/TypeMigrationBundle.properties @@ -1,6 +1,5 @@ change.class.parameter.incorrect.type.error.hint=Incorrect type change.class.type.parameter.family.name=Change class type parameter -checkbox.statically.import.matcher.methods=Statically import matcher's methods convert.to.atomic.family.name=Convert to atomic convert.to.longadder.family.name=Convert to 'LongAdder' convert.to.threadlocal.family.name=Convert to 'ThreadLocal' @@ -9,9 +8,7 @@ inspection.guava.erase.option=Erase @javax.annotations.Nullable from converted f inspection.guava.method.chains.option=Report method chains inspection.guava.return.types.option=Report return types inspection.guava.variables.option=Report variables -inspection.migrate.assert.to.matcher.description=Assert expression #ref can be replaced with ''{0}'' call #loc migrate.fix.text=Migrate ''{0}'' type to ''{1}'' migrate.guava.to.java.family.name=Migrate Guava type to Java migrate.method.chain.fix.text=Migrate method chain type to ''{0}'' -inspection.guava.name=Guava's functional primitives can be replaced with Java -inspection.assertion.name=JUnit assertion can be 'assertThat()' call \ No newline at end of file +inspection.guava.name=Guava's functional primitives can be replaced with Java \ No newline at end of file diff --git a/java/typeMigration/src/META-INF/TypeMigration.xml b/java/typeMigration/src/META-INF/TypeMigration.xml index 4e4f1fff4404..e3ca04d68481 100644 --- a/java/typeMigration/src/META-INF/TypeMigration.xml +++ b/java/typeMigration/src/META-INF/TypeMigration.xml @@ -42,12 +42,6 @@ groupKey="group.names.language.level.specific.issues.and.migration.aids8" enabledByDefault="true" level="WARNING" implementationClass="com.intellij.refactoring.typeMigration.inspections.GuavaInspection" bundle="messages.TypeMigrationBundle" key="inspection.guava.name"/> - - - diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/MigrateAssertToMatcherAssertInspection.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/MigrateAssertToMatcherAssertInspection.java deleted file mode 100644 index b5afa76d1a22..000000000000 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/MigrateAssertToMatcherAssertInspection.java +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.intellij.refactoring.typeMigration.inspections; - -import com.intellij.codeInsight.intention.impl.AddOnDemandStaticImportAction; -import com.intellij.codeInspection.*; -import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Pair; -import com.intellij.psi.*; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.InheritanceUtil; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiTypesUtil; -import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; -import com.intellij.refactoring.typeMigration.TypeMigrationBundle; -import com.intellij.util.IncorrectOperationException; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -/** - * @author Dmitry Batkovich - */ -public class MigrateAssertToMatcherAssertInspection extends AbstractBaseJavaLocalInspectionTool { - - private final static Logger LOG = Logger.getInstance(MigrateAssertToMatcherAssertInspection.class); - private final static Map> ASSERT_METHODS = new HashMap<>(); - - static { - ASSERT_METHODS.put("assertArrayEquals", Pair.create("$expected$, $actual$", "$actual$, {0}.is($expected$)")); - ASSERT_METHODS.put("assertEquals", Pair.create("$expected$, $actual$", "$actual$, {0}.is($expected$)")); - ASSERT_METHODS.put("assertNotEquals", Pair.create("$expected$, $actual$", "$actual$, {0}.not({0}.is($expected$))")); - ASSERT_METHODS.put("assertSame", Pair.create("$expected$, $actual$", "$actual$, {0}.sameInstance($expected$)")); - ASSERT_METHODS.put("assertNotSame", Pair.create("$expected$, $actual$", "$actual$, {0}.not({0}.sameInstance($expected$))")); - ASSERT_METHODS.put("assertNotNull", Pair.create("$obj$", "$obj$, {0}.notNullValue()")); - ASSERT_METHODS.put("assertNull", Pair.create("$obj$", "$obj$, {0}.nullValue()")); - ASSERT_METHODS.put("assertTrue", Pair.create("$cond$", "$cond$, {0}.is(true)")); - ASSERT_METHODS.put("assertFalse", Pair.create("$cond$", "$cond$, {0}.is(false)")); - } - - private static final String CORE_MATCHERS_CLASS_NAME = "org.hamcrest.CoreMatchers"; - private static final String MATCHERS_CLASS_NAME = "org.hamcrest.Matchers"; - private static final String ORDERING_COMPARISON_NAME = "org.hamcrest.number.OrderingComparison"; - - public boolean myStaticallyImportMatchers = true; - - @Nullable - @Override - public JComponent createOptionsPanel() { - return new SingleCheckboxOptionsPanel(TypeMigrationBundle.message("checkbox.statically.import.matcher.methods"), this, - "myStaticallyImportMatchers"); - } - - @NotNull - @Override - public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { - final GlobalSearchScope resolveScope = holder.getFile().getResolveScope(); - final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(holder.getProject()); - final PsiClass coreMatchersClass = javaPsiFacade.findClass(CORE_MATCHERS_CLASS_NAME, resolveScope); - final PsiClass matchersClass = javaPsiFacade.findClass(MATCHERS_CLASS_NAME, resolveScope); - if (coreMatchersClass == null && matchersClass == null) { - return PsiElementVisitor.EMPTY_VISITOR; - } - return new JavaElementVisitor() { - @Override - public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) { - final PsiReferenceExpression methodExpression = expression.getMethodExpression(); - final String methodName = methodExpression.getReferenceName(); - if (!ASSERT_METHODS.containsKey(methodName)) return; - final PsiClass assertClass; - final PsiMethod assertMethod = expression.resolveMethod(); - if (assertMethod == null || (assertClass = assertMethod.getContainingClass()) == null) { - return; - } - if (!"junit.framework.Assert".equals(assertClass.getQualifiedName()) && - !"org.junit.Assert".equals(assertClass.getQualifiedName())) { - return; - } - - if (isBooleanAssert(assertMethod.getName())) { - PsiExpression[] args = expression.getArgumentList().getExpressions(); - if (args[args.length - 1] instanceof PsiBinaryExpression && - javaPsiFacade.findClass(ORDERING_COMPARISON_NAME, expression.getResolveScope()) == null) { - return; - } - } - - final PsiElement referenceNameElement = methodExpression.getReferenceNameElement(); - if (referenceNameElement != null) { - holder.registerProblem(referenceNameElement, - TypeMigrationBundle.message("inspection.migrate.assert.to.matcher.description", "assertThat()"), - new MigrateAssertToMatcherAssertFix( - matchersClass != null ? MATCHERS_CLASS_NAME : CORE_MATCHERS_CLASS_NAME)); - } - } - }; - } - - public class MigrateAssertToMatcherAssertFix implements LocalQuickFix { - private final String myMatchersClassName; - - public MigrateAssertToMatcherAssertFix(String name) { - myMatchersClassName = name; - } - - @Nls - @NotNull - @Override - public String getFamilyName() { - return CommonQuickFixBundle.message("fix.replace.with.x", "assertThat()"); - } - - @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - final PsiElement element = descriptor.getPsiElement(); - final PsiElement grandParent = element.getParent().getParent(); - if (!(grandParent instanceof PsiMethodCallExpression)) { - return; - } - final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)grandParent; - final PsiMethod method = methodCall.resolveMethod(); - if (method == null) { - return; - } - final String methodName = method.getName(); - Pair templatePair = null; - if (isBooleanAssert(methodName)) { - final PsiExpression[] expressions = methodCall.getArgumentList().getExpressions(); - final PsiExpression conditionExpression = expressions[expressions.length - 1]; - final boolean negate = methodName.contains("False"); - if (conditionExpression instanceof PsiBinaryExpression) { - templatePair = getSuitableMatcherForBinaryExpressionInsideBooleanAssert((PsiBinaryExpression)conditionExpression, negate); - } - else if (conditionExpression instanceof PsiMethodCallExpression) { - templatePair = getSuitableMatcherForMethodCallInsideBooleanAssert((PsiMethodCallExpression)conditionExpression, negate); - } - } - if (templatePair == null) { - templatePair = ASSERT_METHODS.get(methodName); - } - LOG.assertTrue(templatePair != null); - templatePair = buildFullTemplate(templatePair, method); - final PsiExpression replaced; - try { - replaced = TypeConversionDescriptor.replaceExpression(methodCall, templatePair.getFirst(), MessageFormat.format(templatePair.getSecond(), myMatchersClassName)); - } - catch (IncorrectOperationException e) { - LOG.error("Replacer can't match expression:\n" + - methodCall.getText() + - "\nwith replacement template:\n(" + - templatePair.getFirst() + - ", " + - templatePair.getSecond() + - ")"); - throw e; - } - - if (myStaticallyImportMatchers) { - for (PsiJavaCodeReferenceElement ref : ContainerUtil.reverse( - new ArrayList<>(PsiTreeUtil.findChildrenOfType(replaced, PsiJavaCodeReferenceElement.class)))) { - if (!ref.isValid()) continue; - final PsiElement resolvedElement = ref.resolve(); - if (resolvedElement instanceof PsiClass) { - final String qName = ((PsiClass)resolvedElement).getQualifiedName(); - if (qName != null && qName.startsWith("org.hamcrest")) { - final PsiIdentifier identifier = PsiTreeUtil.getChildOfType(ref, PsiIdentifier.class); - if (identifier != null) { - AddOnDemandStaticImportAction.invoke(project, replaced.getContainingFile(), null, identifier); - } - } - } - } - } - } - - private Pair<@NonNls String, @NonNls String> buildFullTemplate(Pair templatePair, PsiMethod method) { - if (templatePair == null) { - return null; - } - final boolean hasMessage = hasMessage(method); - final String searchTemplate = "'_Assert?." + method.getName() + "(" + (hasMessage ? "$msg$, " : "") + templatePair.getFirst() + ")"; - final String replaceTemplate = "org.hamcrest.MatcherAssert.assertThat(" + (hasMessage ? "$msg$, " : "") + templatePair.getSecond() + ")"; - return Pair.create(searchTemplate, replaceTemplate); - } - - @Nullable - private Pair<@NonNls String, @NonNls String> getSuitableMatcherForBinaryExpressionInsideBooleanAssert(PsiBinaryExpression expression, boolean negate) { - final PsiJavaToken sign = expression.getOperationSign(); - IElementType tokenType = sign.getTokenType(); - if (negate) { - tokenType = negate(tokenType); - } - final @NonNls String fromTemplate = "$left$ " + sign.getText() + " $right$"; - if (JavaTokenType.EQEQ.equals(tokenType) || JavaTokenType.NE.equals(tokenType)) { - boolean isEqEqForPrimitives = true; - for (PsiExpression operand : Arrays.asList(expression.getLOperand(), expression.getROperand())) { - if (!(operand.getType() instanceof PsiPrimitiveType)) { - isEqEqForPrimitives = false; - break; - } - } - @NonNls String rightPartOfAfterTemplate = - isEqEqForPrimitives ? "{0}.is($right$)" : "{0}.sameInstance($right$)"; - if (JavaTokenType.NE.equals(tokenType)) { - rightPartOfAfterTemplate = "{0}.not(" + rightPartOfAfterTemplate + ")"; - } - return Pair.create(fromTemplate, - "$left$, " + rightPartOfAfterTemplate); - } - @NonNls String replaceTemplate = null; - if (JavaTokenType.GT.equals(tokenType)) { - replaceTemplate = "greaterThan($right$)"; - } - else if (JavaTokenType.LT.equals(tokenType)) { - replaceTemplate = "lessThan($right$)"; - } - else if (JavaTokenType.GE.equals(tokenType)) { - replaceTemplate = "greaterThanOrEqualTo($right$)"; - } - else if (JavaTokenType.LE.equals(tokenType)) { - replaceTemplate = "lessThanOrEqualTo($right$)"; - } - if (replaceTemplate == null) { - return null; - } - replaceTemplate = ORDERING_COMPARISON_NAME + "." + replaceTemplate; - return Pair.create(fromTemplate, "$left$, " + replaceTemplate); - } - } - - private static boolean isBooleanAssert(String methodName) { - return "assertFalse".equals(methodName) || "assertTrue".equals(methodName); - } - - private static IElementType negate(IElementType tokenType) { - if (JavaTokenType.GT.equals(tokenType)) { - return JavaTokenType.LE; - } - else if (JavaTokenType.LT.equals(tokenType)) { - return JavaTokenType.GE; - } - else if (JavaTokenType.GE.equals(tokenType)) { - return JavaTokenType.LT; - } - else if (JavaTokenType.LE.equals(tokenType)) { - return JavaTokenType.GT; - } - return null; - } - - @Nullable - private static Pair getSuitableMatcherForMethodCallInsideBooleanAssert(PsiMethodCallExpression expression, boolean negate) { - final String methodName = expression.getMethodExpression().getReferenceName(); - @NonNls String fromTemplate = null; - @NonNls String toLeftPart = null; - @NonNls String toRightPart = null; - if ("contains".equals(methodName)) { - final PsiMethod method = expression.resolveMethod(); - final PsiClass containingClass; - if (method != null && - (containingClass = method.getContainingClass()) != null) { - - if (CommonClassNames.JAVA_LANG_STRING.equals(containingClass.getQualifiedName())) { - fromTemplate = "$str$.contains($sub$)"; - toLeftPart = "$str$, "; - toRightPart = "{0}.containsString($sub$)"; - } else if (InheritanceUtil.isInheritor(containingClass, CommonClassNames.JAVA_UTIL_COLLECTION)) { - fromTemplate = "$collection$.contains($element$)"; - toLeftPart = "$collection$, "; - toRightPart = "{0}.hasItem($element$)"; - } - } - } - else if ("equals".equals(methodName)) { - final PsiMethod method = expression.resolveMethod(); - if (method != null && isUniqueObjectParameter(method.getParameterList())) { - fromTemplate = "$left$.equals($right$)"; - toLeftPart = "$left$, "; - toRightPart = "{0}.is($right$)"; - } - } - if (fromTemplate == null) { - return null; - } - if (negate) { - toRightPart = "{0}.not(" + toRightPart + ")"; - } - return Pair.create(fromTemplate, toLeftPart + toRightPart); - } - - private static boolean isUniqueObjectParameter(PsiParameterList parameters) { - if (parameters.getParametersCount() != 1) { - return false; - } - final PsiParameter parameter = parameters.getParameters()[0]; - final PsiClass parameterClass = PsiTypesUtil.getPsiClass(parameter.getType()); - return parameterClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(parameterClass.getQualifiedName()); - } - - private static boolean hasMessage(PsiMethod method) { - final PsiParameter maybeMessage = method.getParameterList().getParameters()[0]; - final PsiClass maybeString = PsiTypesUtil.getPsiClass(maybeMessage.getType()); - return maybeString != null && CommonClassNames.JAVA_LANG_STRING.equals(maybeString.getQualifiedName()); - } -} \ No newline at end of file diff --git a/java/typeMigration/test/com/intellij/codeInsight/inspections/MigrateAssertToMatcherAssertTest.java b/java/typeMigration/test/com/intellij/codeInsight/inspections/MigrateAssertToMatcherAssertTest.java deleted file mode 100644 index 5a2cbd32fdbe..000000000000 --- a/java/typeMigration/test/com/intellij/codeInsight/inspections/MigrateAssertToMatcherAssertTest.java +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.intellij.codeInsight.inspections; - -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.codeInspection.LocalQuickFix; -import com.intellij.codeInspection.ex.QuickFixWrapper; -import com.intellij.project.IntelliJProjectConfiguration; -import com.intellij.refactoring.typeMigration.inspections.MigrateAssertToMatcherAssertInspection; -import com.intellij.testFramework.PlatformTestUtil; -import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; -import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; -import com.intellij.util.ArrayUtilRt; - -/** - * @author Dmitry Batkovich - */ -public class MigrateAssertToMatcherAssertTest extends JavaCodeInsightFixtureTestCase { - @Override - protected String getTestDataPath() { - return PlatformTestUtil.getCommunityPath() + "/java/typeMigration/testData/inspections/migrateAssertsToAssertThat"; - } - - @Override - protected void tuneFixture(JavaModuleFixtureBuilder moduleBuilder) { - moduleBuilder.addLibrary("test-env", - ArrayUtilRt.toStringArray(IntelliJProjectConfiguration.getProjectLibraryClassesRootPaths("JUnit4"))); - moduleBuilder.addLibrary("test-env", - ArrayUtilRt.toStringArray(IntelliJProjectConfiguration.getProjectLibraryClassesRootPaths("hamcrest"))); - } - - public void testMigrateAssertToMatcherAssert() { - myFixture.configureByFile(getTestName(false) + ".java"); - myFixture.enableInspections(new MigrateAssertToMatcherAssertInspection()); - myFixture.checkHighlighting(); - for (IntentionAction wrapper : myFixture.getAllQuickFixes()) { - if (wrapper instanceof QuickFixWrapper) { - final LocalQuickFix fix = ((QuickFixWrapper)wrapper).getFix(); - if (fix instanceof MigrateAssertToMatcherAssertInspection.MigrateAssertToMatcherAssertFix) { - myFixture.launchAction(wrapper); - } - } - } - myFixture.checkResultByFile(getTestName(false) + "_after.java"); - } - -} diff --git a/java/typeMigration/testData/inspections/migrateAssertsToAssertThat/MigrateAssertToMatcherAssert.java b/java/typeMigration/testData/inspections/migrateAssertsToAssertThat/MigrateAssertToMatcherAssert.java deleted file mode 100644 index e3e293ccaaad..000000000000 --- a/java/typeMigration/testData/inspections/migrateAssertsToAssertThat/MigrateAssertToMatcherAssert.java +++ /dev/null @@ -1,39 +0,0 @@ -import org.junit.Assert; -import java.util.Collection; - -public class MigrateAssertToMatcherAssert { - void m() { - Assert.assertTrue(2 != 3); - Assert.assertTrue(2 == 3); - Assert.assertTrue(2 > 3); - Assert.assertTrue(2 < 3); - Assert.assertTrue(2 >= 3); - Assert.assertTrue(2 <= 3); - - Assert.assertFalse(2 != 3); - Assert.assertFalse(2 == 3); - Assert.assertFalse(2 > 3); - Assert.assertFalse(2 < 3); - Assert.assertFalse(2 >= 3); - Assert.assertFalse(2 <= 3); - } - - void m2() { - Assert.assertTrue("asd".equals("zxc")); - Assert.assertTrue("asd" == "zxc"); - Assert.assertTrue("asd".contains("qwe")); - } - - void m3(Collection c, String o) { - Assert.assertTrue(c.contains(o)); - Assert.assertEquals(c, o); - Assert.assertEquals("msg", c, o); - Assert.assertNotNull(c); - Assert.assertNull(c); - Assert.assertFalse(c.contains(o)); - } - - void m(int[] a, int[] b) { - Assert.assertArrayEquals(a, b); - } -} \ No newline at end of file diff --git a/java/typeMigration/testData/inspections/migrateAssertsToAssertThat/MigrateAssertToMatcherAssert_after.java b/java/typeMigration/testData/inspections/migrateAssertsToAssertThat/MigrateAssertToMatcherAssert_after.java deleted file mode 100644 index 42f05a9f9d47..000000000000 --- a/java/typeMigration/testData/inspections/migrateAssertsToAssertThat/MigrateAssertToMatcherAssert_after.java +++ /dev/null @@ -1,46 +0,0 @@ -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.hamcrest.number.OrderingComparison; -import org.junit.Assert; -import java.util.Collection; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; -import static org.hamcrest.number.OrderingComparison.*; - -public class MigrateAssertToMatcherAssert { - void m() { - assertThat(2, not(is(3))); - assertThat(2, is(3)); - assertThat(2, greaterThan(3)); - assertThat(2, lessThan(3)); - assertThat(2, greaterThanOrEqualTo(3)); - assertThat(2, lessThanOrEqualTo(3)); - - assertThat(2 != 3, is(false)); - assertThat(2 == 3, is(false)); - assertThat(2, lessThanOrEqualTo(3)); - assertThat(2, greaterThanOrEqualTo(3)); - assertThat(2, lessThan(3)); - assertThat(2, greaterThan(3)); - } - - void m2() { - assertThat("asd", is("zxc")); - assertThat("asd", sameInstance("zxc")); - assertThat("asd", containsString("qwe")); - } - - void m3(Collection c, String o) { - assertThat(c, hasItem(o)); - assertThat(o, is(c)); - assertThat("msg", o, is(c)); - assertThat(c, notNullValue()); - assertThat(c, nullValue()); - assertThat(c, not(hasItem(o))); - } - - void m(int[] a, int[] b) { - assertThat(b, is(a)); - } -} \ No newline at end of file diff --git a/jvm/jvm-analysis-impl/resources/META-INF/JvmAnalysisPlugin.xml b/jvm/jvm-analysis-impl/resources/META-INF/JvmAnalysisPlugin.xml index 1e1e30eb3c7a..0bc88e403d9c 100644 --- a/jvm/jvm-analysis-impl/resources/META-INF/JvmAnalysisPlugin.xml +++ b/jvm/jvm-analysis-impl/resources/META-INF/JvmAnalysisPlugin.xml @@ -23,6 +23,11 @@ groupPathKey="jvm.inspections.group.name" groupKey="jvm.inspections.test.frameworks.group.name" key="jvm.inspection.test.failed.line.display.name" implementationClass="com.intellij.codeInspection.test.TestFailedLineInspection"/> + #ref()#ref() may be 'assertSame()' #loc +jvm.inspections.migrate.assertion.name=JUnit assertion can be 'assertThat()' call +jvm.inspections.migrate.assert.to.matcher.option=Statically import matcher's methods +jvm.inspections.migrate.assert.to.matcher.description=Assert expression #ref can be replaced with ''{0}'' call #loc + jvm.inspection.test.failed.line.display.name=Failed line in test jvm.inspections.thread.run.display.name=Call to 'Thread.run()' diff --git a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/test/junit/HamcrestAssertionsConverterInspection.kt b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/test/junit/HamcrestAssertionsConverterInspection.kt new file mode 100644 index 000000000000..54947e596654 --- /dev/null +++ b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/test/junit/HamcrestAssertionsConverterInspection.kt @@ -0,0 +1,209 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.codeInspection.test.junit + +import com.intellij.analysis.JvmAnalysisBundle +import com.intellij.codeInspection.* +import com.intellij.codeInspection.test.junit.HamcrestCommonClassNames.* +import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel +import com.intellij.openapi.project.Project +import com.intellij.psi.* +import com.intellij.psi.CommonClassNames.* +import com.intellij.uast.UastHintedVisitorAdapter +import com.intellij.util.castSafelyTo +import com.siyeh.ig.junit.JUnitCommonClassNames.* +import com.siyeh.ig.psiutils.TypeUtils +import org.jetbrains.uast.* +import org.jetbrains.uast.generate.UastElementFactory +import org.jetbrains.uast.generate.getUastElementFactory +import org.jetbrains.uast.generate.replace +import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor +import javax.swing.JComponent + +class HamcrestAssertionsConverterInspection : AbstractBaseUastLocalInspectionTool() { + @JvmField + var myStaticallyImportMatchers = true + + override fun createOptionsPanel(): JComponent = SingleCheckboxOptionsPanel( + JvmAnalysisBundle.message("jvm.inspections.migrate.assert.to.matcher.option"), this, "myStaticallyImportMatchers" + ) + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { + val matcherFqn = + JavaPsiFacade.getInstance(holder.project).findClass(ORG_HAMCREST_MATCHERS, holder.file.resolveScope)?.qualifiedName + ?: JavaPsiFacade.getInstance(holder.project).findClass(ORG_HAMCREST_CORE_MATCHERS, holder.file.resolveScope)?.qualifiedName + ?: return PsiElementVisitor.EMPTY_VISITOR + + return UastHintedVisitorAdapter.create( + holder.file.language, + HamcrestAssertionsConverterVisitor(holder, matcherFqn), + arrayOf(UCallExpression::class.java), + directOnly = true + ) + } +} + +private class HamcrestAssertionsConverterVisitor( + private val holder: ProblemsHolder, + private val matcherFqn: String +) : AbstractUastNonRecursiveVisitor() { + private fun isBooleanAssert(methodName: String) = methodName == "assertTrue" || methodName == "assertFalse" + + override fun visitCallExpression(node: UCallExpression): Boolean { + val methodName = node.methodName ?: return true + if (!JUNIT_ASSERT_METHODS.contains(methodName)) return true + val method = node.resolveToUElement() ?: return true + val methodClass = method.getContainingUClass() ?: return true + if (methodClass.qualifiedName != ORG_JUNIT_ASSERT && methodClass.qualifiedName != JUNIT_FRAMEWORK_ASSERT) return true + if (isBooleanAssert(methodName)) { + val args = node.valueArguments + val resolveScope = node.sourcePsi?.resolveScope ?: return true + val psiFacade = JavaPsiFacade.getInstance(holder.project) + if (args.last() is UBinaryExpression && psiFacade.findClass(ORG_HAMCREST_NUMBER_ORDERING_COMPARISON, resolveScope) == null) return true + } + val message = JvmAnalysisBundle.message("jvm.inspections.migrate.assert.to.matcher.description", "assertThat()") + holder.registerUProblem(node, message, MigrateToAssertThatQuickFix(matcherFqn)) + return true + } + + companion object { + private val JUNIT_ASSERT_METHODS = listOf( + "assertArrayEquals", + "assertEquals", "assertNotEquals", + "assertSame", "assertNotSame", + "assertNotNull", "assertNull", + "assertTrue", "assertFalse" + ) + } +} + +private class MigrateToAssertThatQuickFix(val matcherClassFqn: String) : LocalQuickFix { + override fun getFamilyName(): String = CommonQuickFixBundle.message("fix.replace.with.x", "assertThat()") + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val element = descriptor.psiElement ?: return + val call = element.getUastParentOfType() ?: return + val factory = call.getUastElementFactory(project) ?: return + val methodName = call.methodName ?: return + val arguments = call.valueArguments.toMutableList() + val method = call.resolveToUElement()?.castSafelyTo() ?: return + val message = if (TypeUtils.typeEquals(JAVA_LANG_STRING, method.uastParameters.first().type)) { + arguments.removeFirst() + } else null + val (left, right) = when (methodName) { + "assertTrue", "assertFalse" -> { + val conditionArgument = arguments.lastOrNull() ?: return + when (conditionArgument) { + is UBinaryExpression -> { + val operator = conditionArgument.operator.normalize(methodName) + val matchExpression = factory.buildMatchExpression(operator, conditionArgument.rightOperand) ?: return + conditionArgument.leftOperand to matchExpression + } + is UQualifiedReferenceExpression -> { + val conditionCall = conditionArgument.selector.castSafelyTo() ?: return + val conditionMethodName = conditionCall.methodName ?: return + val matchExpression = if (methodName.contains("False")) { + factory.createMatchExpression( + "not", factory.buildMatchExpression(conditionMethodName, conditionArgument.receiver, conditionCall.valueArguments.first()) + ) + } else { + factory.buildMatchExpression(conditionMethodName, conditionArgument.receiver, conditionCall.valueArguments.first()) + } ?: return + conditionArgument.receiver to matchExpression + } + else -> return + } + } + "assertEquals", "assertArrayEquals" -> { + val matchExpression = factory.createMatchExpression("is", arguments.first()) ?: return + arguments.last() to matchExpression + } + "assertNotEquals" -> { + val matchExpression = factory.createMatchExpression( + "not", factory.createMatchExpression("is", arguments.first()) + ) ?: return + arguments.last() to matchExpression + } + "assertSame" -> { + val matchExpression = factory.createMatchExpression("sameInstance", arguments.first()) ?: return + arguments.last() to matchExpression + } + "assertNotSame" -> { + val matchExpression = factory.createMatchExpression( + "not", factory.createMatchExpression("sameInstance", arguments.first()) + ) ?: return + arguments.last() to matchExpression + } + "assertNull" -> { + val matchExpression = factory.createMatchExpression("nullValue") ?: return + arguments.first() to matchExpression + } + "assertNotNull" -> { + val matchExpression = factory.createMatchExpression("notNullValue") ?: return + arguments.first() to matchExpression + } + else -> return + } + + val assertThatCall = factory.createAssertThat(listOfNotNull(message, left, right)) ?: return + call.getQualifiedParentOrThis().replace(assertThatCall) + } + + private fun UastElementFactory.createAssertThat(params: List): UExpression? { + val matchAssert = createQualifiedReference("org.hamcrest.MatcherAssert", null) ?: return null + return createCallExpression(matchAssert, "assertThat", params, null, UastCallKind.METHOD_CALL) + ?.getQualifiedParentOrThis() + } + + private fun UastElementFactory.createMatchExpression(name: String, parameter: UExpression? = null): UExpression? { + val paramList = if (parameter == null) emptyList() else listOf(parameter) + val matcher = createQualifiedReference(matcherClassFqn, null) ?: return null + return createCallExpression(matcher, name, paramList, null, UastCallKind.METHOD_CALL)?.getQualifiedParentOrThis() + } + + private fun UExpression.isPrimitiveType() = getExpressionType() is PsiPrimitiveType + + private fun UastElementFactory.createIdEqualsExpression(param: UExpression) = + if (param.isPrimitiveType()) createMatchExpression("is", param) else createMatchExpression("sameInstance", param) + + private fun UastBinaryOperator.inverse(): UastBinaryOperator = when (this) { + UastBinaryOperator.EQUALS -> UastBinaryOperator.NOT_EQUALS + UastBinaryOperator.NOT_EQUALS -> UastBinaryOperator.EQUALS + UastBinaryOperator.IDENTITY_EQUALS -> UastBinaryOperator.IDENTITY_NOT_EQUALS + UastBinaryOperator.IDENTITY_NOT_EQUALS -> UastBinaryOperator.IDENTITY_EQUALS + UastBinaryOperator.GREATER -> UastBinaryOperator.LESS_OR_EQUALS + UastBinaryOperator.LESS -> UastBinaryOperator.GREATER_OR_EQUALS + UastBinaryOperator.GREATER_OR_EQUALS -> UastBinaryOperator.LESS + UastBinaryOperator.LESS_OR_EQUALS -> UastBinaryOperator.GREATER + else -> this + } + + fun UastBinaryOperator.normalize(methodName: String): UastBinaryOperator = if (methodName.contains("False")) inverse() else this + + private fun UastElementFactory.buildMatchExpression(operator: UastBinaryOperator, param: UExpression): UExpression? = when (operator) { + UastBinaryOperator.EQUALS -> createMatchExpression("is", param) + UastBinaryOperator.NOT_EQUALS -> createMatchExpression("not", createMatchExpression("is", param)) + UastBinaryOperator.IDENTITY_EQUALS -> createIdEqualsExpression(param) + UastBinaryOperator.IDENTITY_NOT_EQUALS -> createMatchExpression("not", createIdEqualsExpression(param)) + UastBinaryOperator.GREATER -> createMatchExpression("greaterThan", param) + UastBinaryOperator.LESS -> createMatchExpression("lessThan", param) + UastBinaryOperator.GREATER_OR_EQUALS -> createMatchExpression("greaterThanOrEqualTo", param) + UastBinaryOperator.LESS_OR_EQUALS -> createMatchExpression("lessThanOrEqualTo", param) + else -> null + } + + private fun UastElementFactory.buildMatchExpression(methodName: String, receiver: UExpression, param: UExpression): UExpression? { + return when (methodName) { + "contains" -> { + if (receiver.getExpressionType()?.isInheritorOf(JAVA_UTIL_COLLECTION) == true) { + return createMatchExpression("hasItem", param) + } + if (TypeUtils.typeEquals(JAVA_LANG_STRING, param.getExpressionType())) { + return createMatchExpression("containsString", param) + } + return createMatchExpression("contains", param) + } + "equals" -> createMatchExpression("is", param) + else -> null + } + } +} \ No newline at end of file diff --git a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/test/junit/HamcrestCommonClassNames.java b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/test/junit/HamcrestCommonClassNames.java new file mode 100644 index 000000000000..4b3dddac293d --- /dev/null +++ b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/test/junit/HamcrestCommonClassNames.java @@ -0,0 +1,8 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.codeInspection.test.junit; + +public class HamcrestCommonClassNames { + public static final String ORG_HAMCREST_NUMBER_ORDERING_COMPARISON = "org.hamcrest.number.OrderingComparison"; + public static final String ORG_HAMCREST_CORE_MATCHERS = "org.hamcrest.CoreMatchers"; + public static final String ORG_HAMCREST_MATCHERS = "org.hamcrest.Matchers"; +} diff --git a/jvm/jvm-analysis-java-tests/testSrc/com/intellij/codeInspection/tests/java/test/JavaHamcrestAssertionsConverterInspectionTest.kt b/jvm/jvm-analysis-java-tests/testSrc/com/intellij/codeInspection/tests/java/test/JavaHamcrestAssertionsConverterInspectionTest.kt new file mode 100644 index 000000000000..a4c2b81218e8 --- /dev/null +++ b/jvm/jvm-analysis-java-tests/testSrc/com/intellij/codeInspection/tests/java/test/JavaHamcrestAssertionsConverterInspectionTest.kt @@ -0,0 +1,176 @@ +package com.intellij.codeInspection.tests.java.test + +import com.intellij.codeInspection.tests.ULanguage +import com.intellij.codeInspection.tests.test.HamcrestAssertionsConverterInspectionTestBase + +class JavaHamcrestAssertionsConverterInspectionTest : HamcrestAssertionsConverterInspectionTestBase() { + fun `test highlighting`() { + myFixture.testHighlighting(ULanguage.JAVA, """ + import org.junit.Assert; + import java.util.Collection; + + class Foo { + void m() { + Assert.assertTrue(2 != 3); + Assert.assertTrue(2 == 3); + Assert.assertTrue(2 > 3); + Assert.assertTrue(2 < 3); + Assert.assertTrue(2 >= 3); + Assert.assertTrue(2 <= 3); + + Assert.assertFalse(2 != 3); + Assert.assertFalse(2 == 3); + Assert.assertFalse(2 > 3); + Assert.assertFalse(2 < 3); + Assert.assertFalse(2 >= 3); + Assert.assertFalse(2 <= 3); + } + + void m2() { + Assert.assertTrue("asd".equals("zxc")); + Assert.assertTrue("asd" == "zxc"); + Assert.assertTrue("asd".contains("qwe")); + } + + void m3(Collection c, String o) { + Assert.assertTrue(c.contains(o)); + Assert.assertEquals(c, o); + Assert.assertEquals("msg", c, o); + Assert.assertNotNull(c); + Assert.assertNull(c); + Assert.assertFalse(c.contains(o)); + } + + void m(int[] a, int[] b) { + Assert.assertArrayEquals(a, b); + } + } + """.trimIndent()) + } + + fun `test quickfix binary expression`() { + myFixture.testAllQuickfixes(ULanguage.JAVA, """ + import org.junit.Assert; + + class MigrationTest { + void migrate() { + Assert.assertTrue(2 != 3); + Assert.assertTrue(2 == 3); + Assert.assertTrue(2 > 3); + Assert.assertTrue(2 < 3); + Assert.assertTrue(2 >= 3); + Assert.assertTrue(2 <= 3); + Assert.assertFalse(2 != 3); + Assert.assertFalse(2 == 3); + Assert.assertFalse(2 > 3); + Assert.assertFalse(2 < 3); + Assert.assertFalse(2 >= 3); + Assert.assertFalse(2 <= 3); + } + } + """.trimIndent(), """ + import org.hamcrest.MatcherAssert; + import org.hamcrest.Matchers; + import org.junit.Assert; + + class MigrationTest { + void migrate() { + MatcherAssert.assertThat(2, Matchers.not(Matchers.is(3))); + MatcherAssert.assertThat(2, Matchers.is(3)); + MatcherAssert.assertThat(2, Matchers.greaterThan(3)); + MatcherAssert.assertThat(2, Matchers.lessThan(3)); + MatcherAssert.assertThat(2, Matchers.greaterThanOrEqualTo(3)); + MatcherAssert.assertThat(2, Matchers.lessThanOrEqualTo(3)); + MatcherAssert.assertThat(2, Matchers.is(3)); + MatcherAssert.assertThat(2, Matchers.not(Matchers.is(3))); + MatcherAssert.assertThat(2, Matchers.lessThanOrEqualTo(3)); + MatcherAssert.assertThat(2, Matchers.greaterThanOrEqualTo(3)); + MatcherAssert.assertThat(2, Matchers.lessThan(3)); + MatcherAssert.assertThat(2, Matchers.greaterThan(3)); + } + } + """.trimIndent(), "Replace with 'assertThat()'") + } + + fun `test quickfix string`() { + myFixture.testAllQuickfixes(ULanguage.JAVA, """ + import org.junit.Assert; + + class Foo { + void migrate() { + Assert.assertTrue("asd".equals("zxc")); + Assert.assertTrue("asd" == "zxc"); + Assert.assertTrue("asd".contains("qwe")); + } + } + """.trimIndent(), """ + import org.hamcrest.MatcherAssert; + import org.hamcrest.Matchers; + import org.junit.Assert; + + class Foo { + void migrate() { + MatcherAssert.assertThat("asd", Matchers.is("zxc")); + MatcherAssert.assertThat("asd", Matchers.sameInstance("zxc")); + MatcherAssert.assertThat("asd", Matchers.containsString("qwe")); + } + } + """.trimIndent(), "Replace with 'assertThat()'") + } + + fun `test quickfix collection`() { + myFixture.testAllQuickfixes(ULanguage.JAVA, """ + import org.junit.Assert; + import java.util.Collection; + + class Foo { + void migrate(Collection c, String o) { + Assert.assertTrue(c.contains(o)); + Assert.assertEquals(c, o); + Assert.assertEquals("msg", c, o); + Assert.assertNotNull(c); + Assert.assertNull(c); + Assert.assertFalse(c.contains(o)); + } + } + """.trimIndent(), """ + import org.hamcrest.MatcherAssert; + import org.hamcrest.Matchers; + import org.junit.Assert; + import java.util.Collection; + + class Foo { + void migrate(Collection c, String o) { + MatcherAssert.assertThat(c, Matchers.hasItem(o)); + MatcherAssert.assertThat(o, Matchers.is(c)); + MatcherAssert.assertThat("msg", o, Matchers.is(c)); + MatcherAssert.assertThat(c, Matchers.notNullValue()); + MatcherAssert.assertThat(c, Matchers.nullValue()); + MatcherAssert.assertThat(c, Matchers.not(Matchers.hasItem(o))); + } + } + """.trimIndent(), "Replace with 'assertThat()'") + } + + fun `test quickfix array`() { + myFixture.testAllQuickfixes(ULanguage.JAVA, """ + import org.junit.Assert; + + class Foo { + void migrate(int[] a, int[] b) { + Assert.assertArrayEquals(a, b); + } + } + """.trimIndent(), """ + import org.hamcrest.MatcherAssert; + import org.hamcrest.Matchers; + import org.junit.Assert; + + class Foo { + void migrate(int[] a, int[] b) { + MatcherAssert.assertThat(b, Matchers.is(a)); + } + } + """.trimIndent(), "Replace with 'assertThat()'") + } +} \ No newline at end of file diff --git a/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/codeInspection/tests/kotlin/test/KotlinHamcrestAssertionsConverterInspectionTest.kt b/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/codeInspection/tests/kotlin/test/KotlinHamcrestAssertionsConverterInspectionTest.kt new file mode 100644 index 000000000000..b7cdeb53774e --- /dev/null +++ b/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/codeInspection/tests/kotlin/test/KotlinHamcrestAssertionsConverterInspectionTest.kt @@ -0,0 +1,187 @@ +package com.intellij.codeInspection.tests.kotlin.test + +import com.intellij.codeInspection.tests.ULanguage +import com.intellij.codeInspection.tests.test.HamcrestAssertionsConverterInspectionTestBase +import com.intellij.openapi.module.Module +import com.intellij.openapi.roots.ContentEntry +import com.intellij.openapi.roots.ModifiableRootModel +import com.intellij.testFramework.LightProjectDescriptor +import com.intellij.testFramework.PsiTestUtil +import com.intellij.util.PathUtil +import java.io.File + +class KotlinHamcrestAssertionsConverterInspectionTest : HamcrestAssertionsConverterInspectionTestBase() { + override fun getProjectDescriptor(): LightProjectDescriptor = object : JUnitProjectDescriptor(languageLevel) { + override fun configureModule(module: Module, model: ModifiableRootModel, contentEntry: ContentEntry) { + super.configureModule(module, model, contentEntry) + val jar = File(PathUtil.getJarPathForClass(JvmStatic::class.java)) + PsiTestUtil.addLibrary(model, "kotlin-stdlib", jar.parent, jar.name) + } + } + + fun `test highlighting`() { + myFixture.testHighlighting(ULanguage.KOTLIN, """ + import org.junit.Assert + + class Foo { + fun m() { + Assert.assertTrue(2 != 3) + Assert.assertTrue(2 == 3) + Assert.assertTrue(2 > 3) + Assert.assertTrue(2 < 3) + Assert.assertTrue(2 >= 3) + Assert.assertTrue(2 <= 3) + + Assert.assertFalse(2 != 3) + Assert.assertFalse(2 == 3) + Assert.assertFalse(2 > 3) + Assert.assertFalse(2 < 3) + Assert.assertFalse(2 >= 3) + Assert.assertFalse(2 <= 3) + } + + fun m2() { + Assert.assertTrue("asd".equals("zxc")) + Assert.assertTrue("asd" == "zxc") + } + + fun m3(c: Collection, o: String) { + Assert.assertTrue(c.contains(o)) + Assert.assertEquals(c, o) + Assert.assertEquals("msg", c, o) + Assert.assertNotNull(c) + Assert.assertNull(c) + Assert.assertFalse(c.contains(o)) + } + + fun m(a: IntArray, b: IntArray) { + Assert.assertArrayEquals(a, b) + } + } + """.trimIndent()) + } + + fun `test quickfix binary expression`() { + myFixture.testAllQuickfixes(ULanguage.KOTLIN, """ + import org.junit.Assert + + class MigrationTest { + fun migrate() { + Assert.assertTrue(2 != 3) + Assert.assertTrue(2 == 3) + Assert.assertTrue(2 > 3) + Assert.assertTrue(2 < 3) + Assert.assertTrue(2 >= 3) + Assert.assertTrue(2 <= 3) + Assert.assertFalse(2 != 3) + Assert.assertFalse(2 == 3) + Assert.assertFalse(2 > 3) + Assert.assertFalse(2 < 3) + Assert.assertFalse(2 >= 3) + Assert.assertFalse(2 <= 3) + } + } + """.trimIndent(), """ + import org.hamcrest.MatcherAssert + import org.hamcrest.Matchers + import org.junit.Assert + + class MigrationTest { + fun migrate() { + MatcherAssert.assertThat(2, Matchers.not(Matchers.`is`(3))) + MatcherAssert.assertThat(2, Matchers.`is`(3)) + MatcherAssert.assertThat(2, Matchers.greaterThan(3)) + MatcherAssert.assertThat(2, Matchers.lessThan(3)) + MatcherAssert.assertThat(2, Matchers.greaterThanOrEqualTo(3)) + MatcherAssert.assertThat(2, Matchers.lessThanOrEqualTo(3)) + MatcherAssert.assertThat(2, Matchers.`is`(3)) + MatcherAssert.assertThat(2, Matchers.not(Matchers.`is`(3))) + MatcherAssert.assertThat(2, Matchers.lessThanOrEqualTo(3)) + MatcherAssert.assertThat(2, Matchers.greaterThanOrEqualTo(3)) + MatcherAssert.assertThat(2, Matchers.lessThan(3)) + MatcherAssert.assertThat(2, Matchers.greaterThan(3)) + } + } + """.trimIndent(), "Replace with 'assertThat()'") + } + + fun `test quickfix string`() { + myFixture.testAllQuickfixes(ULanguage.KOTLIN, """ + import org.junit.Assert + + class Foo { + fun migrate() { + Assert.assertTrue("asd".equals("zxc")) + Assert.assertTrue("asd" === "zxc") + Assert.assertTrue("asd".contains("zxc")) + } + } + """.trimIndent(), """ + import org.hamcrest.MatcherAssert + import org.hamcrest.Matchers + import org.junit.Assert + + class Foo { + fun migrate() { + MatcherAssert.assertThat("asd", Matchers.`is`("zxc")) + MatcherAssert.assertThat("asd", Matchers.sameInstance("zxc")) + MatcherAssert.assertThat("asd", Matchers.containsString("zxc")) + } + } + """.trimIndent(), "Replace with 'assertThat()'") + } + + fun `test quickfix collection`() { + myFixture.testAllQuickfixes(ULanguage.KOTLIN, """ + import org.junit.Assert + + class Foo { + fun migrate(c: Collection, o: String) { + Assert.assertTrue(c.contains(o)) + Assert.assertEquals(c, o) + Assert.assertEquals("msg", c, o) + Assert.assertNotNull(c) + Assert.assertNull(c) + Assert.assertFalse(c.contains(o)) + } + } + """.trimIndent(), """ + import org.hamcrest.MatcherAssert + import org.hamcrest.Matchers + import org.junit.Assert + + class Foo { + fun migrate(c: Collection, o: String) { + MatcherAssert.assertThat(c, Matchers.hasItem(o)) + MatcherAssert.assertThat(o, Matchers.`is`(c)) + MatcherAssert.assertThat("msg", o, Matchers.`is`(c)) + MatcherAssert.assertThat(c, Matchers.notNullValue()) + MatcherAssert.assertThat(c, Matchers.nullValue()) + MatcherAssert.assertThat(c, Matchers.not(Matchers.hasItem(o))) + } + } + """.trimIndent(), "Replace with 'assertThat()'") + } + + fun `test quickfix array`() { + myFixture.testAllQuickfixes(ULanguage.KOTLIN, """ + import org.junit.Assert + + class Foo { + fun migrate(a: IntArray, b: IntArray) { + Assert.assertArrayEquals(a, b) + } + } + """.trimIndent(), """ + import org.hamcrest.MatcherAssert + import org.hamcrest.Matchers + import org.junit.Assert + + class Foo { + fun migrate(a: IntArray, b: IntArray) { + MatcherAssert.assertThat(b, Matchers.`is`(a)) + } + } + """.trimIndent(), "Replace with 'assertThat()'") + } +} \ No newline at end of file diff --git a/jvm/jvm-analysis-tests/src/com/intellij/codeInspection/tests/test/HamcrestAssertionsConverterInspectionTestBase.kt b/jvm/jvm-analysis-tests/src/com/intellij/codeInspection/tests/test/HamcrestAssertionsConverterInspectionTestBase.kt new file mode 100644 index 000000000000..7b7257d61054 --- /dev/null +++ b/jvm/jvm-analysis-tests/src/com/intellij/codeInspection/tests/test/HamcrestAssertionsConverterInspectionTestBase.kt @@ -0,0 +1,25 @@ +package com.intellij.codeInspection.tests.test + +import com.intellij.codeInspection.test.junit.HamcrestAssertionsConverterInspection +import com.intellij.codeInspection.tests.UastInspectionTestBase +import com.intellij.codeInspection.tests.test.junit.addHamcrestLibrary +import com.intellij.codeInspection.tests.test.junit.addJUnit4Library +import com.intellij.openapi.module.Module +import com.intellij.openapi.roots.ContentEntry +import com.intellij.openapi.roots.ModifiableRootModel +import com.intellij.pom.java.LanguageLevel +import com.intellij.testFramework.LightProjectDescriptor + +abstract class HamcrestAssertionsConverterInspectionTestBase : UastInspectionTestBase() { + override val inspection = HamcrestAssertionsConverterInspection() + + protected open class JUnitProjectDescriptor(languageLevel: LanguageLevel) : ProjectDescriptor(languageLevel) { + override fun configureModule(module: Module, model: ModifiableRootModel, contentEntry: ContentEntry) { + super.configureModule(module, model, contentEntry) + model.addJUnit4Library() + model.addHamcrestLibrary() + } + } + + override fun getProjectDescriptor(): LightProjectDescriptor = JUnitProjectDescriptor(sdkLevel) +} \ No newline at end of file