blob: 6dd124886989786e6bab408bc12913934d2027b8 [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 com.intellij.psi;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.impl.PsiManagerEx;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.testFramework.IdeaTestCase;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.testFramework.SkipSlowTestLocally;
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
import com.intellij.util.Processor;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import java.io.IOException;
/**
* @author Dmitry Avdeev
*/
@SkipSlowTestLocally
public class PsiModificationTrackerTest extends LightPlatformCodeInsightFixtureTestCase {
@Override
public void setUp() throws Exception {
IdeaTestCase.initPlatformPrefix();
super.setUp();
}
public void testAnnotationNotChanged() throws Exception {
doReplaceTest("@SuppressWarnings(\"zz\")\n" +
"public class Foo { <selection></selection>}",
"hi");
}
public void testAnnotationNameChanged() throws Exception {
doReplaceTest("@Suppr<selection>ess</selection>Warnings(\"zz\")\n" +
"public class Foo { }",
"hi");
}
public void testAnnotationParameterChanged() throws Exception {
doReplaceTest("@SuppressWarnings(\"<selection>zz</selection>\")\n" +
"public class Foo { }",
"hi");
}
public void testAnnotationRemoved() throws Exception {
doReplaceTest("<selection>@SuppressWarnings(\"zz\")</selection>\n" +
"public class Foo { }",
"");
}
public void testAnnotationWithClassRemoved() throws Exception {
doReplaceTest("<selection>@SuppressWarnings(\"zz\")\n" +
"public </selection> class Foo { }",
"");
}
public void testRemoveAnnotatedMethod() throws Exception {
doReplaceTest("public class Foo {\n" +
" <selection> " +
" @SuppressWarnings(\"\")\n" +
" public void method() {}\n" +
"</selection>" +
"}",
"");
}
public void testRenameAnnotatedMethod() throws Exception {
doReplaceTest("public class Foo {\n" +
" @SuppressWarnings(\"\")\n" +
" public void me<selection>th</selection>od() {}\n" +
"}",
"zzz");
}
public void testRenameAnnotatedClass() throws Exception {
doReplaceTest(" @SuppressWarnings(\"\")\n" +
"public class F<selection>o</selection>o {\n" +
" public void method() {}\n" +
"}",
"zzz");
}
public void testRemoveAll() throws Exception {
doReplaceTest("<selection>@SuppressWarnings(\"zz\")\n" +
"public class Foo { }</selection>",
"");
}
public void testRemoveFile() throws Exception {
doTest("<selection>@SuppressWarnings(\"zz\")\n" +
"public class Foo { }</selection>",
new Processor<PsiFile>() {
@Override
public boolean process(PsiFile psiFile) {
try {
final VirtualFile vFile = psiFile.getVirtualFile();
assert vFile != null : psiFile;
FileEditorManager.getInstance(getProject()).closeFile(vFile);
vFile.delete(this);
}
catch (IOException e) {
fail(e.getMessage());
}
return false;
}
});
}
private void doReplaceTest(@NonNls String text, @NonNls final String with) {
doTest(text, new Processor<PsiFile>() {
@Override
public boolean process(PsiFile psiFile) {
replaceSelection(with);
return false;
}
});
}
private void doTest(@NonNls String text, Processor<PsiFile> run) {
PsiFile file = myFixture.configureByText(JavaFileType.INSTANCE, text);
PsiModificationTracker modificationTracker = PsiManager.getInstance(getProject()).getModificationTracker();
long count = modificationTracker.getModificationCount();
run.process(file);
assertFalse(modificationTracker.getModificationCount() == count);
}
private void replaceSelection(final String with) {
new WriteCommandAction.Simple(getProject()) {
@Override
protected void run() throws Throwable {
SelectionModel sel = myFixture.getEditor().getSelectionModel();
myFixture.getEditor().getDocument().replaceString(sel.getSelectionStart(), sel.getSelectionEnd(), with);
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
}
}.execute();
}
public void testJavaStructureModificationChangesAfterPackageDelete() {
PsiFile file = myFixture.addFileToProject("/x/y/Z.java", "text");
PsiModificationTracker modificationTracker = PsiManager.getInstance(getProject()).getModificationTracker();
long count = modificationTracker.getJavaStructureModificationCount();
file.getContainingDirectory().delete();
assertEquals(count + 1, modificationTracker.getJavaStructureModificationCount());
}
public void testClassShouldNotAppearWithoutEvents_WithPsi() throws IOException {
final VirtualFile file = myFixture.getTempDirFixture().createFile("Foo.java", "");
final Document document = FileDocumentManager.getInstance().getDocument(file);
assertNotNull(document);
new WriteCommandAction.Simple(getProject()) {
@Override
protected void run() throws Throwable {
assertNull(JavaPsiFacade.getInstance(getProject()).findClass("Foo", GlobalSearchScope.allScope(getProject())));
PsiManager psiManager = PsiManager.getInstance(getProject());
PsiModificationTracker tracker = psiManager.getModificationTracker();
long count1 = tracker.getJavaStructureModificationCount();
PsiJavaFile psiFile = (PsiJavaFile)psiManager.findFile(file);
document.insertString(0, "class Foo {}");
assertEquals(count1, tracker.getJavaStructureModificationCount()); // no PSI changes yet
//so the class should not exist
assertNull(JavaPsiFacade.getInstance(getProject()).findClass("Foo", GlobalSearchScope.allScope(getProject())));
assertSize(0, psiFile.getClasses());
assertEquals("", psiManager.findFile(file).getText());
PlatformTestUtil.tryGcSoftlyReachableObjects();
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
assertFalse(count1 == tracker.getJavaStructureModificationCount());
assertNotNull(JavaPsiFacade.getInstance(getProject()).findClass("Foo", GlobalSearchScope.allScope(getProject())));
assertEquals("class Foo {}", psiManager.findFile(file).getText());
assertEquals("class Foo {}", psiManager.findFile(file).getNode().getText());
assertSize(1, psiFile.getClasses());
}
}.execute();
}
public void testClassShouldNotAppearWithoutEvents_WithoutPsi() throws IOException {
final GlobalSearchScope allScope = GlobalSearchScope.allScope(getProject());
final JavaPsiFacade facade = JavaPsiFacade.getInstance(getProject());
final PsiManager psiManager = PsiManager.getInstance(getProject());
final PsiModificationTracker tracker = psiManager.getModificationTracker();
final VirtualFile file = myFixture.getTempDirFixture().createFile("Foo.java", "");
final Document document = FileDocumentManager.getInstance().getDocument(file);
assertNotNull(document);
new WriteCommandAction.Simple(getProject()) {
@Override
protected void run() throws Throwable {
assertNull(facade.findClass("Foo", allScope));
long count1 = tracker.getJavaStructureModificationCount();
PlatformTestUtil.tryGcSoftlyReachableObjects();
assertNull(PsiDocumentManager.getInstance(getProject()).getCachedPsiFile(document));
document.insertString(0, "class Foo {}");
assertFalse(count1 == tracker.getJavaStructureModificationCount());
assertTrue(PsiDocumentManager.getInstance(getProject()).isCommitted(document));
assertNotNull(facade.findClass("Foo", allScope));
PsiJavaFile psiFile = (PsiJavaFile)psiManager.findFile(file);
assertSize(1, psiFile.getClasses());
assertEquals("class Foo {}", psiFile.getText());
assertEquals("class Foo {}", psiFile.getNode().getText());
}
}.execute();
}
public void testClassShouldNotDisappearWithoutEvents() throws IOException {
new WriteCommandAction.Simple(getProject()) {
@Override
protected void run() throws Throwable {
PsiModificationTracker tracker = PsiManager.getInstance(getProject()).getModificationTracker();
long count0 = tracker.getJavaStructureModificationCount();
final VirtualFile file = myFixture.addFileToProject("Foo.java", "class Foo {}").getVirtualFile();
final Document document = FileDocumentManager.getInstance().getDocument(file);
assertNotNull(document);
assertNotNull(JavaPsiFacade.getInstance(getProject()).findClass("Foo", GlobalSearchScope.allScope(getProject())));
long count1 = tracker.getJavaStructureModificationCount();
assertFalse(count1 == count0);
document.deleteString(0, document.getTextLength());
// some plugins (e.g. Copyright) hold file reference in an invokeLater runnable, let them pass
UIUtil.dispatchAllInvocationEvents();
// gc softly-referenced file and AST
PlatformTestUtil.tryGcSoftlyReachableObjects();
final PsiManagerEx psiManager = (PsiManagerEx)PsiManager.getInstance(getProject());
assertNull(psiManager.getFileManager().getCachedPsiFile(file));
assertEquals(count1, tracker.getJavaStructureModificationCount()); // no PSI changes yet
//so the class should still be there
assertNotNull(JavaPsiFacade.getInstance(getProject()).findClass("Foo", GlobalSearchScope.allScope(getProject())));
assertSize(1, ((PsiJavaFile)psiManager.findFile(file)).getClasses());
assertEquals("class Foo {}", psiManager.findFile(file).getText());
PlatformTestUtil.tryGcSoftlyReachableObjects();
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
assertFalse(count1 == tracker.getJavaStructureModificationCount());
assertNull(JavaPsiFacade.getInstance(getProject()).findClass("Foo", GlobalSearchScope.allScope(getProject())));
}
}.execute();
}
}