KT-44043 [Sealed interfaces]: tests

So far, quick-fix tests infrastructure neither supports the check
for multiple files (several .before/.after pairs) nor multi module
structure. Provided tests are quite limited yet allow to make sure that
the fix in question appears in expected context and get activated.

^KT-44043 fixed
This commit is contained in:
Andrei Klunnyi
2021-01-04 11:29:10 +01:00
parent 43cc022613
commit eed27906e3
5 changed files with 223 additions and 1 deletions
@@ -7,25 +7,46 @@ package org.jetbrains.kotlin.idea.quickfix
import com.intellij.openapi.actionSystem.LangDataKeys
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFileSystemItem
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.parentOfType
import com.intellij.refactoring.PackageWrapper
import com.intellij.refactoring.move.MoveCallback
import com.intellij.refactoring.move.MoveHandler
import org.jetbrains.kotlin.descriptors.containingPackage
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.actions.internal.refactoringTesting.cases.*
import org.jetbrains.kotlin.idea.refactoring.move.getTargetPackageFqName
import org.jetbrains.kotlin.idea.refactoring.move.guessNewFileName
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsHandler
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsHandlerActions
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.KotlinAwareMoveFilesOrDirectoriesModel
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinNestedClassesToUpperLevelModel
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinTopLevelDeclarationsModel
import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors
import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.projectStructure.module
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
class MoveToSealedMatchingPackageFix(element: KtTypeReference) : KotlinQuickFixAction<KtTypeReference>(element) {
private val moveHandler = MoveKotlinDeclarationsHandler(false)
private val moveHandler =
if (ApplicationManager.getApplication().isUnitTestMode) {
MoveKotlinDeclarationsHandler(MoveKotlinDeclarationsHandlerTestActions())
} else {
MoveKotlinDeclarationsHandler(false)
}
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
val typeReference = element ?: return
@@ -78,4 +99,115 @@ class MoveToSealedMatchingPackageFix(element: KtTypeReference) : KotlinQuickFixA
return MoveToSealedMatchingPackageFix(annotationEntry)
}
}
}
private class MoveKotlinDeclarationsHandlerTestActions : MoveKotlinDeclarationsHandlerActions {
override fun invokeMoveKotlinTopLevelDeclarationsRefactoring(
project: Project,
elementsToMove: Set<KtNamedDeclaration>,
targetPackageName: String,
targetDirectory: PsiDirectory?,
targetFile: KtFile?,
freezeTargets: Boolean,
moveToPackage: Boolean,
moveCallback: MoveCallback?
) {
val sourceFiles = getSourceFiles(elementsToMove)
val targetFilePath =
targetFile?.virtualFile?.path ?: sourceFiles[0].virtualFile.parent.path + "/" + guessNewFileName(elementsToMove)
val model = MoveKotlinTopLevelDeclarationsModel(
project = project,
elementsToMove = elementsToMove.toList(),
targetPackage = targetPackageName,
selectedPsiDirectory = targetDirectory,
fileNameInPackage = "Derived.kt",
targetFilePath = targetFilePath,
isMoveToPackage = true,
isSearchReferences = false,
isSearchInComments = false,
isSearchInNonJavaFiles = false,
isDeleteEmptyFiles = false,
applyMPPDeclarations = false,
moveCallback = null
)
model.computeModelResult(throwOnConflicts = true).processor.run()
}
private fun getSourceFiles(elementsToMove: Collection<KtNamedDeclaration>): List<KtFile> {
return elementsToMove.map { obj: KtPureElement -> obj.containingKtFile }
.distinct()
}
override fun invokeKotlinSelectNestedClassChooser(nestedClass: KtClassOrObject, targetContainer: PsiElement?) =
doWithMoveKotlinNestedClassesToUpperLevelModel(nestedClass, targetContainer)
private fun doWithMoveKotlinNestedClassesToUpperLevelModel(nestedClass: KtClassOrObject, targetContainer: PsiElement?) {
val outerClass = nestedClass.containingClassOrObject ?: throw FailedToRunCaseException()
val newTarget = targetContainer
?: outerClass.containingClassOrObject
?: outerClass.containingFile.let { it.containingDirectory ?: it }
val packageName = getTargetPackageFqName(newTarget)?.asString() ?: ""
val model = object : MoveKotlinNestedClassesToUpperLevelModel(
project = nestedClass.project,
innerClass = nestedClass,
target = newTarget,
parameter = "",
className = nestedClass.name ?: "",
passOuterClass = false,
searchInComments = false,
isSearchInNonJavaFiles = false,
packageName = packageName,
isOpenInEditor = false
) {
override fun chooseSourceRoot(
newPackage: PackageWrapper,
contentSourceRoots: List<VirtualFile>,
initialDir: PsiDirectory?
) = contentSourceRoots.firstOrNull()
}
model.computeModelResult(throwOnConflicts = true).processor.run()
}
override fun invokeKotlinAwareMoveFilesOrDirectoriesRefactoring(
project: Project,
initialDirectory: PsiDirectory?,
elements: List<PsiFileSystemItem>,
moveCallback: MoveCallback?
) {
val targetPath =
initialDirectory?.virtualFile?.path
?: elements.firstOrNull()?.containingFile?.virtualFile?.path
?: throw NotImplementedError()
val model = KotlinAwareMoveFilesOrDirectoriesModel(
project = project,
elementsToMove = elements,
targetDirectoryName = randomDirectoryPathMutator(targetPath),
updatePackageDirective = randomBoolean(),
searchReferences = randomBoolean(),
moveCallback = null
)
project.executeCommand(MoveHandler.getRefactoringName()) {
model.computeModelResult().processor.run()
}
}
override fun showErrorHint(project: Project, editor: Editor?, message: String, title: String, helpId: String?) =
throw NotImplementedError()
override fun invokeMoveKotlinNestedClassesRefactoring(
project: Project,
elementsToMove: List<KtClassOrObject>,
originalClass: KtClassOrObject,
targetClass: KtClassOrObject,
moveCallback: MoveCallback?
) = throw NotImplementedError()
}
@@ -0,0 +1,27 @@
// FILE: DerivedOutsideOfPackage.before.kt
// "Move Derived to the package/module of SomeSealedClass" "true"
// ERROR: Inheritor of sealed class or interface declared in package <root> but it must be in package foo where base class is declared
// COMPILER_ARGUMENTS: -XXLanguage:+SealedInterfaces -XXLanguage:+AllowSealedInheritorsInDifferentFilesOfSamePackage
import foo.SomeSealedClass
class ArbitraryClass {
class Derived: <caret>SomeSealedClass()
}
// FILE: foo/SealedDeclarations.kt
package foo
sealed class SomeSealedClass
// FILE: DerivedOutsideOfPackage.after.kt
// "Move Derived to the package/module of SomeSealedClass" "true"
// ERROR: Inheritor of sealed class or interface declared in package <root> but it must be in package foo where base class is declared
// COMPILER_ARGUMENTS: -XXLanguage:+SealedInterfaces -XXLanguage:+AllowSealedInheritorsInDifferentFilesOfSamePackage
class ArbitraryClass {
<caret>}
@@ -0,0 +1,27 @@
// FILE: DerivedOutsideOfPackage.before.kt
// "Move Derived to the package/module of SomeSealedClass" "true"
// ERROR: Inheritor of sealed class or interface declared in package <root> but it must be in package foo where base class is declared
// COMPILER_ARGUMENTS: -XXLanguage:+SealedInterfaces -XXLanguage:+AllowSealedInheritorsInDifferentFilesOfSamePackage
import foo.SomeSealedClass
class Derived: <caret>SomeSealedClass()
class ArbitraryClass
// FILE: foo/SealedDeclarations.kt
package foo
sealed class SomeSealedClass
// FILE: DerivedOutsideOfPackage.after.kt
// "Move Derived to the package/module of SomeSealedClass" "true"
// ERROR: Inheritor of sealed class or interface declared in package <root> but it must be in package foo where base class is declared
// COMPILER_ARGUMENTS: -XXLanguage:+SealedInterfaces -XXLanguage:+AllowSealedInheritorsInDifferentFilesOfSamePackage
import foo.SomeSealedClass
<caret>class ArbitraryClass
@@ -3493,6 +3493,29 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
}
}
@TestMetadata("idea/testData/quickfix/moveToSealedParent")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class MoveToSealedParent extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, this, testDataFilePath);
}
public void testAllFilesPresentInMoveToSealedParent() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/moveToSealedParent"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), null, true);
}
@TestMetadata("nestedDeclarationToSealed.test")
public void testNestedDeclarationToSealed() throws Exception {
runTest("idea/testData/quickfix/moveToSealedParent/nestedDeclarationToSealed.test");
}
@TestMetadata("topLevelDeclarationToSealed.test")
public void testTopLevelDeclarationToSealed() throws Exception {
runTest("idea/testData/quickfix/moveToSealedParent/topLevelDeclarationToSealed.test");
}
}
@TestMetadata("idea/testData/quickfix/moveTypeAliasToTopLevel")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -9808,6 +9808,19 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
}
}
@TestMetadata("idea/testData/quickfix/moveToSealedParent")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class MoveToSealedParent extends AbstractQuickFixTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInMoveToSealedParent() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/moveToSealedParent"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true);
}
}
@TestMetadata("idea/testData/quickfix/moveTypeAliasToTopLevel")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)