blob: c6146d6214a050cb2c7dabc1ff6213696223a6d7 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.refactoring.rename
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.*
import com.intellij.psi.impl.source.PostprocessReformattingAspect
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.refactoring.BaseRefactoringProcessor.ConflictsInTestsException
import com.intellij.refactoring.rename.RenameProcessor
import com.intellij.refactoring.rename.RenameUtil
import com.intellij.testFramework.fixtures.CodeInsightTestUtil
import org.jetbrains.plugins.groovy.GroovyFileType
import org.jetbrains.plugins.groovy.LightGroovyTestCase
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod
import org.jetbrains.plugins.groovy.refactoring.rename.inplace.GrVariableInplaceRenameHandler
import org.jetbrains.plugins.groovy.util.TestUtils
/**
* @author ven
*/
public class RenameTest extends LightGroovyTestCase {
@Override
protected String getBasePath() {
TestUtils.testDataPath + 'groovy/refactoring/rename/'
}
public void testClosureIt() throws Throwable { doTest(); }
public void testTo_getter() throws Throwable { doTest(); }
public void testTo_prop() throws Throwable { doTest(); }
public void testTo_setter() throws Throwable { doTest(); }
public void testScriptMethod() throws Throwable { doTest(); }
public void testParameterIsNotAUsageOfGroovyParameter() throws Exception {
myFixture.configureByText(GroovyFileType.GROOVY_FILE_TYPE, """
def foo(f) {
// Parameter
println 'Parameter' // also
return <caret>f
}
""")
def txt = "Just the Parameter word, which shouldn't be renamed"
def txtFile = myFixture.addFileToProject("a.txt", txt)
def parameter = myFixture.file.findReferenceAt(myFixture.editor.caretModel.offset).resolve()
myFixture.renameElement(parameter, "newName", true, true)
myFixture.checkResult """
def foo(newName) {
// Parameter
println 'Parameter' // also
return <caret>newName
}
"""
assertEquals txt, txtFile.text
}
public void testPreserveUnknownImports() throws Exception {
def someClass = myFixture.addClass("public class SomeClass {}")
myFixture.configureByText(GroovyFileType.GROOVY_FILE_TYPE, """
import foo.bar.Zoo
SomeClass c = new SomeClass()
Zoo zoo
""")
myFixture.renameElement(someClass, "NewClass")
myFixture.checkResult """
import foo.bar.Zoo
NewClass c = new NewClass()
Zoo zoo
"""
}
public void testRenameGetter() throws Exception {
myFixture.addFileToProject("Foo.groovy", "class Foo { def getFoo(){return 2}}")
final PsiClass clazz = myFixture.findClass("Foo")
def methods = clazz.findMethodsByName("getFoo", false)
myFixture.configureByText("a.groovy", "print new Foo().foo")
myFixture.renameElement methods[0], "get"
myFixture.checkResult "print new Foo().get()"
}
public void testRenameSetter() throws Exception {
myFixture.addFileToProject("Foo.groovy","class Foo { def setFoo(def foo){}}")
def clazz = myFixture.findClass("Foo")
def methods = clazz.findMethodsByName("setFoo", false)
myFixture.configureByText("a.groovy", "print new Foo().foo = 2")
myFixture.renameElement methods[0], "set"
myFixture.checkResult "print new Foo().set(2)"
}
public void testProperty() {
myFixture.configureByText("a.groovy", """
class Foo {
def p<caret>rop
def foo() {
print prop
print getProp()
setProp(2)
}
}""")
PsiElement field = myFixture.elementAtCaret
myFixture.renameElement field, "newName"
myFixture.checkResult """
class Foo {
def newName
def foo() {
print newName
print getNewName()
setNewName(2)
}
}"""
}
public void testPropertyWithLocalCollision() {
myFixture.configureByText("a.groovy", """
class Foo {
def p<caret>rop
def foo() {
def newName = a;
print prop
print getProp()
setProp(2)
print newName
}
}""")
PsiElement field = myFixture.elementAtCaret
myFixture.renameElement field, "newName"
myFixture.checkResult """
class Foo {
def newName
def foo() {
def newName = a;
print this.newName
print getNewName()
setNewName(2)
print newName
}
}"""
}
public void testPropertyWithFieldCollision() {
myFixture.configureByText("a.groovy", """\
class A {
String na<caret>me;
class X {
String ndame;
void foo() {
print name
print getName()
setName("a")
}
}
}""")
PsiElement field = myFixture.elementAtCaret
myFixture.renameElement field, "ndame"
myFixture.checkResult """\
class A {
String ndame;
class X {
String ndame;
void foo() {
print A.this.ndame
print A.this.getNdame()
A.this.setNdame("a")
}
}
}"""
}
public void testRenameFieldWithNonstandardName() {
def file = myFixture.configureByText("a.groovy", """
class SomeBean {
String xXx<caret> = "field"
public String getxXx() {
return "method"
}
public static void main(String[] args) {
println(new SomeBean().xXx)
println(new SomeBean().getxXx())
}
}
""")
final PsiClass clazz = myFixture.findClass('SomeBean')
myFixture.renameElement new PropertyForRename([clazz.findFieldByName('xXx', false), clazz.findMethodsByName('getxXx', false)[0]], 'xXx', PsiManager.getInstance(project)), "xXx777"
assertEquals """
class SomeBean {
String xXx777 = "field"
public String getxXx777() {
return "method"
}
public static void main(String[] args) {
println(new SomeBean().xXx777)
println(new SomeBean().getxXx777())
}
}
""", file.text
}
void testRenameClassWithConstructorWithOptionalParams() {
myFixture.configureByText('a.groovy', '''\
class Test {
def Test(def abc = null){}
}
print new Test()
print new Test(1)
''')
def clazz = myFixture.findClass('Test')
myFixture.renameElement clazz, 'Foo'
myFixture.checkResult '''\
class Foo {
def Foo(def abc = null){}
}
print new Foo()
print new Foo(1)
'''
}
public void doTest() {
final String testFile = getTestName(true).replace('$', '/') + ".test";
final List<String> list = TestUtils.readInput(TestUtils.absoluteTestDataPath + "groovy/refactoring/rename/" + testFile);
myFixture.configureByText(GroovyFileType.GROOVY_FILE_TYPE, list.get(0));
PsiReference ref = myFixture.file.findReferenceAt(myFixture.editor.caretModel.offset);
final PsiElement resolved = ref == null ? null : ref.resolve();
if (resolved instanceof PsiMethod && !(resolved instanceof GrAccessorMethod)) {
PsiMethod method = (PsiMethod)resolved;
String name = method.name;
String newName = createNewNameForMethod(name);
myFixture.renameElementAtCaret(newName);
} else if (resolved instanceof GrAccessorMethod) {
GrField field = ((GrAccessorMethod)resolved).property;
RenameProcessor processor = new RenameProcessor(myFixture.project, field, "newName", true, true);
processor.addElement(resolved, createNewNameForMethod(((GrAccessorMethod)resolved).name));
processor.run();
} else {
myFixture.renameElementAtCaret("newName");
}
PostprocessReformattingAspect.getInstance(project).doPostponedFormatting();
myFixture.checkResult(list.get(1));
}
private static String createNewNameForMethod(final String name) {
String newName = "newName";
if (name.startsWith("get")) {
newName = "get" + StringUtil.capitalize(newName);
}
else if (name.startsWith("is")) {
newName = "is" + StringUtil.capitalize(newName);
}
else if (name.startsWith("set")) {
newName = "set" + StringUtil.capitalize(newName);
}
return newName;
}
public void testRecursivePathRename() {
def file = myFixture.configureByText("SomeBean.groovy", """
class SomeBean {
SomeBean someBean<caret>
static {
new SomeBean().someBean.someBean.someBean.someBean.toString()
}
}
""")
myFixture.renameElementAtCaret "b"
assertEquals """
class SomeBean {
SomeBean b
static {
new SomeBean().b.b.b.b.toString()
}
}
""", file.text
}
public void testDontAutoRenameDynamicallyTypeUsage() throws Exception {
myFixture.configureByText "a.groovy", """
class Goo {
def pp<caret>roject() {}
}
new Goo().pproject()
def foo(p) {
p.pproject()
}
"""
def method = PsiTreeUtil.findElementOfClassAtOffset(myFixture.file, myFixture.editor.caretModel.offset, GrMethod.class, false)
def usages = RenameUtil.findUsages(method, "project", false, false, [(method):"project"])
assert !usages[0].isNonCodeUsage
assert usages[1].isNonCodeUsage
}
public void testRenameAliasImportedProperty() {
myFixture.addFileToProject("Foo.groovy", """class Foo {
static def bar
}""")
myFixture.configureByText("a.groovy", """
import static Foo.ba<caret>r as foo
print foo
print getFoo()
setFoo(2)
foo = 4""")
myFixture.renameElement myFixture.findClass("Foo").fields[0], "newName"
myFixture.checkResult """
import static Foo.newName as foo
print foo
print getFoo()
setFoo(2)
foo = 4"""
}
public void testRenameAliasImportedClass() {
myFixture.addFileToProject("Foo.groovy", """class Foo {
static def bar
}""")
myFixture.configureByText("a.groovy", """
import Foo as Bar
Bar bar = new Bar()
""")
myFixture.renameElement myFixture.findClass("Foo"), "F"
myFixture.checkResult """
import F as Bar
Bar bar = new Bar()
"""
}
public void testRenameAliasImportedMethod() {
myFixture.addFileToProject("Foo.groovy", """class Foo {
static def bar(){}
}""")
myFixture.configureByText("a.groovy", """
import static Foo.bar as foo
foo()
""")
myFixture.renameElement myFixture.findClass("Foo").findMethodsByName("bar", false)[0], "b"
myFixture.checkResult """
import static Foo.b as foo
foo()
"""
}
public void testRenameAliasImportedField() {
myFixture.addFileToProject("Foo.groovy", """class Foo {
public static bar
}""")
myFixture.configureByText("a.groovy", """
import static Foo.ba<caret>r as foo
print foo
foo = 4""")
myFixture.renameElement myFixture.findClass("Foo").fields[0], "newName"
myFixture.checkResult """
import static Foo.newName as foo
print foo
foo = 4"""
}
public void testInplaceRename() {
doInplaceRenameTest();
}
public void testInplaceRenameWithGetter() {
doInplaceRenameTest();
}
public void testInplaceRenameWithStaticField() {
doInplaceRenameTest();
}
void testInplaceRenameOfClosureImplicitParameter(){
doInplaceRenameTest()
}
public void testRenameClassWithLiteralUsages() throws Exception {
def file = myFixture.addFileToProject("aaa.groovy", """
class Foo {
Foo(int a) {}
}
def x = [2] as Foo
def y = ['super':2] as Foo
""")
myFixture.configureFromExistingVirtualFile file.virtualFile
myFixture.renameElement myFixture.findClass("Foo"), "Bar"
myFixture.checkResult """
class Bar {
Bar(int a) {}
}
def x = [2] as Bar
def y = ['super':2] as Bar
"""
}
public void testExtensionOnClassRename() {
myFixture.configureByText "Foo.gpp", "class Foo {}"
myFixture.renameElement myFixture.findClass("Foo"), "Bar"
assert "gpp", myFixture.file.virtualFile.extension
}
public void testRenameJavaUsageFail() {
myFixture.addFileToProject "Bar.java", """
class Bar {
void bar() {
new Foo().foo();
}
}"""
myFixture.configureByText "Foo.groovy", """
class Foo {
def foo() {}
}"""
try {
myFixture.renameElement myFixture.findClass("Foo").methods[0], "'newName'"
} catch (ConflictsInTestsException e) {
assertEquals "<b><code>'newName'</code></b> is not a correct identifier to use in <b><code>new Foo().foo</code></b>", e.message
return;
}
assertTrue false
}
public void testRenameJavaPrivateField() {
myFixture.addFileToProject "Foo.java", """
public class Foo {
private int field;
}"""
myFixture.configureByText "Bar.groovy", """
print new Foo(field: 2)
"""
myFixture.renameElement myFixture.findClass("Foo").fields[0], "anotherOneName"
myFixture.checkResult """
print new Foo(anotherOneName: 2)
"""
}
void testRenameProp() {
myFixture.configureByText("Foo.groovy", """
class Book {
String title
}
class Test {
def testBook(){
def book = new Book()
book.with {
title = 'Test'
}
}
}""")
new PropertyRenameHandler().invoke(project, [myFixture.findClass('Book').fields[0]] as PsiElement[], null);
myFixture.checkResult """
class Book {
String s
}
class Test {
def testBook(){
def book = new Book()
book.with {
s = 'Test'
}
}
}"""
}
private def doInplaceRenameTest() {
String prefix = "/${getTestName(false)}"
myFixture.configureByFile prefix + ".groovy";
WriteCommandAction.runWriteCommandAction project, {
CodeInsightTestUtil.doInlineRename(new GrVariableInplaceRenameHandler(), "foo", myFixture);
}
myFixture.checkResultByFile prefix + "_after.groovy"
}
void testRenameJavaGetter() {
myFixture.configureByText('J.java', '''
class J {
int ge<caret>tFoo() {return 2;}
}
''')
PsiFile groovyFile = myFixture.addFileToProject('g.groovy', '''print new J().foo''')
myFixture.renameElementAtCaret('getAbc')
assertEquals('''print new J().abc''', groovyFile.text)
}
void testMethodWithSpacesRename() {
def file = myFixture.configureByText('_A.groovy', '''\
class X {
def foo(){}
}
new X().foo()
''') as GroovyFile
def method = (file.classes[0] as GrTypeDefinition).codeMethods[0]
myFixture.renameElement(method, 'f oo');
myFixture.checkResult('''\
class X {
def 'f oo'(){}
}
new X().'f oo'()
''')
}
void testMethodWithSpacesRenameInJava() {
def file = myFixture.addFileToProject('_A.groovy', '''\
class X {
def foo(){}
}
new X().foo()
''') as GroovyFile
def method = (file.classes[0] as GrTypeDefinition).codeMethods[0]
myFixture.configureByText('Java.java', '''\
class Java {
void ab() {
new X().foo()
}
}''')
try {
myFixture.renameElement(method, 'f oo');
assert false
}
catch (ConflictsInTestsException ignored) {
assert true
}
}
void testTupleConstructor() {
myFixture.with {
configureByText('a.groovy', '''\
import groovy.transform.TupleConstructor
@TupleConstructor
class X<caret>x {}
''')
renameElementAtCaret('Yy')
checkResult("""\
import groovy.transform.TupleConstructor
@TupleConstructor
class Y<caret>y {}
""")
}
}
void testConstructor() {
myFixture.with {
configureByText('a.groovy', '''\
class Foo {
def Fo<caret>o() {}
}
''')
renameElementAtCaret('Bar')
checkResult('''\
class Bar {
def Ba<caret>r() {}
}
''')
}
}
void testStringNameForMethod() {
myFixture.with {
configureByText(GroovyFileType.GROOVY_FILE_TYPE, 'def fo<caret>o() {}')
renameElementAtCaret('import')
checkResult("def 'import'() {}")
}
}
void testConstructorAndSuper() {
myFixture.with {
configureByText(GroovyFileType.GROOVY_FILE_TYPE, '''\
class B<caret>ase {
def Base() {}
}
class Inheritor extends Base {
def Inheritor() {
super()
}
}
''')
renameElementAtCaret('Bassse')
checkResult('''\
class Bassse {
def Bassse() {}
}
class Inheritor extends Bassse {
def Inheritor() {
super()
}
}
''')
}
}
void testOverridenMethodWithOptionalParams() {
myFixture.with {
configureByText(GroovyFileType.GROOVY_FILE_TYPE, '''\
class Base {
void fo<caret>o(){}
}
class Inheritor extends Base {
void foo(int x = 5) {}
}
new Base().foo()
new Inheritor().foo()
new Inheritor().foo(2)
''')
renameElementAtCaret('bar')
checkResult('''\
class Base {
void ba<caret>r(){}
}
class Inheritor extends Base {
void bar(int x = 5) {}
}
new Base().bar()
new Inheritor().bar()
new Inheritor().bar(2)
''')
}
}
void testRenameScriptFile() {
myFixture.with {
final PsiFile file = configureByText('Abc.groovy', '''\
print new Abc()
''')
renameElement(file, 'Abcd.groovy')
checkResult('''\
print new Abcd()
''')
}
}
void testTraitField() {
myFixture.with {
configureByText('a.groovy', '''\
trait T {
public int f<caret>oo = 5
def bar() {
print foo
}
}
class X implements T {
def bar() {
print T__foo
}
}
trait T2 extends T {
def bar() {
print T__foo //actually this ref is unresolved since super fields are not visible in extending traits
}
}
''')
renameElementAtCaret("baz")
checkResult('''\
trait T {
public int baz = 5
def bar() {
print baz
}
}
class X implements T {
def bar() {
print T__baz
}
}
trait T2 extends T {
def bar() {
print T__foo //actually this ref is unresolved since super fields are not visible in extending traits
}
}
''')
}
}
}