diff --git a/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt index 7a6158665b..0f390b5dbd 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt @@ -2,7 +2,7 @@ package org.utbot.common import java.lang.reflect.InvocationTargetException import java.lang.reflect.Method - +import kotlin.reflect.KClass val Class<*>.packageName: String get() = `package`?.name?:"" @@ -11,3 +11,6 @@ fun Method.invokeCatching(obj: Any?, args: List) = try { } catch (e: InvocationTargetException) { Result.failure(e.targetException) } + +val KClass<*>.allNestedClasses: List> + get() = listOf(this) + nestedClasses.flatMap { it.allNestedClasses } 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 a2260ef573..19da58144e 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 @@ -44,6 +44,7 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.scripting.resolve.classId import org.utbot.common.HTML_LINE_SEPARATOR import org.utbot.common.PathUtil.toHtmlLinkTag +import org.utbot.common.allNestedClasses import org.utbot.common.appendHtmlLine import org.utbot.framework.codegen.Import import org.utbot.framework.codegen.ParametrizedTestSource @@ -70,6 +71,7 @@ import org.utbot.intellij.plugin.ui.WarningTestsReportNotifier import org.utbot.intellij.plugin.ui.utils.getOrCreateSarifReportsPath import org.utbot.intellij.plugin.ui.utils.showErrorDialogLater import org.utbot.intellij.plugin.util.RunConfigurationHelper +import org.utbot.intellij.plugin.util.extractClassMethodsIncludingNested import org.utbot.intellij.plugin.util.signature import org.utbot.sarif.SarifReport import java.nio.file.Path @@ -82,7 +84,11 @@ import org.utbot.intellij.plugin.util.IntelliJApiHelper.run object CodeGenerationController { - fun generateTests(model: GenerateTestsModel, testSetsByClass: Map>) { + fun generateTests( + model: GenerateTestsModel, + testSetsByClass: Map>, + psi2KClass: Map> + ) { val baseTestDirectory = model.testSourceRoot?.toPsiDirectory(model.project) ?: return val allTestPackages = getPackageDirectories(baseTestDirectory) @@ -98,9 +104,10 @@ object CodeGenerationController { val testDirectory = allTestPackages[classPackageName] ?: baseTestDirectory val testClass = createTestClass(srcClass, testDirectory, model) ?: continue val file = testClass.containingFile + val cut = psi2KClass[srcClass] ?: error("Didn't find KClass instance for class ${srcClass.name}") runWriteCommandAction(model.project, "Generate tests with UtBot", null, { try { - generateCodeAndReport(srcClass, testClass, file, testSets, model, latch, reports) + generateCodeAndReport(srcClass, cut, testClass, file, testSets, model, latch, reports) testFiles.add(file) } catch (e: IncorrectOperationException) { showCreatingClassError(model.project, createTestClassName(srcClass)) @@ -240,6 +247,7 @@ object CodeGenerationController { private fun generateCodeAndReport( srcClass: PsiClass, + classUnderTest: KClass<*>, testClass: PsiClass, file: PsiFile, testSets: List, @@ -247,8 +255,7 @@ object CodeGenerationController { reportsCountDown: CountDownLatch, reports: MutableList, ) { - val classUnderTest = testSets.first().method.clazz - val classMethods = TestIntegrationUtils.extractClassMethods(srcClass, false) + val classMethods = srcClass.extractClassMethodsIncludingNested(false) val paramNames = DumbService.getInstance(model.project) .runReadActionInSmartMode(Computable { findMethodParamNames(classUnderTest, classMethods) }) @@ -339,7 +346,7 @@ object CodeGenerationController { private fun findMethodParamNames(clazz: KClass<*>, methods: List): Map> { val bySignature = methods.associate { it.signature() to it.paramNames() } - return clazz.functions + return clazz.allNestedClasses.flatMap { it.functions } .mapNotNull { method -> bySignature[method.signature()]?.let { params -> method.executableId to params } } .toMap() } diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt index 8311f8d20a..eec8a7cbf8 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt @@ -19,15 +19,14 @@ import com.intellij.openapi.ui.Messages import com.intellij.openapi.util.Computable import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.PsiClass -import com.intellij.psi.SyntheticElement +import com.intellij.psi.PsiMethod import com.intellij.refactoring.util.classMembers.MemberInfo -import com.intellij.testIntegration.TestIntegrationUtils import com.intellij.util.concurrency.AppExecutorUtil import mu.KotlinLogging import org.jetbrains.kotlin.idea.util.module import org.utbot.analytics.EngineAnalyticsContext import org.utbot.analytics.Predictors -import org.utbot.common.filterWhen +import org.utbot.common.allNestedClasses import org.utbot.engine.util.mockListeners.ForceMockListener import org.utbot.framework.plugin.services.JdkInfoService import org.utbot.framework.UtSettings @@ -56,11 +55,11 @@ import java.util.concurrent.TimeUnit import org.utbot.engine.util.mockListeners.ForceStaticMockListener import org.utbot.framework.PathSelectorType import org.utbot.framework.plugin.services.WorkingDirService +import org.utbot.intellij.plugin.models.packageName import org.utbot.intellij.plugin.settings.Settings +import org.utbot.intellij.plugin.util.extractClassMethodsIncludingNested import org.utbot.intellij.plugin.ui.utils.isBuildWithGradle -import org.utbot.intellij.plugin.ui.utils.suitableTestSourceRoots import org.utbot.intellij.plugin.util.PluginWorkingDirProvider -import org.utbot.intellij.plugin.util.isAbstract import kotlin.reflect.KClass import kotlin.reflect.full.functions @@ -71,9 +70,10 @@ object UtTestsDialogProcessor { fun createDialogAndGenerateTests( project: Project, srcClasses: Set, + extractMembersFromSrcClasses: Boolean, focusedMethod: MemberInfo?, ) { - createDialog(project, srcClasses, focusedMethod)?.let { + createDialog(project, srcClasses, extractMembersFromSrcClasses, focusedMethod)?.let { if (it.showAndGet()) createTests(project, it.model) } } @@ -81,6 +81,7 @@ object UtTestsDialogProcessor { private fun createDialog( project: Project, srcClasses: Set, + extractMembersFromSrcClasses: Boolean, focusedMethod: MemberInfo?, ): GenerateTestsDialogWindow? { val srcModule = findSrcModule(srcClasses) @@ -105,6 +106,7 @@ object UtTestsDialogProcessor { srcModule, testModules, srcClasses, + extractMembersFromSrcClasses, if (focusedMethod != null) setOf(focusedMethod) else null, UtSettings.utBotGenerationTimeoutInMillis, ) @@ -145,6 +147,7 @@ object UtTestsDialogProcessor { val context = UtContext(classLoader) val testSetsByClass = mutableMapOf>() + val psi2KClass = mutableMapOf>() var processedClasses = 0 val totalClasses = model.srcClasses.size @@ -159,18 +162,23 @@ object UtTestsDialogProcessor { for (srcClass in model.srcClasses) { val methods = ReadAction.nonBlocking>> { - val clazz = classLoader.loadClass(srcClass.qualifiedName).kotlin - val srcMethods = - model.selectedMethods?.toList() ?: TestIntegrationUtils.extractClassMethods( - srcClass, - false - ) - .filterWhen(UtSettings.skipTestGenerationForSyntheticMethods) { - it.member !is SyntheticElement - } - .filterNot { it.isAbstract } + val canonicalName = srcClass.canonicalName + val clazz = classLoader.loadClass(canonicalName).kotlin + psi2KClass[srcClass] = clazz + + val srcMethods = if (model.extractMembersFromSrcClasses) { + val chosenMethods = model.selectedMembers?.filter { it.member is PsiMethod } ?: listOf() + val chosenNestedClasses = model.selectedMembers?.mapNotNull { it.member as? PsiClass } ?: listOf() + chosenMethods + chosenNestedClasses.flatMap { + it.extractClassMethodsIncludingNested(false) + } + } else { + srcClass.extractClassMethodsIncludingNested(false) + } DumbService.getInstance(project).runReadActionInSmartMode(Computable { - findMethodsInClassMatchingSelected(clazz, srcMethods) + clazz.allNestedClasses.flatMap { + findMethodsInClassMatchingSelected(it, srcMethods) + } }) }.executeSynchronously() @@ -264,7 +272,7 @@ object UtTestsDialogProcessor { invokeLater { withUtContext(context) { - generateTests(model, testSetsByClass) + generateTests(model, testSetsByClass, psi2KClass) } } } @@ -273,6 +281,19 @@ object UtTestsDialogProcessor { } } + private val PsiClass.canonicalName: String + get() { + return if (packageName.isEmpty()) { + qualifiedName?.replace(".", "$") ?: "" + } else { + val name = qualifiedName + ?.substringAfter("$packageName.") + ?.replace(".", "$") + ?: "" + "$packageName.$name" + } + } + /** * Configures utbot-analytics models for the better path selection. * 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 02842ad139..66c0a41df7 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 @@ -26,8 +26,9 @@ data class GenerateTestsModel( val srcModule: Module, val potentialTestModules: List, var srcClasses: Set, - var selectedMethods: Set?, - var timeout:Long, + val extractMembersFromSrcClasses: Boolean, + var selectedMembers: Set?, // TODO: maybe we should make it not nullable? + var timeout: Long, var generateWarningsForStaticMocking: Boolean = false, var fuzzingValue: Double = 0.05 ) { @@ -36,6 +37,7 @@ data class GenerateTestsModel( var testModule: Module = potentialTestModules.firstOrNull() ?: error("Empty list of test modules in model") var testSourceRoot: VirtualFile? = null + fun setSourceRootAndFindTestModule(newTestSourceRoot: VirtualFile?) { requireNotNull(newTestSourceRoot) testSourceRoot = newTestSourceRoot 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 bfc595acd9..fd2a024f88 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 @@ -39,14 +39,11 @@ import com.intellij.openapi.vfs.newvfs.impl.FakeVirtualFile import com.intellij.openapi.wm.ToolWindowManager import com.intellij.psi.PsiClass import com.intellij.psi.PsiManager -import com.intellij.psi.PsiMethod -import com.intellij.psi.SyntheticElement import com.intellij.refactoring.PackageWrapper import com.intellij.refactoring.ui.MemberSelectionTable import com.intellij.refactoring.ui.PackageNameReferenceEditorCombo import com.intellij.refactoring.util.RefactoringUtil import com.intellij.refactoring.util.classMembers.MemberInfo -import com.intellij.testIntegration.TestIntegrationUtils import com.intellij.ui.ColoredListCellRenderer import com.intellij.ui.ContextHelpLabel import com.intellij.ui.HyperlinkLabel @@ -97,7 +94,6 @@ 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.common.filterWhen import org.utbot.framework.UtSettings import org.utbot.framework.codegen.ForceStaticMocking import org.utbot.framework.codegen.Junit4 @@ -136,7 +132,7 @@ import org.utbot.intellij.plugin.ui.utils.parseVersion import org.utbot.intellij.plugin.ui.utils.testResourceRootTypes import org.utbot.intellij.plugin.ui.utils.testRootType import org.utbot.intellij.plugin.util.IntelliJApiHelper -import org.utbot.intellij.plugin.util.isAbstract +import org.utbot.intellij.plugin.util.extractFirstLevelMembers private const val RECENTS_KEY = "org.utbot.recents" @@ -384,17 +380,14 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m private fun updateMembersTable() { val srcClasses = model.srcClasses - val items: List - if (srcClasses.size == 1) { - items = TestIntegrationUtils.extractClassMethods(srcClasses.single(), false) - .filterWhen(UtSettings.skipTestGenerationForSyntheticMethods) { it.member !is SyntheticElement } - .filterNot { it.isAbstract } - updateMethodsTable(items) + val items = if (model.extractMembersFromSrcClasses) { + srcClasses.flatMap { it.extractFirstLevelMembers(false) } } else { - items = srcClasses.map { MemberInfo(it) } - updateClassesTable(items) + srcClasses.map { MemberInfo(it) } } + checkMembers(items) + membersTable.setMemberInfos(items) if (items.isEmpty()) isOKActionEnabled = false // fix issue with MemberSelectionTable height, set it directly. @@ -403,28 +396,14 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m membersTable.preferredScrollableViewportSize = size(-1, height) } - private fun updateMethodsTable(allMethods: List) { - val selectedDisplayNames = model.selectedMethods?.map { it.displayName } ?: emptyList() - val selectedMethods = if (selectedDisplayNames.isEmpty()) - allMethods - else allMethods.filter { it.displayName in selectedDisplayNames } + private fun checkMembers(allMembers: List) { + val selectedDisplayNames = model.selectedMembers?.map { it.displayName } ?: emptyList() + val selectedMembers = allMembers.filter { it.displayName in selectedDisplayNames } - if (selectedMethods.isEmpty()) { - checkMembers(allMethods) - } else { - checkMembers(selectedMethods) - } - - membersTable.setMemberInfos(allMethods) - } - - private fun updateClassesTable(srcClasses: List) { - checkMembers(srcClasses) - membersTable.setMemberInfos(srcClasses) + val methodsToCheck = selectedMembers.ifEmpty { allMembers } + methodsToCheck.forEach { it.isChecked = true } } - private fun checkMembers(members: List) = members.forEach { it.isChecked = true } - private fun getTestRoot() : VirtualFile? { model.testSourceRoot?.let { if (it.isDirectory || it is FakeVirtualFile) return it @@ -497,12 +476,12 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m if (testPackageField.text != SAME_PACKAGE_LABEL) testPackageField.text else "" val selectedMembers = membersTable.selectedMemberInfos - model.srcClasses = selectedMembers - .mapNotNull { it.member as? PsiClass ?: it.member.containingClass } - .toSet() - - val selectedMethods = selectedMembers.filter { it.member is PsiMethod }.toSet() - model.selectedMethods = if (selectedMethods.any()) selectedMethods else null + if (!model.extractMembersFromSrcClasses) { + model.srcClasses = selectedMembers + .mapNotNull { it.member as? PsiClass } + .toSet() + } + model.selectedMembers = selectedMembers.toSet() model.testFramework = testFrameworks.item model.mockStrategy = mockStrategies.item diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/actions/GenerateTestsAction.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/actions/GenerateTestsAction.kt index 65dcb81f53..ee7201bd5c 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/actions/GenerateTestsAction.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/actions/GenerateTestsAction.kt @@ -1,7 +1,6 @@ package org.utbot.intellij.plugin.ui.actions import org.utbot.intellij.plugin.generator.UtTestsDialogProcessor -import org.utbot.intellij.plugin.ui.utils.KotlinPsiElementHandler import org.utbot.intellij.plugin.ui.utils.PsiElementHandler import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent @@ -15,25 +14,24 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.* import com.intellij.psi.util.PsiTreeUtil import com.intellij.refactoring.util.classMembers.MemberInfo -import com.intellij.testIntegration.TestIntegrationUtils import org.jetbrains.kotlin.idea.core.getPackage import org.jetbrains.kotlin.idea.core.util.toPsiDirectory import org.jetbrains.kotlin.idea.core.util.toPsiFile -import org.jetbrains.kotlin.psi.KtClass +import org.utbot.intellij.plugin.util.extractFirstLevelMembers import java.util.* class GenerateTestsAction : AnAction() { override fun actionPerformed(e: AnActionEvent) { val project = e.project ?: return - val (srcClasses, focusedMethod) = getPsiTargets(e) ?: return - UtTestsDialogProcessor.createDialogAndGenerateTests(project, srcClasses, focusedMethod) + val (srcClasses, focusedMethod, extractMembersFromSrcClasses) = getPsiTargets(e) ?: return + UtTestsDialogProcessor.createDialogAndGenerateTests(project, srcClasses, extractMembersFromSrcClasses, focusedMethod) } override fun update(e: AnActionEvent) { e.presentation.isEnabled = getPsiTargets(e) != null } - private fun getPsiTargets(e: AnActionEvent): Pair, MemberInfo?>? { + private fun getPsiTargets(e: AnActionEvent): Triple, MemberInfo?, Boolean>? { val project = e.project ?: return null val editor = e.getData(CommonDataKeys.EDITOR) if (editor != null) { @@ -47,27 +45,45 @@ class GenerateTestsAction : AnAction() { val srcClass = psiElementHandler.containingClass(element) ?: return null if (srcClass.isInterface) return null val srcSourceRoot = srcClass.getSourceRoot() ?: return null - val srcMethods = TestIntegrationUtils.extractClassMethods(srcClass, false) - val focusedMethod = focusedMethodOrNull(element, srcMethods, psiElementHandler) + val srcMembers = srcClass.extractFirstLevelMembers(false) + val focusedMethod = focusedMethodOrNull(element, srcMembers, psiElementHandler) val module = ModuleUtil.findModuleForFile(srcSourceRoot, project) ?: return null val matchingRoot = ModuleRootManager.getInstance(module).contentEntries .flatMap { entry -> entry.sourceFolders.toList() } .singleOrNull { folder -> folder.file == srcSourceRoot } - if (srcMethods.isEmpty() || matchingRoot == null || matchingRoot.rootType.isForTests) { + if (srcMembers.isEmpty() || matchingRoot == null || matchingRoot.rootType.isForTests) { return null } - return Pair(setOf(srcClass), focusedMethod) + return Triple(setOf(srcClass), focusedMethod, true) } } else { // The action is being called from 'Project' tool window val srcClasses = mutableSetOf() - e.getData(CommonDataKeys.PSI_ELEMENT)?.let { - srcClasses += getAllClasses(it) - } - e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY)?.let { - srcClasses += getAllClasses(project, it) + var selectedMethod: MemberInfo? = null + var extractMembersFromSrcClasses = false + val element = e.getData(CommonDataKeys.PSI_ELEMENT) ?: return null + if (element is PsiFileSystemItem) { + e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY)?.let { + srcClasses += getAllClasses(project, it) + } + } else { + val file = element.containingFile ?: return null + val psiElementHandler = PsiElementHandler.makePsiElementHandler(file) + + if (psiElementHandler.isCreateTestActionAvailable(element)) { + psiElementHandler.containingClass(element)?.let { + srcClasses += setOf(it) + extractMembersFromSrcClasses = true + if (it.extractFirstLevelMembers(false).isEmpty()) + return null + } + + if (element is PsiMethod) { + selectedMethod = MemberInfo(element) + } + } } srcClasses.removeIf { it.isInterface } var commonSourceRoot = null as VirtualFile? @@ -84,7 +100,7 @@ class GenerateTestsAction : AnAction() { .filter { folder -> !folder.rootType.isForTests && folder.file == commonSourceRoot} .findAny().isPresent ) return null - return Pair(srcClasses, null) + return Triple(srcClasses.toSet(), selectedMethod, extractMembersFromSrcClasses) } return null } @@ -114,20 +130,12 @@ class GenerateTestsAction : AnAction() { return methods.singleOrNull { it.member == currentMethod } } - private fun getAllClasses(psiElement: PsiElement): Set { - return when (psiElement) { - is KtClass -> setOf(KotlinPsiElementHandler().toPsi(psiElement, PsiClass::class.java)) - is PsiClass -> setOf(psiElement) - is PsiDirectory -> getAllClasses(psiElement) - else -> emptySet() - } - } - private fun getAllClasses(directory: PsiDirectory): Set { val allClasses = directory.files.flatMap { getClassesFromFile(it) }.toMutableSet() for (subDir in directory.subdirectories) allClasses += getAllClasses(subDir) return allClasses } + private fun getAllClasses(project: Project, virtualFiles: Array): Set { val psiFiles = virtualFiles.mapNotNull { it.toPsiFile(project) } val psiDirectories = virtualFiles.mapNotNull { it.toPsiDirectory(project) } diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/JavaPsiElementHandler.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/JavaPsiElementHandler.kt index 0e908d6aa1..e2016f0e69 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/JavaPsiElementHandler.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/JavaPsiElementHandler.kt @@ -4,7 +4,6 @@ import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import com.intellij.psi.PsiMethod import com.intellij.psi.util.PsiTreeUtil -import com.intellij.testIntegration.TestIntegrationUtils import com.intellij.testIntegration.createTest.CreateTestAction class JavaPsiElementHandler( @@ -25,7 +24,5 @@ class JavaPsiElementHandler( CreateTestAction.isAvailableForElement(element) override fun containingClass(element: PsiElement): PsiClass? = - if (PsiTreeUtil.getParentOfType(element, PsiClass::class.java, false) != null) { - TestIntegrationUtils.findOuterClass(element) - } else null + PsiTreeUtil.getParentOfType(element, classClass, false) } \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/KotlinPsiElementHandler.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/KotlinPsiElementHandler.kt index 4365f45d76..0beca8f9a1 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/KotlinPsiElementHandler.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/utils/KotlinPsiElementHandler.kt @@ -8,10 +8,11 @@ import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtNamedFunction -import org.jetbrains.kotlin.psi.psiUtil.parents +import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.uast.toUElement class KotlinPsiElementHandler( + // TODO: KtClassOrObject? override val classClass: Class = KtClass::class.java, override val methodClass: Class = KtNamedFunction::class.java, ) : PsiElementHandler { @@ -27,9 +28,9 @@ class KotlinPsiElementHandler( getTarget(element)?.let { KotlinCreateTestIntention().applicabilityRange(it) != null } ?: false private fun getTarget(element: PsiElement?): KtNamedDeclaration? = - element?.parents + element?.parentsWithSelf ?.firstOrNull { it is KtClassOrObject || it is KtNamedDeclaration && it.parent is KtFile } as? KtNamedDeclaration override fun containingClass(element: PsiElement): PsiClass? = - (element.parents.firstOrNull { it is KtClassOrObject })?.let { toPsi(it, PsiClass::class.java) } + element.parentsWithSelf.firstOrNull { it is KtClassOrObject }?.let { toPsi(it, PsiClass::class.java) } } \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/ExtractMembersHelper.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/ExtractMembersHelper.kt new file mode 100644 index 0000000000..b19b0b0e71 --- /dev/null +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/ExtractMembersHelper.kt @@ -0,0 +1,33 @@ +package org.utbot.intellij.plugin.util + +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiModifier +import com.intellij.psi.PsiModifierListOwner +import com.intellij.psi.SyntheticElement +import com.intellij.refactoring.classMembers.MemberInfoBase +import com.intellij.refactoring.util.classMembers.MemberInfo +import com.intellij.testIntegration.TestIntegrationUtils +import org.utbot.common.filterWhen +import org.utbot.framework.UtSettings + +private val MemberInfoBase.isAbstract: Boolean + get() = this.member.modifierList?.hasModifierProperty(PsiModifier.ABSTRACT)?: false + +private fun Iterable.filterTestableMethods(): List = this + .filterWhen(UtSettings.skipTestGenerationForSyntheticMethods) { it.member !is SyntheticElement } + .filterNot { it.isAbstract } + +// TODO: maybe we need to delete [includeInherited] param here (always false when calling)? +fun PsiClass.extractClassMethodsIncludingNested(includeInherited: Boolean): List = + TestIntegrationUtils.extractClassMethods(this, includeInherited) + .filterTestableMethods() + innerClasses.flatMap { it.extractClassMethodsIncludingNested(includeInherited) } + +fun PsiClass.extractFirstLevelMembers(includeInherited: Boolean): List { + val methods = TestIntegrationUtils.extractClassMethods(this, includeInherited) + .filterTestableMethods() + val classes = if (includeInherited) + allInnerClasses + else + innerClasses + return methods + classes.map { MemberInfo(it) } +} \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/MemberInfoUtils.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/MemberInfoUtils.kt deleted file mode 100644 index 912b99f05e..0000000000 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/MemberInfoUtils.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.utbot.intellij.plugin.util - -import com.intellij.psi.PsiModifier -import com.intellij.psi.PsiModifierListOwner -import com.intellij.refactoring.classMembers.MemberInfoBase - -val MemberInfoBase.isAbstract: Boolean - get() = this.member.modifierList?.hasModifierProperty(PsiModifier.ABSTRACT)?: false \ No newline at end of file