diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/AbstractApplyAndRevertTestCase.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/AbstractApplyAndRevertTestCase.java index c35d19e89445..7981be7f01a3 100644 --- a/java/java-tests/testSrc/com/intellij/java/propertyBased/AbstractApplyAndRevertTestCase.java +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/AbstractApplyAndRevertTestCase.java @@ -18,6 +18,7 @@ package com.intellij.java.propertyBased; import com.intellij.codeInspection.ex.InspectionProfileImpl; import com.intellij.history.Label; import com.intellij.history.LocalHistory; +import com.intellij.history.LocalHistoryException; import com.intellij.ide.impl.ProjectUtil; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; @@ -33,6 +34,7 @@ import com.intellij.openapi.editor.EditorFactory; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.event.DocumentListener; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; @@ -43,19 +45,20 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiJavaFile; +import com.intellij.psi.PsiManager; import com.intellij.psi.impl.source.PostprocessReformattingAspect; import com.intellij.psi.search.FilenameIndex; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.testFramework.CompilerTester; -import com.intellij.testFramework.PlatformTestCase; -import com.intellij.testFramework.TestDataProvider; -import com.intellij.testFramework.UsefulTestCase; +import com.intellij.testFramework.*; +import com.intellij.util.ThrowableRunnable; import com.intellij.util.containers.ContainerUtil; import slowCheck.Generator; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; import java.util.stream.Collectors; public abstract class AbstractApplyAndRevertTestCase extends PlatformTestCase { @@ -73,18 +76,28 @@ public abstract class AbstractApplyAndRevertTestCase extends PlatformTestCase { return Generator.sampledFrom(allFiles); } + protected Generator psiJavaFiles() { + return javaFiles().map(vf -> (PsiJavaFile)PsiManager.getInstance(myProject).findFile(vf)); + } + protected static void restrictChangesToDocument(Document document, Runnable r) { + watchDocumentChanges(r::run, event -> { + Document changed = event.getDocument(); + if (changed != document) { + VirtualFile file = FileDocumentManager.getInstance().getFile(changed); + if (file != null && file.isInLocalFileSystem()) { + throw new AssertionError("Unexpected document change: " + changed); + } + } + }); + } + + private static void watchDocumentChanges(ThrowableRunnable r, final Consumer eventHandler) throws E { Disposable disposable = Disposer.newDisposable(); EditorFactory.getInstance().getEventMulticaster().addDocumentListener(new DocumentListener() { @Override - public void beforeDocumentChange(DocumentEvent event) { - Document changed = event.getDocument(); - if (changed != document) { - VirtualFile file = FileDocumentManager.getInstance().getFile(changed); - if (file != null && file.isInLocalFileSystem()) { - throw new AssertionError("Unexpected document change: " + changed); - } - } + public void documentChanged(DocumentEvent event) { + eventHandler.accept(event); } }, disposable); try { @@ -96,24 +109,50 @@ public abstract class AbstractApplyAndRevertTestCase extends PlatformTestCase { protected void changeAndRevert(Runnable r) { Label label = LocalHistory.getInstance().putUserLabel(myProject, "changeAndRevert"); + boolean failed = false; try { r.run(); } + catch (Throwable e) { + failed = true; + throw e; + } finally { + restoreEverything(label, failed); + } + } + + private void restoreEverything(Label label, boolean failed) { + try { WriteAction.run(() -> { - try { - PostprocessReformattingAspect.getInstance(myProject).doPostponedFormatting(); - FileDocumentManager.getInstance().saveAllDocuments(); - label.revert(myProject, myProject.getBaseDir()); - PsiDocumentManager.getInstance(myProject).commitAllDocuments(); - assertEmpty(PsiDocumentManager.getInstance(myProject).getUncommittedDocuments()); - assertEmpty(FileDocumentManager.getInstance().getUnsavedDocuments()); - } - catch (Throwable e) { - e.printStackTrace(); - } + PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject); + new RunAll( + () -> PostprocessReformattingAspect.getInstance(myProject).doPostponedFormatting(), + () -> FileEditorManagerEx.getInstanceEx(myProject).closeAllFiles(), + () -> FileDocumentManager.getInstance().saveAllDocuments(), + () -> revertVfs(label, documentManager), + () -> documentManager.commitAllDocuments(), + () -> assertEmpty(documentManager.getUncommittedDocuments()), + () -> assertEmpty(FileDocumentManager.getInstance().getUnsavedDocuments()) + ).run(); }); } + catch (Throwable e) { + if (failed) { + LOG.info("Exceptions while restoring state", e); + } else { + throw e; + } + } + } + + private void revertVfs(Label label, PsiDocumentManager documentManager) throws LocalHistoryException { + watchDocumentChanges(() -> label.revert(myProject, myProject.getBaseDir()), + __ -> { + if (documentManager.getUncommittedDocuments().length > 3) { + documentManager.commitAllDocuments(); + } + }); } @Override diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/ActionOnRange.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/ActionOnRange.java index 5b8138d3e912..cfbe3d07950a 100644 --- a/java/java-tests/testSrc/com/intellij/java/propertyBased/ActionOnRange.java +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/ActionOnRange.java @@ -17,6 +17,7 @@ package com.intellij.java.propertyBased; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.RangeMarker; +import com.intellij.openapi.util.Segment; import com.intellij.openapi.util.TextRange; import org.jetbrains.annotations.Nullable; @@ -24,7 +25,7 @@ import org.jetbrains.annotations.Nullable; * @author peter */ class ActionOnRange { - private final RangeMarker myMarker; + protected final RangeMarker myMarker; private TextRange finalRange; ActionOnRange(Document document, int start, int end) { @@ -36,6 +37,10 @@ class ActionOnRange { return range == null ? -1 : range.getStartOffset(); } + Segment getCurrentRange() { + return finalRange == null ? myMarker : finalRange; + } + @Nullable TextRange getFinalRange() { if (finalRange == null) { diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/AddNullArgument.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/AddNullArgument.java new file mode 100644 index 000000000000..1cca42e697ff --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/AddNullArgument.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2017 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.java.propertyBased; + +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiMethodCallExpression; +import com.intellij.psi.util.PsiTreeUtil; + +class AddNullArgument extends FilePsiMutation { + + AddNullArgument(PsiFile file) { + super(file); + } + + @Override + protected void performMutation() { + PsiTreeUtil.findChildrenOfType(myFile, PsiMethodCallExpression.class).stream() + .filter(PsiElement::isValid) + .filter(call -> call.getArgumentList().getExpressions().length > 1) + .forEach(call -> call.getArgumentList().add( + JavaPsiFacade.getElementFactory(myFile.getProject()).createExpressionFromText("null", call))); + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/ApplyRandomIntentionsTest.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/ApplyRandomIntentionsTest.java index 4015167a4576..3ab7fac2cbc0 100644 --- a/java/java-tests/testSrc/com/intellij/java/propertyBased/ApplyRandomIntentionsTest.java +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/ApplyRandomIntentionsTest.java @@ -15,18 +15,20 @@ */ package com.intellij.java.propertyBased; -import com.intellij.openapi.command.WriteCommandAction; -import com.intellij.psi.*; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiManager; +import com.intellij.psi.impl.PsiDocumentManagerImpl; import com.intellij.psi.util.PsiModificationTracker; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.testFramework.SkipSlowTestLocally; import com.intellij.util.SystemProperties; -import org.jetbrains.annotations.Nullable; -import slowCheck.*; +import com.intellij.util.containers.ContainerUtil; +import slowCheck.CheckerSettings; +import slowCheck.Generator; +import slowCheck.IntDistribution; +import slowCheck.PropertyChecker; -import java.util.Objects; +import java.util.List; import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Consumer; @SkipSlowTestLocally public class ApplyRandomIntentionsTest extends AbstractApplyAndRevertTestCase { @@ -34,109 +36,18 @@ public class ApplyRandomIntentionsTest extends AbstractApplyAndRevertTestCase { @Override public void setUp() throws Exception { super.setUp(); + ((PsiDocumentManagerImpl)PsiDocumentManager.getInstance(myProject)).disableBackgroundCommit(getTestRootDisposable()); + } + + public void testIntentionsInDifferentFiles() throws Throwable { initCompiler(); - } - - public void testOpenFilesAndRevert() throws Throwable { - doOpenFilesAndRevert(null); - } - - public void testOpenFilesAndRevertDeleteForeachInitializers() throws Throwable { - doOpenFilesAndRevert(psiFile -> { - WriteCommandAction.runWriteCommandAction(myProject, () -> PsiTreeUtil.findChildrenOfType(psiFile, PsiForStatement.class).stream() - .limit(20) - .forEach(stmt -> stmt.getInitialization().delete())); - }); - } - - public void testOpenFilesAndRevertDeleteSecondArgument() throws Throwable { - doOpenFilesAndRevert(psiFile -> { - WriteCommandAction.runWriteCommandAction(myProject, () -> PsiTreeUtil.findChildrenOfType(psiFile, PsiCallExpression.class) - .stream() - .filter(PsiElement::isValid) - .map(PsiCall::getArgumentList) - .filter(Objects::nonNull) - .filter(argList -> argList.getExpressions().length > 1) - .limit(20) - .forEach(argList -> { - if (!argList.isValid()) return; - PsiExpression arg = argList.getExpressions()[1]; - if (!arg.isValid()) return; - arg.delete(); - })); - }); - } - - public void testOpenFilesAndRevertAddNullArgument() throws Throwable { - doOpenFilesAndRevert(psiFile -> { - WriteCommandAction.runWriteCommandAction(myProject, - () -> PsiTreeUtil.findChildrenOfType(psiFile, PsiMethodCallExpression.class).stream() - .filter(PsiElement::isValid) - .filter(call -> call.getArgumentList().getExpressions().length > 1) - .forEach(call -> call.getArgumentList().add(JavaPsiFacade.getElementFactory(myProject).createExpressionFromText("null", call)))); - }); - } - - public void testMakeAllMethodsVoid() throws Throwable { - doOpenFilesAndRevert(psiFile -> { - WriteCommandAction.runWriteCommandAction(myProject, - () -> PsiTreeUtil.findChildrenOfType(psiFile, PsiMethod.class).stream() - .filter(method -> method.getReturnTypeElement() != null) - .forEach(method -> method.getReturnTypeElement().replace(JavaPsiFacade.getElementFactory(myProject).createTypeElement(PsiType.VOID)))); - }); - } - - private void doOpenFilesAndRevert(@Nullable Consumer mutation) throws Throwable { - PsiManager psiManager = PsiManager.getInstance(myProject); - CheckerSettings settings = CheckerSettings.DEFAULT_SETTINGS; - PropertyChecker.forAll(settings.withIterationCount(10), javaFiles(), file -> { - if (mutation == null) { - checkCompiles(myCompilerTester.rebuild()); - } - - PsiFile psiFile = psiManager.findFile(file); - if (psiFile == null) return false; - - Generator genInvocation = Generator.from(data -> InvokeIntention.generate(psiFile, data)).noShrink(); - PropertyChecker.forAll(settings.withIterationCount(20), Generator.nonEmptyLists(genInvocation), list -> { - PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject); - changeAndRevert(() -> { - if (mutation != null) { - mutation.accept(psiFile); - } - documentManager.commitAllDocuments(); - long modCount = psiManager.getModificationTracker().getModificationCount(); - for (InvokeIntention invocation : list) { - String textBefore = psiFile.getText(); - try { - invocation.invokeIntention(); - } - catch (Throwable e) { - LOG.debug("File " + file.getName() + " text before applying " + invocation + ":\n" + textBefore); - throw e; - } - } - if (mutation == null && modCount != psiManager.getModificationTracker().getModificationCount()) { - checkCompiles(myCompilerTester.make()); - } - }); - return true; - }); - - return true; - }); - } - - public void testModificationsInDifferentFiles() throws Throwable { - PsiManager psiManager = PsiManager.getInstance(myProject); - PsiModificationTracker tracker = psiManager.getModificationTracker(); + PsiModificationTracker tracker = PsiManager.getInstance(myProject).getModificationTracker(); AtomicLong rebuildStamp = new AtomicLong(); CheckerSettings settings = CheckerSettings.DEFAULT_SETTINGS.withIterationCount(30); - Generator genIntention = - Generator.from(data -> InvokeIntention.generate(psiManager.findFile(javaFiles().generateValue(data)), data)); - + Generator genIntention = psiJavaFiles().flatMap( + file -> Generator.from(data -> InvokeIntention.generate(file, data))); PropertyChecker.forAll(settings, Generator.listsOf(genIntention.noShrink()), list -> { long startModCount = tracker.getModificationCount(); if (rebuildStamp.getAndSet(startModCount) != startModCount) { @@ -144,9 +55,8 @@ public class ApplyRandomIntentionsTest extends AbstractApplyAndRevertTestCase { } changeAndRevert(() -> { - for (InvokeIntention invocation : list) { - invocation.invokeIntention(); - } + MadTestingAction.runActions(list, myProject); + if (tracker.getModificationCount() != startModCount) { checkCompiles(myCompilerTester.make()); } @@ -155,6 +65,30 @@ public class ApplyRandomIntentionsTest extends AbstractApplyAndRevertTestCase { }); } + public void testIntentionsAndModificationsInDifferentFiles() throws Throwable { + CheckerSettings settings = CheckerSettings.DEFAULT_SETTINGS.withIterationCount(50); + Generator> genActionGroup = psiJavaFiles().flatMap( + file -> { + Generator mutation = Generator.anyOf(DeleteRange.deletePsiRange(file), + Generator.constant(new AddNullArgument(file)), + Generator.constant(new DeleteForeachInitializers(file)), + Generator.constant(new DeleteSecondArgument(file)), + Generator.constant(new MakeAllMethodsVoid(file))); + Generator allActions = Generator.frequency(2, Generator.from(data -> InvokeIntention.generate(file, data)), + 1, Generator.constant(new RehighlightAllEditors(myProject)), + 1, mutation); + return Generator.listsOf(IntDistribution.uniform(0, 5), allActions.noShrink()); + }); + + PropertyChecker.forAll(settings, Generator.listsOf(genActionGroup).map(ContainerUtil::flatten), list -> { + changeAndRevert(() -> { + //System.out.println(list); + MadTestingAction.runActions(list, myProject); + }); + return true; + }); + } + @Override protected String getTestDataPath() { return SystemProperties.getUserHome() + "/IdeaProjects/univocity-parsers"; diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/DeleteForeachInitializers.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/DeleteForeachInitializers.java new file mode 100644 index 000000000000..b86e6bbd97cc --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/DeleteForeachInitializers.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2017 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.java.propertyBased; + +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiForStatement; +import com.intellij.psi.util.PsiTreeUtil; + +import java.util.Objects; + +class DeleteForeachInitializers extends FilePsiMutation { + + DeleteForeachInitializers(PsiFile file) { + super(file); + } + + @Override + protected void performMutation() { + PsiTreeUtil.findChildrenOfType(myFile, PsiForStatement.class).stream() + .limit(20) + .map(stmt -> stmt.getInitialization()) + .filter(Objects::nonNull) + .forEach(stmt -> stmt.delete()); + } + +} diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/DeleteRange.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/DeleteRange.java new file mode 100644 index 000000000000..578062b589b6 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/DeleteRange.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2017 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.java.propertyBased; + +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtilCore; +import com.intellij.testFramework.PsiTestUtil; +import org.jetbrains.annotations.NotNull; +import slowCheck.Generator; +import slowCheck.IntDistribution; + +import java.util.Objects; + +class DeleteRange extends ActionOnRange implements MadTestingAction { + private final PsiFile myFile; + + private DeleteRange(PsiFile file, int startOffset, int endOffset) { + super(file.getViewProvider().getDocument(), startOffset, endOffset); + assert myMarker.getDocument().getTextLength() == file.getTextLength(); + myFile = file; + } + + static Generator deletePsiRange(@NotNull PsiFile psiFile) { + return Generator.from(data -> { + if (psiFile.getTextLength() == 0) return new DeleteRange(psiFile, 0, 0); + + int startOffset = Generator.integers(0, psiFile.getTextLength() - 1).generateValue(data); + PsiElement start = psiFile.findElementAt(startOffset); + PsiElement end = psiFile.findElementAt(startOffset + data.drawInt(IntDistribution.geometric(10))); + if (start == null || end == null) return null; + + PsiElement commonParent = PsiTreeUtil.findCommonParent(start, end); + return new DeleteRange(psiFile, + commonParent.getTextRange().getStartOffset(), + commonParent.getTextRange().getEndOffset()); + }).suchThat(Objects::nonNull); + } + + @Override + public String toString() { + return "DeleteRange: " + myFile.getVirtualFile().getPath() + " " + getCurrentRange(); + } + + public void performAction() { + TextRange range = getFinalRange(); + if (range == null) return; + + WriteCommandAction.runWriteCommandAction(myFile.getProject(), () -> myFile.getViewProvider().getDocument() + .deleteString(range.getStartOffset(), range.getEndOffset())); + + PsiUtilCore.ensureValid(myFile); + PsiTestUtil.checkStubsMatchText(myFile); + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/DeleteSecondArgument.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/DeleteSecondArgument.java new file mode 100644 index 000000000000..8e1112e144f8 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/DeleteSecondArgument.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2017 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.java.propertyBased; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; + +import java.util.Objects; + +class DeleteSecondArgument extends FilePsiMutation { + DeleteSecondArgument(PsiFile file) { + super(file); + } + + @Override + protected void performMutation() { + PsiTreeUtil.findChildrenOfType(myFile, PsiCallExpression.class) + .stream() + .filter(PsiElement::isValid) + .map(PsiCall::getArgumentList) + .filter(Objects::nonNull) + .filter(argList -> argList.getExpressions().length > 1) + .limit(20) + .forEach(argList -> { + if (!argList.isValid()) return; + PsiExpression arg = argList.getExpressions()[1]; + if (!arg.isValid()) return; + arg.delete(); + }); + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/FilePsiMutation.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/FilePsiMutation.java new file mode 100644 index 000000000000..bc64d54a9c23 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/FilePsiMutation.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2017 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.java.propertyBased; + +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.psi.PsiFile; +import com.intellij.testFramework.PsiTestUtil; + +/** + * @author peter + */ +abstract class FilePsiMutation implements MadTestingAction { + protected final PsiFile myFile; + + FilePsiMutation(PsiFile file) { + myFile = file; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + myFile.getVirtualFile().getPath() + "]"; + } + + @Override + public void performAction() { + WriteCommandAction.runWriteCommandAction(myFile.getProject(), this::performMutation); + PsiTestUtil.checkStubsMatchText(myFile); + } + + protected abstract void performMutation(); +} diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/InvokeIntention.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/InvokeIntention.java index e3bff0849fb3..87dc39172abc 100644 --- a/java/java-tests/testSrc/com/intellij/java/propertyBased/InvokeIntention.java +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/InvokeIntention.java @@ -15,7 +15,9 @@ */ package com.intellij.java.propertyBased; +import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditorManager; @@ -33,7 +35,7 @@ import slowCheck.Generator; import java.util.List; -class InvokeIntention extends ActionOnRange { +class InvokeIntention extends ActionOnRange implements MadTestingAction { private final PsiFile myFile; private final int myIntentionIndex; private IntentionAction myIntentionAction; @@ -57,24 +59,23 @@ class InvokeIntention extends ActionOnRange { if (name == null) { name = myIntentionAction.toString(); } - return "Intention: " + myFile.getVirtualFile().getPath() + ", offset " + getStartOffset() + ", invoke " + name; + return "InvokeIntention[" + myFile.getVirtualFile().getPath() + ", offset " + getStartOffset() + ", invoke '" + name + "']"; } - void invokeIntention() { + public void performAction() { int offset = getStartOffset(); if (offset < 0) return; Editor editor = FileEditorManager.getInstance(myFile.getProject()).openTextEditor(new OpenFileDescriptor(myFile.getProject(), myFile.getVirtualFile(), offset), true); - - CodeInsightTestFixtureImpl.instantiateAndRun(myFile, editor, new int[0], false); + + List infos = RehighlightAllEditors.highlightEditor(editor, myFile.getProject()); + boolean hasErrors = infos.stream().anyMatch(i -> i.getSeverity() == HighlightSeverity.ERROR); IntentionAction intention = myIntentionAction = getRandomIntention(editor); if (intention == null) return; - System.out.println("apply " + this); - String currentFileText = myFile.getText(); - Document changedDocument = getDocumentToBeChanged(intention); + String textBefore = changedDocument == null ? null : changedDocument.getText(); Runnable r = () -> CodeInsightTestFixtureImpl.invokeIntention(intention, myFile, editor, intention.getText()); if (changedDocument != null) { @@ -87,8 +88,8 @@ class InvokeIntention extends ActionOnRange { PsiDocumentManager.getInstance(myFile.getProject()).isDocumentBlockedByPsi(changedDocument)) { throw new AssertionError("Document is left blocked by PSI"); } - if (myFile.textMatches(currentFileText)) { - throw new AssertionError("No change was performed: " + currentFileText); + if (!hasErrors && textBefore != null && textBefore.equals(changedDocument.getText())) { + throw new AssertionError("No change was performed in " + changedDocument + "\n" + textBefore); } PsiTestUtil.checkStubsMatchText(myFile); @@ -114,8 +115,6 @@ class InvokeIntention extends ActionOnRange { actionText.startsWith("Attach annotations") || actionText.startsWith("Convert to string literal") || actionText.startsWith("Optimize imports") || // https://youtrack.jetbrains.com/issue/IDEA-173801 - actionText.startsWith("Move assignment to field declaration") || // https://youtrack.jetbrains.com/issue/IDEA-174956 - actionText.startsWith("Add on demand static import") || // https://youtrack.jetbrains.com/issue/IDEA-174965 actionText.startsWith("Make method default") || actionText.contains("to custom tags") || // changes only inspection settings actionText.startsWith("Typo: Change to...") || // doesn't change file text (starts live template) @@ -123,6 +122,10 @@ class InvokeIntention extends ActionOnRange { actionText.startsWith("Rename reference") || // doesn't change file text (starts live template) actionText.startsWith("Detail exceptions") || // can produce uncompilable code if 'catch' section contains 'instanceof's actionText.startsWith("Insert call to super method") || // super method can declare checked exceptions, unexpected at this point + actionText.startsWith("Add qualifier") || // in javadoc; displays a popup which isn't allowed in tests + actionText.startsWith("Cast to ") || // produces uncompilable code by design + actionText.startsWith("Unwrap 'else' branch (changes semantics)") || // might produce code with final variables are initialized several times + actionText.startsWith("Create missing 'switch' branches") || // if all existing branches do 'return something', we don't automatically generate compilable code for new branches actionText.startsWith("Unimplement"); } diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/MadTestingAction.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/MadTestingAction.java new file mode 100644 index 000000000000..bc9c3e5e892e --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/MadTestingAction.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2017 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.java.propertyBased; + +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiDocumentManager; + +import java.util.List; + +/** + * @author peter + */ +interface MadTestingAction { + + void performAction(); + + static void runActions(List list, Project project) { + for (int i = 0; i < list.size(); i++) { + MadTestingAction action = list.get(i); + if (i > 0 && action == list.get(i - 1)) continue; + + FileDocumentManager.getInstance().saveAllDocuments(); + PsiDocumentManager.getInstance(project).commitAllDocuments(); + + action.performAction(); + } + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/MakeAllMethodsVoid.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/MakeAllMethodsVoid.java new file mode 100644 index 000000000000..4abc873c0b94 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/MakeAllMethodsVoid.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2017 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.java.propertyBased; + +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiType; +import com.intellij.psi.util.PsiTreeUtil; + +class MakeAllMethodsVoid extends FilePsiMutation { + + MakeAllMethodsVoid(PsiFile file) { + super(file); + } + + @Override + protected void performMutation() { + PsiTreeUtil.findChildrenOfType(myFile, PsiMethod.class).stream() + .filter(method -> method.getReturnTypeElement() != null) + .forEach(method -> method.getReturnTypeElement().replace(JavaPsiFacade.getElementFactory(myFile.getProject()).createTypeElement(PsiType.VOID))); + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/RehighlightAllEditors.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/RehighlightAllEditors.java new file mode 100644 index 000000000000..99c814f64e81 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/RehighlightAllEditors.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2017 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.java.propertyBased; + +import com.intellij.codeInsight.daemon.impl.HighlightInfo; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.TextEditor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * @author peter + */ +class RehighlightAllEditors implements MadTestingAction { + private final Project myProject; + + RehighlightAllEditors(Project project) { + myProject = project; + } + + @Override + public void performAction() { + for (FileEditor editor : FileEditorManager.getInstance(myProject).getAllEditors()) { + if (editor instanceof TextEditor) { + highlightEditor(((TextEditor)editor).getEditor(), myProject); + } + } + } + + @Override + public String toString() { + return "RehighlightAllEditors"; + } + + @NotNull + static List highlightEditor(Editor editor, Project project) { + PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); + return CodeInsightTestFixtureImpl.instantiateAndRun(file, editor, new int[0], false); + } +}