From d7427216d0ff8db900b96a631ba9c8665c3e8019 Mon Sep 17 00:00:00 2001 From: Sergey Evdokimov Date: Mon, 10 Oct 2011 17:18:15 +0400 Subject: [PATCH] Fix: IDEA-72913 (Convert JUnit assertion to assert statement intention) --- .../after.groovy.template | 5 + .../before.groovy.template | 5 + .../description.html | 6 + plugins/groovy/src/META-INF/plugin.xml | 5 + .../GroovyIntentionsBundle.properties | 2 + ...itAssertionToAssertStatementIntention.java | 135 ++++++++++++++++++ .../GroovyConvertJUnitIntentionTest.groovy | 96 +++++++++++++ 7 files changed, 254 insertions(+) create mode 100644 plugins/groovy/resources/intentionDescriptions/ConvertJunitAssertionToAssertStatementIntention/after.groovy.template create mode 100644 plugins/groovy/resources/intentionDescriptions/ConvertJunitAssertionToAssertStatementIntention/before.groovy.template create mode 100644 plugins/groovy/resources/intentionDescriptions/ConvertJunitAssertionToAssertStatementIntention/description.html create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/conversions/ConvertJunitAssertionToAssertStatementIntention.java create mode 100644 plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/GroovyConvertJUnitIntentionTest.groovy diff --git a/plugins/groovy/resources/intentionDescriptions/ConvertJunitAssertionToAssertStatementIntention/after.groovy.template b/plugins/groovy/resources/intentionDescriptions/ConvertJunitAssertionToAssertStatementIntention/after.groovy.template new file mode 100644 index 000000000000..77232d0e2b49 --- /dev/null +++ b/plugins/groovy/resources/intentionDescriptions/ConvertJunitAssertionToAssertStatementIntention/after.groovy.template @@ -0,0 +1,5 @@ +class FooTest extends TestCase { + public void testFoo() { + assert "1" == "1" : "Error" + } +} diff --git a/plugins/groovy/resources/intentionDescriptions/ConvertJunitAssertionToAssertStatementIntention/before.groovy.template b/plugins/groovy/resources/intentionDescriptions/ConvertJunitAssertionToAssertStatementIntention/before.groovy.template new file mode 100644 index 000000000000..dee86ee85aa4 --- /dev/null +++ b/plugins/groovy/resources/intentionDescriptions/ConvertJunitAssertionToAssertStatementIntention/before.groovy.template @@ -0,0 +1,5 @@ +class FooTest extends TestCase { + public void testFoo() { + assertEquals("Error", "1", "1") + } +} diff --git a/plugins/groovy/resources/intentionDescriptions/ConvertJunitAssertionToAssertStatementIntention/description.html b/plugins/groovy/resources/intentionDescriptions/ConvertJunitAssertionToAssertStatementIntention/description.html new file mode 100644 index 000000000000..99a52a9ed0ab --- /dev/null +++ b/plugins/groovy/resources/intentionDescriptions/ConvertJunitAssertionToAssertStatementIntention/description.html @@ -0,0 +1,6 @@ + + +This intention converts JUnit assertion to assert statement.
+For Groovy, it is standard to prefer the assert keyword to the JUnit assert methods. + + \ No newline at end of file diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index ae21ba67a7b1..56a592da2e73 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -880,6 +880,11 @@ intention.category.groovy/intention.category.conversions org.jetbrains.plugins.groovy.intentions.conversions.ConvertClosureToMethodIntention + + org.jetbrains.plugins.groovy.intentions.GroovyIntentionsBundle + intention.category.groovy/intention.category.conversions + org.jetbrains.plugins.groovy.intentions.conversions.ConvertJunitAssertionToAssertStatementIntention + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/GroovyIntentionsBundle.properties b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/GroovyIntentionsBundle.properties index 6cf92546be0b..776d356f7e4a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/GroovyIntentionsBundle.properties +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/GroovyIntentionsBundle.properties @@ -71,6 +71,8 @@ convert.closure.to.method.intention.name=Convert to method convert.closure.to.method.intention.family.name=Convert property to method convert.method.to.closure.intention.name=Convert to property convert.method.to.closure.intention.family.name=Convert method to property of closure type +convert.junit.assertion.to.assert.statement.intention.name=Convert JUnit assertion to assert statement +convert.junit.assertion.to.assert.statement.intention.family.name=Convert JUnit assertion to assert statement ########################################################################################################### # Convert parameter to map entry refactoring ########################################################################################################### diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/conversions/ConvertJunitAssertionToAssertStatementIntention.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/conversions/ConvertJunitAssertionToAssertStatementIntention.java new file mode 100644 index 000000000000..b5a4491e884e --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/conversions/ConvertJunitAssertionToAssertStatementIntention.java @@ -0,0 +1,135 @@ +package org.jetbrains.plugins.groovy.intentions.conversions; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.intentions.base.Intention; +import org.jetbrains.plugins.groovy.intentions.base.PsiElementPredicate; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrAssertStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Sergey Evdokimov + */ +public class ConvertJunitAssertionToAssertStatementIntention extends Intention implements PsiElementPredicate { + + private static final Pattern PATTERN = Pattern.compile("arg(\\d+)"); + + private static Map ourStatementMap = new HashMap(); + static { + ourStatementMap.put("assertNotNull", new String[]{null, "assert arg0 != null", "assert arg1 != null : arg0"}); + ourStatementMap.put("assertNull", new String[]{null, "assert arg0 == null", "assert arg1 == null : arg0"}); + + ourStatementMap.put("assertTrue", new String[]{null, "assert arg0", "assert arg1 : arg0"}); + ourStatementMap.put("assertFalse", new String[]{null, "assert !arg0", "assert !arg1 : arg0"}); + + ourStatementMap.put("assertEquals", new String[]{null, null, "assert arg0 == arg1", "assert arg1 == arg2 : arg0"}); + + ourStatementMap.put("assertSame", new String[]{null, null, "assert arg0.is(arg1)", "assert arg1.is(arg2) : arg0"}); + ourStatementMap.put("assertNotSame", new String[]{null, null, "assert !arg0.is(arg1)", "assert !arg1.is(arg2) : arg0"}); + } + + @Nullable + private static String getReplacementStatement(@NotNull PsiMethod method, @NotNull GrMethodCall methodCall) { + PsiClass containingClass = method.getContainingClass(); + if (containingClass == null) return null; + + String qualifiedName = containingClass.getQualifiedName(); + if (!"junit.framework.Assert".equals(qualifiedName) && !"groovy.util.GroovyTestCase".equals(qualifiedName)) return null; + + String[] replacementStatements = ourStatementMap.get(method.getName()); + if (replacementStatements == null) return null; + + GrArgumentList argumentList = methodCall.getArgumentList(); + if (argumentList == null) return null; + + if (argumentList.getNamedArguments().length > 0) return null; + + GrExpression[] arguments = argumentList.getExpressionArguments(); + + if (arguments.length >= replacementStatements.length) return null; + + return replacementStatements[arguments.length]; + } + + @Nullable + private static GrStatement getReplacementElement(@NotNull PsiMethod method, @NotNull GrMethodCall methodCall) { + String replacementStatement = getReplacementStatement(method, methodCall); + if (replacementStatement == null) return null; + + @SuppressWarnings("ConstantConditions") final + GrExpression[] arguments = methodCall.getArgumentList().getExpressionArguments(); + + GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(method.getProject()); + + GrAssertStatement statement = (GrAssertStatement)factory.createStatementFromText(replacementStatement); + + final Map replaceMap = new HashMap(); + + statement.acceptChildren(new GroovyRecursiveElementVisitor() { + @Override + public void visitExpression(GrExpression expression) { + Matcher matcher = PATTERN.matcher(expression.getText()); + if (matcher.matches()) { + int index = Integer.parseInt(matcher.group(1)); + replaceMap.put(expression, arguments[index]); + } + else { + super.visitExpression(expression); + } + } + }); + + for (Map.Entry entry : replaceMap.entrySet()) { + entry.getKey().replaceWithExpression(entry.getValue(), true); + } + + return statement; + } + + @Override + protected void processIntention(@NotNull PsiElement element, Project project, Editor editor) throws IncorrectOperationException { + GrMethodCall methodCall = (GrMethodCall)element; + + PsiMethod method = methodCall.resolveMethod(); + if (method == null) return; + + GrStatement replacementElement = getReplacementElement(method, methodCall); + if (replacementElement == null) return; + + ((GrMethodCall)element).replaceWithStatement(replacementElement); + } + + @NotNull + @Override + protected PsiElementPredicate getElementPredicate() { + return this; + } + + @Override + public boolean satisfiedBy(PsiElement element) { + if (!(element instanceof GrMethodCall)) return false; + + GrMethodCall methodCall = (GrMethodCall)element; + + PsiMethod method = methodCall.resolveMethod(); + if (method == null) return false; + + return getReplacementStatement(method, methodCall) != null; + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/GroovyConvertJUnitIntentionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/GroovyConvertJUnitIntentionTest.groovy new file mode 100644 index 000000000000..97ea1cd25513 --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/GroovyConvertJUnitIntentionTest.groovy @@ -0,0 +1,96 @@ +package org.jetbrains.plugins.groovy.intentions + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.psi.impl.source.PostprocessReformattingAspect +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase + +/** + * @author Sergey Evdokimov + */ +class GroovyConvertJUnitIntentionTest extends LightCodeInsightFixtureTestCase { + + public void testAssertFalse() { + doTest(""" +class A extends junit.framework.Assert { + public void testXxxx() { + assertFalse("!!!", 1 == 2) + } +} +""", """ +class A extends junit.framework.Assert { + public void testXxxx() { + assert !(1 == 2): "!!!" + } +} +""") + } + + public void testAssertEquals() { + doTest(""" +class A extends junit.framework.Assert { + public void testXxxx() { + assertEquals("!!!", 1, 2) + } +} +""", """ +class A extends junit.framework.Assert { + public void testXxxx() { + assert 1 == 2: "!!!" + } +} +""") + } + + public void testAssertNotSame() { + doTest(""" +class A extends junit.framework.Assert { + public void testXxxx() { + assertNotSame("1", "2") + } +} +""", """ +class A extends junit.framework.Assert { + public void testXxxx() { + assert !"1".is("2") + } +} +""") + } + + public void testFail() { + doTest(""" +class A extends junit.framework.Assert { + public void testXxxx() { + assertSame("1") + } +} +""", null) + } + + private void doTest(String before, String after) { + myFixture.addFileToProject("junit/framework/Assert.groovy", """ +package junit.framework; +public class Assert { + public static void assertTrue(java.lang.String message, boolean condition) { } + public static void assertTrue(boolean condition) { } + public static void assertFalse(java.lang.String message, boolean condition) { } + public static void assertEquals(java.lang.Object expected, java.lang.Object actual) { } + public static void assertNotSame(java.lang.Object expected, java.lang.Object actual) { } +} +""") + myFixture.configureByText("A.groovy", before); + + String hint = GroovyIntentionsBundle.message("convert.junit.assertion.to.assert.statement.intention.name"); + final List list = myFixture.filterAvailableIntentions(hint); + if (after == null) { + assertEmpty(list); + return; + } + + myFixture.launchAction(assertOneElement(list)); + PostprocessReformattingAspect.getInstance(getProject()).doPostponedFormatting(); + + myFixture.checkResult(after) + } + +}