Deduplicate code in AbstractQuickFixMulti{File,Module}Test

This commit is contained in:
Alexander Udalov
2017-09-19 14:18:05 +03:00
parent 113c83b47a
commit 60430828cf
2 changed files with 72 additions and 150 deletions
@@ -17,22 +17,23 @@
package org.jetbrains.kotlin.idea.quickfix package org.jetbrains.kotlin.idea.quickfix
import com.intellij.codeInsight.CodeInsightSettings import com.intellij.codeInsight.CodeInsightSettings
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.codeInsight.daemon.quickFix.ActionHint import com.intellij.codeInsight.daemon.quickFix.ActionHint
import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInspection.InspectionEP import com.intellij.codeInspection.InspectionEP
import com.intellij.codeInspection.LocalInspectionEP import com.intellij.codeInspection.LocalInspectionEP
import com.intellij.ide.highlighter.JavaFileType import com.intellij.ide.highlighter.JavaFileType
import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.fileTypes.FileType import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.fileTypes.PlainTextFileType import com.intellij.openapi.fileTypes.PlainTextFileType
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.CharsetToolkit import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.testFramework.VfsTestUtil import com.intellij.psi.PsiFile
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
import com.intellij.util.ArrayUtil import com.intellij.util.ArrayUtil
import com.intellij.util.containers.ContainerUtil import com.intellij.util.containers.ContainerUtil
@@ -155,7 +156,7 @@ abstract class AbstractQuickFixMultiFileTest : KotlinLightCodeInsightFixtureTest
DirectiveBasedActionUtils.checkForUnexpectedErrors(psiFile) DirectiveBasedActionUtils.checkForUnexpectedErrors(psiFile)
} }
doAction(text, actionShouldBeAvailable, getTestName(false)) doAction(text, file, editor, actionShouldBeAvailable, getTestName(false), this::availableActions, myFixture::doHighlighting)
val actualText = file.text val actualText = file.text
val afterText = StringBuilder(actualText).insert(editor.caretModel.offset, "<caret>").toString() val afterText = StringBuilder(actualText).insert(editor.caretModel.offset, "<caret>").toString()
@@ -218,7 +219,7 @@ abstract class AbstractQuickFixMultiFileTest : KotlinLightCodeInsightFixtureTest
DirectiveBasedActionUtils.checkForUnexpectedErrors(psiFile) DirectiveBasedActionUtils.checkForUnexpectedErrors(psiFile)
} }
doAction(text, actionShouldBeAvailable, beforeFileName) doAction(text, file, editor, actionShouldBeAvailable, beforeFileName, this::availableActions, myFixture::doHighlighting)
if (actionShouldBeAvailable) { if (actionShouldBeAvailable) {
val afterFilePath = beforeFileName.replace(".before.Main.", ".after.") val afterFilePath = beforeFileName.replace(".before.Main.", ".after.")
@@ -257,50 +258,6 @@ abstract class AbstractQuickFixMultiFileTest : KotlinLightCodeInsightFixtureTest
}, "", "") }, "", "")
} }
@Throws(Exception::class)
fun doAction(text: String, actionShouldBeAvailable: Boolean, testFilePath: String) {
val pattern = if (text.startsWith("/"))
Pattern.compile(text.substring(1, text.length - 1))
else
Pattern.compile(StringUtil.escapeToRegexp(text))
val availableActions = availableActions
val action = findActionByPattern(pattern, availableActions)
if (action == null) {
if (actionShouldBeAvailable) {
val texts = getActionsTexts(availableActions)
val infos = myFixture.doHighlighting()
TestCase.fail("Action with text '" + text + "' is not available in test " + testFilePath + "\n" +
"Available actions (" + texts.size + "): \n" +
StringUtil.join(texts, "\n") +
"\nActions:\n" +
StringUtil.join(availableActions, "\n") +
"\nInfos:\n" +
StringUtil.join(infos, "\n"))
}
else {
DirectiveBasedActionUtils.checkAvailableActionsAreExpected(file, availableActions)
}
}
else {
if (!actionShouldBeAvailable) {
TestCase.fail("Action '$text' is available (but must not) in test $testFilePath")
}
CodeInsightTestFixtureImpl.invokeIntention(action, file, editor, action.text)
if (!shouldBeAvailableAfterExecution()) {
val afterAction = findActionByPattern(pattern, this.availableActions)
if (afterAction != null) {
TestCase.fail("Action '$text' is still available after its invocation in test $testFilePath")
}
}
}
}
private val availableActions: List<IntentionAction> private val availableActions: List<IntentionAction>
get() { get() {
myFixture.doHighlighting() myFixture.doHighlighting()
@@ -310,42 +267,70 @@ abstract class AbstractQuickFixMultiFileTest : KotlinLightCodeInsightFixtureTest
class TestFile internal constructor(val path: String, val content: String) class TestFile internal constructor(val path: String, val content: String)
companion object { companion object {
private fun getActionsTexts(availableActions: List<IntentionAction>): List<String> =
availableActions.map(IntentionAction::getText)
protected fun shouldBeAvailableAfterExecution(): Boolean { private fun extraFileNamePrefix(mainFileName: String): String =
return false mainFileName.replace(".Main.kt", ".").replace(".Main.java", ".")
protected fun guessFileType(file: TestFile): FileType = when {
file.path.contains("." + KotlinFileType.EXTENSION) -> KotlinFileType.INSTANCE
file.path.contains("." + JavaFileType.DEFAULT_EXTENSION) -> JavaFileType.INSTANCE
else -> PlainTextFileType.INSTANCE
} }
private fun getActionsTexts(availableActions: List<IntentionAction>): List<String> { private fun findActionByPattern(pattern: Pattern, availableActions: List<IntentionAction>): IntentionAction? =
val texts = ArrayList<String>() availableActions.firstOrNull { pattern.matcher(it.text).matches() }
for (intentionAction in availableActions) {
texts.add(intentionAction.text)
}
return texts
}
private fun extraFileNamePrefix(mainFileName: String): String { fun doAction(
return mainFileName.replace(".Main.kt", ".").replace(".Main.java", ".") text: String,
} file: PsiFile,
editor: Editor,
actionShouldBeAvailable: Boolean,
testFilePath: String,
getAvailableActions: () -> List<IntentionAction>,
doHighlighting: () -> List<HighlightInfo>,
shouldBeAvailableAfterExecution: Boolean = false
) {
val pattern = if (text.startsWith("/"))
Pattern.compile(text.substring(1, text.length - 1))
else
Pattern.compile(StringUtil.escapeToRegexp(text))
protected fun guessFileType(file: TestFile): FileType { val availableActions = getAvailableActions()
if (file.path.contains("." + KotlinFileType.EXTENSION)) { val action = findActionByPattern(pattern, availableActions)
return KotlinFileType.INSTANCE
}
else if (file.path.contains("." + JavaFileType.DEFAULT_EXTENSION)) {
return JavaFileType.INSTANCE
}
else {
return PlainTextFileType.INSTANCE
}
}
private fun findActionByPattern(pattern: Pattern, availableActions: List<IntentionAction>): IntentionAction? { if (action == null) {
for (availableAction in availableActions) { if (actionShouldBeAvailable) {
if (pattern.matcher(availableAction.text).matches()) { val texts = getActionsTexts(availableActions)
return availableAction val infos = doHighlighting()
TestCase.fail("Action with text '" + text + "' is not available in test " + testFilePath + "\n" +
"Available actions (" + texts.size + "): \n" +
StringUtil.join(texts, "\n") +
"\nActions:\n" +
StringUtil.join(availableActions, "\n") +
"\nInfos:\n" +
StringUtil.join(infos, "\n"))
}
else {
DirectiveBasedActionUtils.checkAvailableActionsAreExpected(file, availableActions)
}
}
else {
if (!actionShouldBeAvailable) {
TestCase.fail("Action '$text' is available (but must not) in test $testFilePath")
}
CodeInsightTestFixtureImpl.invokeIntention(action, file, editor, action.text)
if (!shouldBeAvailableAfterExecution) {
val afterAction = findActionByPattern(pattern, getAvailableActions())
if (afterAction != null) {
TestCase.fail("Action '$text' is still available after its invocation in test $testFilePath")
}
} }
} }
return null
} }
} }
} }
@@ -19,10 +19,7 @@ package org.jetbrains.kotlin.idea.quickfix
import com.intellij.codeInsight.daemon.quickFix.ActionHint import com.intellij.codeInsight.daemon.quickFix.ActionHint
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase
import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler
import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.util.text.StringUtil
import com.intellij.util.ui.UIUtil
import junit.framework.ComparisonFailure import junit.framework.ComparisonFailure
import junit.framework.TestCase import junit.framework.TestCase
import org.jetbrains.kotlin.idea.inspections.findExistingEditor import org.jetbrains.kotlin.idea.inspections.findExistingEditor
@@ -34,17 +31,11 @@ import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.InTextDirectivesUtils import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File import java.io.File
import java.util.regex.Pattern
abstract class AbstractQuickFixMultiModuleTest : AbstractMultiModuleTest() { abstract class AbstractQuickFixMultiModuleTest : AbstractMultiModuleTest() {
override fun getTestDataPath() = PluginTestCaseBase.getTestDataPathBase() + "/multiModuleQuickFix/" override fun getTestDataPath() = PluginTestCaseBase.getTestDataPathBase() + "/multiModuleQuickFix/"
protected fun shouldBeAvailableAfterExecution(file: KtFile) =
InTextDirectivesUtils.isDirectiveDefined(file.text, "// SHOULD_BE_AVAILABLE_AFTER_EXECUTION")
private fun getActionsTexts(availableActions: List<IntentionAction>) = availableActions.map { it.text }
protected fun doQuickFixTest() { protected fun doQuickFixTest() {
val allFilesInProject = PluginJetFilesProvider.allFilesInProject(myProject!!) val allFilesInProject = PluginJetFilesProvider.allFilesInProject(myProject!!)
val actionFile = allFilesInProject.single { file -> val actionFile = allFilesInProject.single { file ->
@@ -58,18 +49,19 @@ abstract class AbstractQuickFixMultiModuleTest : AbstractMultiModuleTest() {
CommandProcessor.getInstance().executeCommand(project, { CommandProcessor.getInstance().executeCommand(project, {
try { try {
val psiFile = actionFile val actionHint = ActionHint.parse(actionFile, actionFileText)
val actionHint = ActionHint.parse(psiFile, actionFileText)
val text = actionHint.expectedText val text = actionHint.expectedText
val actionShouldBeAvailable = actionHint.shouldPresent() val actionShouldBeAvailable = actionHint.shouldPresent()
if (psiFile is KtFile) { if (actionFile is KtFile) {
DirectiveBasedActionUtils.checkForUnexpectedErrors(psiFile) DirectiveBasedActionUtils.checkForUnexpectedErrors(actionFile)
} }
doAction(text, actionShouldBeAvailable, actionFileName, actionFile) AbstractQuickFixMultiFileTest.doAction(
text, file, editor, actionShouldBeAvailable, actionFileName, this::availableActions, this::doHighlighting,
InTextDirectivesUtils.isDirectiveDefined(actionFile.text, "// SHOULD_BE_AVAILABLE_AFTER_EXECUTION")
)
if (actionShouldBeAvailable) { if (actionShouldBeAvailable) {
val testDirectory = File(testDataPath) val testDirectory = File(testDataPath)
@@ -106,66 +98,11 @@ abstract class AbstractQuickFixMultiModuleTest : AbstractMultiModuleTest() {
}, "", "") }, "", "")
} }
// TODO: merge with AbstractQuickFixMultiFileTest private val availableActions: List<IntentionAction>
get() {
fun doAction(text: String, actionShouldBeAvailable: Boolean, testFilePath: String, actionFile: KtFile) { doHighlighting()
val pattern = if (text.startsWith("/")) return LightQuickFixTestCase.getAvailableActions(editor, file)
Pattern.compile(text.substring(1, text.length - 1))
else
Pattern.compile(StringUtil.escapeToRegexp(text))
val availableActions = getAvailableActions()
val action = findActionByPattern(pattern, availableActions)
if (action == null) {
if (actionShouldBeAvailable) {
val texts = getActionsTexts(availableActions)
val infos = doHighlighting()
TestCase.fail("Action with text '" + text + "' is not available in test " + testFilePath + "\n" +
"Available actions (" + texts.size + "): \n" +
StringUtil.join(texts, "\n") +
"\nActions:\n" +
StringUtil.join(availableActions, "\n") +
"\nInfos:\n" +
StringUtil.join(infos, "\n"))
}
else {
DirectiveBasedActionUtils.checkAvailableActionsAreExpected(file, availableActions)
}
} }
else {
if (!actionShouldBeAvailable) {
TestCase.fail("Action '$text' is available (but must not) in test $testFilePath")
}
ShowIntentionActionsHandler.chooseActionAndInvoke(file, editor, action, action.text)
UIUtil.dispatchAllInvocationEvents()
if (!shouldBeAvailableAfterExecution(actionFile)) {
val afterAction = findActionByPattern(pattern, getAvailableActions())
if (afterAction != null) {
TestCase.fail("Action '$text' is still available after its invocation in test $testFilePath")
}
}
}
}
private fun findActionByPattern(pattern: Pattern, availableActions: List<IntentionAction>): IntentionAction? {
for (availableAction in availableActions) {
if (pattern.matcher(availableAction.text).matches()) {
return availableAction
}
}
return null
}
private fun getAvailableActions(): List<IntentionAction> {
doHighlighting()
return LightQuickFixTestCase.getAvailableActions(editor, file)
}
override fun getTestProjectJdk() = PluginTestCaseBase.mockJdk() override fun getTestProjectJdk() = PluginTestCaseBase.mockJdk()
} }