From ad929a6577712850e4e80c61f1f6e29dd71ca64c Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Fri, 2 Oct 2015 20:38:14 +0300 Subject: [PATCH] Unit Test Tooling: Implement "Create Test" action #KT-6472 In Progress --- .../kotlin/idea/util/ApplicationUtils.kt | 18 +- .../after.kt.template | 11 ++ .../before.kt.template | 5 + .../description.html | 5 + idea/src/META-INF/plugin.xml | 5 + .../kotlin/idea/actions/JavaToKotlinAction.kt | 159 ++++++++-------- .../KotlinGenerateTestSupportActionBase.kt | 11 +- .../testIntegration/KotlinCreateTestDialog.kt | 33 ++++ .../KotlinCreateTestIntention.kt | 169 ++++++++++++++++++ .../testIntegration/testIntegrationUtils.kt | 31 ++++ .../classDelegatorToSuperclass.kt | 1 + .../FinalJavaSupertype.before.kt | 1 + .../typeMismatch/casts/typeMismatch2.kt | 1 + .../constructorParameter.kt | 1 + 14 files changed, 361 insertions(+), 90 deletions(-) create mode 100644 idea/resources/intentionDescriptions/KotlinCreateTestIntention/after.kt.template create mode 100644 idea/resources/intentionDescriptions/KotlinCreateTestIntention/before.kt.template create mode 100644 idea/resources/intentionDescriptions/KotlinCreateTestIntention/description.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinCreateTestDialog.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinCreateTestIntention.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/testIntegration/testIntegrationUtils.kt diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt index 0a06cef1a8a..2a72045dab7 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt @@ -17,8 +17,9 @@ package org.jetbrains.kotlin.idea.util.application import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.project.Project import com.intellij.openapi.command.CommandProcessor +import com.intellij.openapi.project.DumbService +import com.intellij.openapi.project.Project public fun runReadAction(action: () -> T): T { return ApplicationManager.getApplication().runReadAction(action) @@ -32,13 +33,20 @@ public fun Project.executeWriteCommand(name: String, command: () -> Unit) { CommandProcessor.getInstance().executeCommand(this, { runWriteAction(command) }, name, null) } -public fun Project.executeCommand(name: String, groupId: Any? = null, command: () -> Unit) { - CommandProcessor.getInstance().executeCommand(this, command, name, groupId) +public fun Project.executeWriteCommand(name: String, groupId: Any? = null, command: () -> T): T { + return executeCommand(name, groupId) { runWriteAction(command) } } -public fun Project.executeWriteCommand(name: String, groupId: Any? = null, command: () -> T): T { +public fun Project.executeCommand(name: String, groupId: Any? = null, command: () -> T): T { var result: T = null as T - CommandProcessor.getInstance().executeCommand(this, { result = runWriteAction(command) }, name, groupId) + CommandProcessor.getInstance().executeCommand(this, { result = command() }, name, groupId) @Suppress("USELESS_CAST") return result as T } + +public fun Project.runWithAlternativeResolveEnabled(action: () -> T): T { + var result: T = null as T + DumbService.getInstance(this).withAlternativeResolveEnabled { result = action() } + @Suppress("USELESS_CAST") + return result as T +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/KotlinCreateTestIntention/after.kt.template b/idea/resources/intentionDescriptions/KotlinCreateTestIntention/after.kt.template new file mode 100644 index 00000000000..30a1d3fc315 --- /dev/null +++ b/idea/resources/intentionDescriptions/KotlinCreateTestIntention/after.kt.template @@ -0,0 +1,11 @@ +class Foo { + fun foo() { + + } +} + +class FooTest : TestCase { + fun testFoo() { + + } +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/KotlinCreateTestIntention/before.kt.template b/idea/resources/intentionDescriptions/KotlinCreateTestIntention/before.kt.template new file mode 100644 index 00000000000..b8b9f221b08 --- /dev/null +++ b/idea/resources/intentionDescriptions/KotlinCreateTestIntention/before.kt.template @@ -0,0 +1,5 @@ +class Foo { + fun foo() { + + } +} diff --git a/idea/resources/intentionDescriptions/KotlinCreateTestIntention/description.html b/idea/resources/intentionDescriptions/KotlinCreateTestIntention/description.html new file mode 100644 index 00000000000..f51ef9a940a --- /dev/null +++ b/idea/resources/intentionDescriptions/KotlinCreateTestIntention/description.html @@ -0,0 +1,5 @@ + + +This intention generates a test case for the selected class. The generated class contains skeleton test functions for the chosen public functions. + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index c3b0e141012..0e8608dd115 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1062,6 +1062,11 @@ Kotlin + + org.jetbrains.kotlin.idea.testIntegration.KotlinCreateTestIntention + Kotlin + + 0) i else "") + ".kt" + if (!ioFile.resolveSibling(fileName).exists()) return fileName + i++ + } + } + + private fun saveResults(javaFiles: List, convertedTexts: List): List { + val result = ArrayList() + for ((psiFile, text) in javaFiles.zip(convertedTexts)) { + val virtualFile = psiFile.getVirtualFile() + val fileName = uniqueKotlinFileName(virtualFile) + try { + virtualFile.rename(this, fileName) + virtualFile.setBinaryContent(CharsetToolkit.getUtf8Bytes(text)) + result.add(virtualFile) + } + catch (e: IOException) { + MessagesEx.error(psiFile.getProject(), e.getMessage()).showLater() + } + } + return result + } + + fun convertFiles(javaFiles: List, project: Project, enableExternalCodeProcessing: Boolean = true): List { + ApplicationManager.getApplication().saveAll() + + var converterResult: JavaToKotlinConverter.FilesResult? = null + fun convert() { + val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaJavaToKotlinServices) + converterResult = converter.filesToKotlin(javaFiles, J2kPostProcessor(formatCode = true), ProgressManager.getInstance().getProgressIndicator()) + } + + val title = "Convert Java to Kotlin" + if (!ProgressManager.getInstance().runProcessWithProgressSynchronously( + { + runReadAction(::convert) + }, + title, + true, + project)) return emptyList() + + + var externalCodeUpdate: (() -> Unit)? = null + + if (enableExternalCodeProcessing && converterResult!!.externalCodeProcessing != null) { + val question = "Some code in the rest of your project may require corrections after performing this conversion. Do you want to find such code and correct it too?" + if (Messages.showOkCancelDialog(project, question, title, Messages.getQuestionIcon()) == Messages.OK) { + ProgressManager.getInstance().runProcessWithProgressSynchronously( + { + runReadAction { + externalCodeUpdate = converterResult!!.externalCodeProcessing!!.prepareWriteOperation(ProgressManager.getInstance().getProgressIndicator()) + } + }, + title, + true, + project) + } + } + + return project.executeWriteCommand("Convert files from Java to Kotlin", null) { + CommandProcessor.getInstance().markCurrentCommandAsGlobal(project) + + val newFiles = saveResults(javaFiles, converterResult!!.results) + + externalCodeUpdate?.invoke() + + newFiles.singleOrNull()?.let { + FileEditorManager.getInstance(project).openFile(it, true) + } + + newFiles.map { it.toPsiFile(project) as JetFile } + } + } + } + + override fun actionPerformed(e: AnActionEvent) { val javaFiles = selectedJavaFiles(e).toList() val project = CommonDataKeys.PROJECT.getData(e.getDataContext())!! - - var converterResult: JavaToKotlinConverter.FilesResult? = null - fun convert() { - val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaJavaToKotlinServices) - converterResult = converter.filesToKotlin(javaFiles, J2kPostProcessor(formatCode = true), ProgressManager.getInstance().getProgressIndicator()) - } - - val title = "Convert Java to Kotlin" - if (!ProgressManager.getInstance().runProcessWithProgressSynchronously( - { - runReadAction(::convert) - }, - title, - true, - project)) return - - - var externalCodeUpdate: (() -> Unit)? = null - - if (converterResult!!.externalCodeProcessing != null) { - val question = "Some code in the rest of your project may require corrections after performing this conversion. Do you want to find such code and correct it too?" - if (Messages.showOkCancelDialog(project, question, title, Messages.getQuestionIcon()) == Messages.OK) { - ProgressManager.getInstance().runProcessWithProgressSynchronously( - { - runReadAction { - externalCodeUpdate = converterResult!!.externalCodeProcessing!!.prepareWriteOperation(ProgressManager.getInstance().getProgressIndicator()) - } - }, - title, - true, - project) - } - } - - project.executeWriteCommand("Convert files from Java to Kotlin") { - CommandProcessor.getInstance().markCurrentCommandAsGlobal(project) - - val newFiles = saveResults(javaFiles, converterResult!!.results) - - externalCodeUpdate?.invoke() - - newFiles.singleOrNull()?.let { - FileEditorManager.getInstance(project).openFile(it, true) - } - } + convertFiles(javaFiles, project) } override fun update(e: AnActionEvent) { @@ -126,32 +163,4 @@ public class JavaToKotlinAction : AnAction() { } return result } - - private fun saveResults(javaFiles: List, convertedTexts: List): List { - val result = ArrayList() - for ((psiFile, text) in javaFiles.zip(convertedTexts)) { - val virtualFile = psiFile.getVirtualFile() - val fileName = uniqueKotlinFileName(virtualFile) - try { - virtualFile.rename(this, fileName) - virtualFile.setBinaryContent(CharsetToolkit.getUtf8Bytes(text)) - result.add(virtualFile) - } - catch (e: IOException) { - MessagesEx.error(psiFile.getProject(), e.getMessage()).showLater() - } - } - return result - } - - private fun uniqueKotlinFileName(javaFile: VirtualFile): String { - val ioFile = File(javaFile.getPath().replace('/', File.separatorChar)) - - var i = 0 - while (true) { - val fileName = javaFile.getNameWithoutExtension() + (if (i > 0) i else "") + ".kt" - if (!ioFile.resolveSibling(fileName).exists()) return fileName - i++ - } - } } diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt b/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt index a6f459b7042..89fe7840116 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateTestSupportActionBase.kt @@ -21,14 +21,12 @@ import com.intellij.codeInsight.generation.actions.GenerateActionPopupTemplateIn import com.intellij.codeInsight.hint.HintManager import com.intellij.ide.fileTemplates.FileTemplateManager import com.intellij.ide.fileTemplates.impl.AllFileTemplatesConfigurable -import com.intellij.lang.java.JavaLanguage import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.editor.Editor -import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.project.Project import com.intellij.openapi.ui.InputValidator import com.intellij.openapi.ui.Messages @@ -42,7 +40,6 @@ import com.intellij.testIntegration.TestFramework import com.intellij.testIntegration.TestIntegrationUtils.MethodKind import com.intellij.ui.components.JBList import com.intellij.util.IncorrectOperationException -import com.intellij.util.SmartList import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor @@ -52,6 +49,7 @@ import org.jetbrains.kotlin.idea.core.overrideImplement.generateUnsupportedOrSup import org.jetbrains.kotlin.idea.core.refactoring.j2k import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.setupEditorSelection import org.jetbrains.kotlin.idea.quickfix.generateMember +import org.jetbrains.kotlin.idea.testIntegration.findSuitableFrameworks import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.psi.JetClassOrObject @@ -69,13 +67,6 @@ abstract class KotlinGenerateTestSupportActionBase( return elementAtCaret.parentsWithSelf.filterIsInstance().firstOrNull { !it.isLocal() } } - private fun findSuitableFrameworks(klass: JetClassOrObject): List { - val lightClass = klass.toLightClass() ?: return emptyList() - val frameworks = Extensions.getExtensions(TestFramework.EXTENSION_NAME).filter { it.language == JavaLanguage.INSTANCE } - return frameworks.firstOrNull { it.isTestClass(lightClass) }?.let { listOf(it) } - ?: frameworks.filterTo(SmartList()) { it.isPotentialTestClass(lightClass) } - } - private fun chooseAndPerform(editor: Editor, frameworks: List, consumer: (TestFramework) -> Unit) { frameworks.ifEmpty { return } frameworks.singleOrNull()?.let { return consumer(it) } diff --git a/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinCreateTestDialog.kt b/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinCreateTestDialog.kt new file mode 100644 index 00000000000..9c5ffed9bf9 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinCreateTestDialog.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2010-2015 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.kotlin.idea.testIntegration + +import com.intellij.openapi.module.Module +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiPackage +import com.intellij.testIntegration.createTest.CreateTestDialog + +class KotlinCreateTestDialog(project: Project, + title: String, + targetClass: PsiClass?, + targetPackage: PsiPackage?, + targetModule: Module) : CreateTestDialog(project, title, targetClass, targetPackage, targetModule) { + public var explicitClassName: String? = null + + override fun getClassName() = explicitClassName ?: super.getClassName() +} diff --git a/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinCreateTestIntention.kt b/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinCreateTestIntention.kt new file mode 100644 index 00000000000..c31f4f1c6d3 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinCreateTestIntention.kt @@ -0,0 +1,169 @@ +/* + * Copyright 2010-2015 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.kotlin.idea.testIntegration + +import com.intellij.CommonBundle +import com.intellij.codeInsight.CodeInsightBundle +import com.intellij.codeInsight.FileModificationService +import com.intellij.codeInsight.navigation.NavigationUtil +import com.intellij.ide.util.PropertiesComponent +import com.intellij.lang.java.JavaLanguage +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.Messages +import com.intellij.openapi.util.TextRange +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.* +import com.intellij.psi.search.GlobalSearchScopesCore +import com.intellij.testIntegration.createTest.CreateTestAction +import com.intellij.testIntegration.createTest.TestGenerators +import org.jetbrains.kotlin.asJava.KotlinLightClass +import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.idea.actions.JavaToKotlinAction +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.idea.core.getPackage +import org.jetbrains.kotlin.idea.core.refactoring.j2k +import org.jetbrains.kotlin.idea.core.refactoring.toPsiDirectory +import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingRangeIntention +import org.jetbrains.kotlin.idea.util.application.executeCommand +import org.jetbrains.kotlin.idea.util.application.runWithAlternativeResolveEnabled +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.psi.JetClass +import org.jetbrains.kotlin.psi.JetClassOrObject +import org.jetbrains.kotlin.psi.JetEnumEntry +import org.jetbrains.kotlin.psi.JetNamedFunction +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.utils.addToStdlib.singletonList +import java.util.* + +class KotlinCreateTestIntention : JetSelfTargetingRangeIntention( + JetClassOrObject::class.java, + CodeInsightBundle.message("intention.create.test") +) { + override fun applicabilityRange(element: JetClassOrObject): TextRange? { + if (element.isLocal()) return null + if (element is JetEnumEntry) return null + if (element is JetClass && (element.isAnnotation() || element.isInterface())) return null + if (ModuleUtilCore.findModuleForPsiElement(element) == null) return null + if (element.resolveToDescriptorIfAny() == null) return null + + return TextRange( + element.startOffset, + element.getDelegationSpecifierList()?.startOffset ?: element.getBody()?.startOffset ?: element.endOffset + ) + } + + override fun applyTo(element: JetClassOrObject, editor: Editor) { + object : CreateTestAction() { + // Based on the com.intellij.testIntegration.createTest.JavaTestGenerator.createTestClass() + private fun findTestClass(targetDirectory: PsiDirectory, className: String): PsiClass? { + val psiPackage = targetDirectory.getPackage() ?: return null + val scope = GlobalSearchScopesCore.directoryScope(targetDirectory, false) + val klass = psiPackage.findClassByShortName(className, scope).firstOrNull() ?: return null + if (!FileModificationService.getInstance().preparePsiElementForWrite(klass)) return null + return klass + } + + private fun getTempJavaClassName(project: Project, kotlinFile: VirtualFile): String { + val baseName = kotlinFile.nameWithoutExtension + val psiDir = kotlinFile.parent!!.toPsiDirectory(project)!! + return sequence(0) { it + 1 } + .map { "$baseName$it" } + .first { psiDir.findFile("$it.java") == null && findTestClass(psiDir, it) == null } + } + + // Based on the com.intellij.testIntegration.createTest.CreateTestAction.CreateTestAction.invoke() + override fun invoke(project: Project, editor: Editor?, element: PsiElement) { + val srcModule = ModuleUtilCore.findModuleForPsiElement(element) ?: return + val propertiesComponent = PropertiesComponent.getInstance() + val testFolders = HashSet() + CreateTestAction.checkForTestRoots(srcModule, testFolders) + if (testFolders.isEmpty() && !propertiesComponent.getBoolean("create.test.in.the.same.root")) { + if (Messages.showOkCancelDialog( + project, + "Create test in the same source root?", + "No Test Roots Found", + Messages.getQuestionIcon()) != Messages.OK) return + + propertiesComponent.setValue("create.test.in.the.same.root", true) + } + + val srcClass = CreateTestAction.getContainingClass(element) ?: return + + val srcDir = element.containingFile.containingDirectory + val srcPackage = JavaDirectoryService.getInstance().getPackage(srcDir) + + val dialog = KotlinCreateTestDialog(project, text, srcClass, srcPackage, srcModule) + if (!dialog.showAndGet()) return + + val existingClass = (findTestClass(dialog.targetDirectory, dialog.className) as? KotlinLightClass)?.getOrigin() + if (existingClass != null) { + // TODO: Override dialog method when it becomes protected + val answer = Messages.showYesNoDialog( + project, + "Kotlin class '${existingClass.name}' already exists. Do you want to update it?", + CommonBundle.getErrorTitle(), + "Rewrite", + "Cancel", + Messages.getErrorIcon() + ) + if (answer == Messages.NO) return + } + + val generatedClass = project.executeCommand(CodeInsightBundle.message("intention.create.test"), this) { + val generator = TestGenerators.INSTANCE.forLanguage(dialog.selectedTestFrameworkDescriptor.language) + project.runWithAlternativeResolveEnabled { + if (existingClass != null) { + dialog.explicitClassName = getTempJavaClassName(project, existingClass.containingFile.virtualFile) + } + generator.generateTest(project, dialog) + } + } as? PsiClass ?: return + + val generatedFile = generatedClass.containingFile as? PsiJavaFile ?: return + + if (generatedClass.language == JavaLanguage.INSTANCE) { + project.executeCommand("Convert class '${generatedClass.name}' to Kotlin", this) { + runWriteAction { + generatedClass.methods.forEach { it.throwsList.referenceElements.forEach { it.delete() } } + } + + if (existingClass != null) { + runWriteAction { + val existingMethodNames = existingClass + .declarations + .filterIsInstance() + .mapTo(HashSet()) { it.name } + generatedClass + .methods + .filter { it.name !in existingMethodNames } + .forEach { it.j2k()?.let { existingClass.addDeclaration(it) } } + generatedClass.delete() + } + NavigationUtil.activateFileWithPsiElement(existingClass) + } + else { + JavaToKotlinAction.convertFiles(generatedFile.singletonList(), project, false).singleOrNull() + } + } + } + } + }.invoke(element.project, editor, element.toLightClass()!!) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/testIntegration/testIntegrationUtils.kt b/idea/src/org/jetbrains/kotlin/idea/testIntegration/testIntegrationUtils.kt new file mode 100644 index 00000000000..82fc5d46fba --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/testIntegration/testIntegrationUtils.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2015 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.kotlin.idea.testIntegration + +import com.intellij.lang.java.JavaLanguage +import com.intellij.openapi.extensions.Extensions +import com.intellij.testIntegration.TestFramework +import com.intellij.util.SmartList +import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.psi.JetClassOrObject + +public fun findSuitableFrameworks(klass: JetClassOrObject): List { + val lightClass = klass.toLightClass() ?: return emptyList() + val frameworks = Extensions.getExtensions(TestFramework.EXTENSION_NAME).filter { it.language == JavaLanguage.INSTANCE } + return frameworks.firstOrNull { it.isTestClass(lightClass) }?.let { listOf(it) } + ?: frameworks.filterTo(SmartList()) { it.isPotentialTestClass(lightClass) } +} diff --git a/idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/classDelegatorToSuperclass.kt b/idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/classDelegatorToSuperclass.kt index 9ff1dcb0b65..d9a75771fae 100644 --- a/idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/classDelegatorToSuperclass.kt +++ b/idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/classDelegatorToSuperclass.kt @@ -1,5 +1,6 @@ // "Create class 'A'" "false" // ACTION: Create interface 'A' +// ACTION: Create Test // ERROR: Unresolved reference: A package p diff --git a/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/finalJavaClass/FinalJavaSupertype.before.kt b/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/finalJavaClass/FinalJavaSupertype.before.kt index 2893104ad36..193457c5e0a 100644 --- a/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/finalJavaClass/FinalJavaSupertype.before.kt +++ b/idea/testData/quickfix/modifiers/addOpenToClassDeclaration/finalJavaClass/FinalJavaSupertype.before.kt @@ -1,5 +1,6 @@ // "class org.jetbrains.kotlin.idea.quickfix.AddOpenModifierToClassDeclarationFix" "false" // ERROR: This type is final, so it cannot be inherited from +// ACTION: Create Test import testPackage.* class foo : JavaClass() {} diff --git a/idea/testData/quickfix/typeMismatch/casts/typeMismatch2.kt b/idea/testData/quickfix/typeMismatch/casts/typeMismatch2.kt index 1e3bae42126..96bf1cdf0e9 100644 --- a/idea/testData/quickfix/typeMismatch/casts/typeMismatch2.kt +++ b/idea/testData/quickfix/typeMismatch/casts/typeMismatch2.kt @@ -1,4 +1,5 @@ // "Cast expression 'Foo()' to 'Foo'" "false" +// ACTION: Create Test // ERROR: Type mismatch.
Required:Foo<kotlin.Int>
Found:Foo<kotlin.Number>
class Foo diff --git a/idea/testData/quickfix/variables/removeValVarFromParameter/constructorParameter.kt b/idea/testData/quickfix/variables/removeValVarFromParameter/constructorParameter.kt index 9d0cb299f77..2abae48fb65 100644 --- a/idea/testData/quickfix/variables/removeValVarFromParameter/constructorParameter.kt +++ b/idea/testData/quickfix/variables/removeValVarFromParameter/constructorParameter.kt @@ -1,5 +1,6 @@ // "Remove 'val' from parameter" "false" // ACTION: Make internal // ACTION: Make private +// ACTION: Create Test class C(val x: String) { } \ No newline at end of file