From e68a3c9b5478bd2cecdbc594aa4a23e0a7768a95 Mon Sep 17 00:00:00 2001 From: Ivan Volkov Date: Tue, 4 Oct 2022 14:35:56 +0300 Subject: [PATCH 1/2] Select Kotlin test src root by default when generating for Kotlin --- .../plugin/models/GenerateTestsModel.kt | 4 +++- .../plugin/ui/GenerateTestsDialogWindow.kt | 4 +--- .../TestFolderComboWithBrowseButton.kt | 14 +++++++++++-- .../intellij/plugin/ui/utils/ModuleUtils.kt | 20 ++++++++++++++++++- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/models/GenerateTestsModel.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/models/GenerateTestsModel.kt index cf1a330978..e11adde947 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/models/GenerateTestsModel.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/models/GenerateTestsModel.kt @@ -19,6 +19,7 @@ import com.intellij.openapi.vfs.newvfs.impl.FakeVirtualFile import com.intellij.psi.PsiClass import com.intellij.psi.PsiJavaFile import com.intellij.refactoring.util.classMembers.MemberInfo +import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass import org.jetbrains.kotlin.psi.KtFile import org.utbot.framework.plugin.api.JavaDocCommentStyle import org.utbot.framework.util.ConflictTriggers @@ -56,13 +57,14 @@ data class GenerateTestsModel( ?: error("Could not find module for $newTestSourceRoot") } + var codegenLanguage = if (srcClasses.all { it is KtUltraLightClass }) CodegenLanguage.KOTLIN else CodegenLanguage.JAVA + var testPackageName: String? = null lateinit var testFramework: TestFramework lateinit var mockStrategy: MockStrategyApi lateinit var mockFramework: MockFramework lateinit var staticsMocking: StaticsMocking lateinit var parametrizedTestSource: ParametrizedTestSource - lateinit var codegenLanguage: CodegenLanguage lateinit var runtimeExceptionTestsBehaviour: RuntimeExceptionTestsBehaviour lateinit var hangingTestsTimeout: HangingTestsTimeout lateinit var forceStaticMocking: ForceStaticMocking diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/GenerateTestsDialogWindow.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/GenerateTestsDialogWindow.kt index f394221bf6..779261d3c2 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/GenerateTestsDialogWindow.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/GenerateTestsDialogWindow.kt @@ -95,7 +95,6 @@ import javax.swing.JSpinner import javax.swing.text.DefaultFormatter import org.jetbrains.concurrency.Promise import org.jetbrains.concurrency.thenRun -import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass import org.utbot.common.PathUtil.toPath import org.utbot.framework.UtSettings import org.utbot.framework.codegen.ForceStaticMocking @@ -651,8 +650,7 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m mockStrategies.isEnabled = areMocksSupported staticsMocking.isEnabled = areMocksSupported && mockStrategies.item != MockStrategyApi.NO_MOCKS - codegenLanguages.item = - if (model.srcClasses.all { it is KtUltraLightClass }) CodegenLanguage.KOTLIN else CodegenLanguage.JAVA + codegenLanguages.item = model.codegenLanguage val installedTestFramework = TestFramework.allItems.singleOrNull { it.isInstalled } currentFrameworkItem = when (parametrizedTestSources.isSelected) { diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/components/TestFolderComboWithBrowseButton.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/components/TestFolderComboWithBrowseButton.kt index 5fe088b3bd..1dc0509c4e 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/components/TestFolderComboWithBrowseButton.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/components/TestFolderComboWithBrowseButton.kt @@ -18,6 +18,7 @@ import javax.swing.DefaultComboBoxModel import javax.swing.JList import org.jetbrains.kotlin.idea.util.projectStructure.allModules import org.utbot.common.PathUtil +import org.utbot.framework.plugin.api.CodegenLanguage import org.utbot.intellij.plugin.models.GenerateTestsModel import org.utbot.intellij.plugin.ui.utils.addDedicatedTestRoot import org.utbot.intellij.plugin.ui.utils.isBuildWithGradle @@ -58,7 +59,17 @@ class TestFolderComboWithBrowseButton(private val model: GenerateTestsModel) : val suggestedModules = if (model.project.isBuildWithGradle) model.project.allModules() else model.potentialTestModules - val testRoots = suggestedModules.flatMap { it.suitableTestSourceRoots().toList() }.toMutableList() + val testRootsByLanguage = CodegenLanguage.allItems.associateWith { language -> + suggestedModules.flatMap { module -> + module.suitableTestSourceRoots(language) + } + } + + // testRoots for default codegen language should go before other testRoots (this impacts default-selected test root item) + val testRoots = with (testRootsByLanguage.entries) { + filter { it.key == model.codegenLanguage } + filter { it.key != model.codegenLanguage } + }.flatMap { it.value }.toMutableList() + // this method is blocked for Gradle, where multiple test modules can exist model.testModule.addDedicatedTestRoot(testRoots) @@ -95,7 +106,6 @@ class TestFolderComboWithBrowseButton(private val model: GenerateTestsModel) : } private fun configureRootsCombo(testRoots: List) { - // unfortunately, Gradle creates Kotlin test source root with Java source root type, so type is misleading val selectedRoot = testRoots.first() // do not update model.testModule here, because fake test source root could have been chosen diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/ModuleUtils.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/ModuleUtils.kt index 20a226adcb..aedcc1dd19 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/ModuleUtils.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/ModuleUtils.kt @@ -145,7 +145,8 @@ private fun Module.suitableTestSourceFolders(codegenLanguage: CodegenLanguage): return sourceFolders .filterNot { it.isForGeneratedSources() } - .filter { it.rootType == codegenLanguage.testRootType() } + .filter { it.isTestSource } + .filter { it.expectedLanguageForTests == codegenLanguage } // Heuristics: User is more likely to choose the shorter path .sortedBy { it.url.length } } @@ -275,3 +276,20 @@ private fun jdkVersionBy(sdk: Sdk?): JavaSdkVersion { } return jdkVersion } + +private val SourceFolder.expectedLanguageForTests: CodegenLanguage? + get() { + // unfortunately, Gradle creates Kotlin test source root with Java source root type, so type is misleading, + // and we should try looking for name first + if (file?.name == "kotlin") + return CodegenLanguage.KOTLIN + + if (file?.name == "java") + return CodegenLanguage.JAVA + + return when (rootType) { + CodegenLanguage.KOTLIN.testRootType() -> CodegenLanguage.KOTLIN + CodegenLanguage.JAVA.testRootType() -> CodegenLanguage.JAVA + else -> null + } + } \ No newline at end of file From 7c6ab37f1443e609b00b58cdc020ab315905037f Mon Sep 17 00:00:00 2001 From: Ivan Volkov Date: Mon, 10 Oct 2022 11:40:02 +0300 Subject: [PATCH 2/2] Move testsourceroot-choosing heuristics from ModuleUtils to TestFolderCombo --- .../generator/CodeGenerationController.kt | 4 +- .../TestFolderComboWithBrowseButton.kt | 29 ++++++------- .../intellij/plugin/ui/utils/ModuleUtils.kt | 41 +++++++++++-------- 3 files changed, 41 insertions(+), 33 deletions(-) diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt index 2be935f2e8..d22c335d51 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt @@ -416,8 +416,8 @@ object CodeGenerationController { // all test roots for the given test module val testRoots = runReadAction { testModule - .suitableTestSourceRoots(this) - .mapNotNull { psiManager.findDirectory(it) } + .suitableTestSourceRoots() + .mapNotNull { psiManager.findDirectory(it.dir) } } // return an util class from one of the test source roots or null if no util class was found diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/components/TestFolderComboWithBrowseButton.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/components/TestFolderComboWithBrowseButton.kt index 1dc0509c4e..c223176153 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/components/TestFolderComboWithBrowseButton.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/components/TestFolderComboWithBrowseButton.kt @@ -18,8 +18,8 @@ import javax.swing.DefaultComboBoxModel import javax.swing.JList import org.jetbrains.kotlin.idea.util.projectStructure.allModules import org.utbot.common.PathUtil -import org.utbot.framework.plugin.api.CodegenLanguage import org.utbot.intellij.plugin.models.GenerateTestsModel +import org.utbot.intellij.plugin.ui.utils.TestSourceRoot import org.utbot.intellij.plugin.ui.utils.addDedicatedTestRoot import org.utbot.intellij.plugin.ui.utils.isBuildWithGradle import org.utbot.intellij.plugin.ui.utils.suitableTestSourceRoots @@ -59,19 +59,20 @@ class TestFolderComboWithBrowseButton(private val model: GenerateTestsModel) : val suggestedModules = if (model.project.isBuildWithGradle) model.project.allModules() else model.potentialTestModules - val testRootsByLanguage = CodegenLanguage.allItems.associateWith { language -> - suggestedModules.flatMap { module -> - module.suitableTestSourceRoots(language) + val testRoots = suggestedModules.flatMap { + it.suitableTestSourceRoots() + }.sortedWith( + compareByDescending { + // Heuristics: Dirs with language == codegenLanguage should go first + it.expectedLanguage == model.codegenLanguage + }.thenBy { + // Heuristics: User is more likely to choose the shorter path + it.dir.path.length } - } - - // testRoots for default codegen language should go before other testRoots (this impacts default-selected test root item) - val testRoots = with (testRootsByLanguage.entries) { - filter { it.key == model.codegenLanguage } + filter { it.key != model.codegenLanguage } - }.flatMap { it.value }.toMutableList() + ).toMutableList() // this method is blocked for Gradle, where multiple test modules can exist - model.testModule.addDedicatedTestRoot(testRoots) + model.testModule.addDedicatedTestRoot(testRoots, model.codegenLanguage) if (testRoots.isNotEmpty()) { configureRootsCombo(testRoots) @@ -105,12 +106,12 @@ class TestFolderComboWithBrowseButton(private val model: GenerateTestsModel) : files.singleOrNull() } - private fun configureRootsCombo(testRoots: List) { + private fun configureRootsCombo(testRoots: List) { val selectedRoot = testRoots.first() // do not update model.testModule here, because fake test source root could have been chosen - model.testSourceRoot = selectedRoot - newItemList(testRoots.toSet()) + model.testSourceRoot = selectedRoot.dir + newItemList(testRoots.map { it.dir }.toSet()) } private fun newItemList(comboItems: Set) { diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/ModuleUtils.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/ModuleUtils.kt index aedcc1dd19..eb371dc4e8 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/ModuleUtils.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/ModuleUtils.kt @@ -37,6 +37,11 @@ import org.jetbrains.kotlin.platform.TargetPlatformVersion private val logger = KotlinLogging.logger {} +data class TestSourceRoot( + val dir: VirtualFile, + val expectedLanguage: CodegenLanguage +) + /** * @return jdk version of the module */ @@ -60,12 +65,6 @@ fun Module.kotlinTargetPlatform(): TargetPlatformVersion { ?.singleOrNull() ?: error("Can't determine target platform for module $this") } -fun Module.suitableTestSourceRoots(): List = - suitableTestSourceRoots(CodegenLanguage.JAVA) + suitableTestSourceRoots(CodegenLanguage.KOTLIN) - -fun Module.suitableTestSourceFolders(): List = - suitableTestSourceFolders(CodegenLanguage.JAVA) + suitableTestSourceFolders(CodegenLanguage.KOTLIN) - /** * Gets a path to test resources source root. * @@ -121,8 +120,8 @@ private fun findPotentialModulesForTests(project: Project, srcModule: Module): L /** * Finds all suitable test root virtual files. */ -fun Module.suitableTestSourceRoots(codegenLanguage: CodegenLanguage): List { - val sourceRootsInModule = suitableTestSourceFolders(codegenLanguage).mapNotNull { it.file } +fun Module.suitableTestSourceRoots(): List { + val sourceRootsInModule = suitableTestSourceFolders().mapNotNull { it.testSourceRoot } if (sourceRootsInModule.isNotEmpty()) { return sourceRootsInModule @@ -133,11 +132,20 @@ fun Module.suitableTestSourceRoots(codegenLanguage: CodegenLanguage): List { +private val SourceFolder.testSourceRoot:TestSourceRoot? + get() { + val file = file + val expectedLanguage = expectedLanguageForTests + if (file != null && expectedLanguage != null) + return TestSourceRoot(file, expectedLanguage) + return null + } + +private fun Module.suitableTestSourceFolders(): List { val sourceFolders = ModuleRootManager.getInstance(this) .contentEntries .flatMap { it.sourceFolders.toList() } @@ -146,10 +154,8 @@ private fun Module.suitableTestSourceFolders(codegenLanguage: CodegenLanguage): return sourceFolders .filterNot { it.isForGeneratedSources() } .filter { it.isTestSource } - .filter { it.expectedLanguageForTests == codegenLanguage } - // Heuristics: User is more likely to choose the shorter path - .sortedBy { it.url.length } } + private val GRADLE_SYSTEM_ID = ProjectSystemId("GRADLE") val Project.isBuildWithGradle get() = @@ -158,11 +164,12 @@ val Project.isBuildWithGradle get() = } private const val dedicatedTestSourceRootName = "utbot_tests" -fun Module.addDedicatedTestRoot(testSourceRoots: MutableList): VirtualFile? { + +fun Module.addDedicatedTestRoot(testSourceRoots: MutableList, language: CodegenLanguage): VirtualFile? { // Don't suggest new test source roots for Gradle project where 'unexpected' test roots won't work if (project.isBuildWithGradle) return null // Dedicated test root already exists - if (testSourceRoots.any { file -> file.name == dedicatedTestSourceRootName }) return null + if (testSourceRoots.any { root -> root.dir.name == dedicatedTestSourceRootName }) return null val moduleInstance = ModuleRootManager.getInstance(this) val testFolder = moduleInstance.contentEntries.flatMap { it.sourceFolders.toList() } @@ -170,7 +177,7 @@ fun Module.addDedicatedTestRoot(testSourceRoots: MutableList): Virt (testFolder?.let { testFolder.file?.parent } ?: testFolder?.contentEntry?.file ?: this.guessModuleDir())?.let { val file = FakeVirtualFile(it, dedicatedTestSourceRootName) - testSourceRoots.add(file) + testSourceRoots.add(TestSourceRoot(file, language)) // We return "true" IFF it's case of not yet created fake directory return if (VfsUtil.findRelativeFile(it, dedicatedTestSourceRootName) == null) file else null }