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 6a9341bd35..0ea6620e62 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 @@ -65,7 +65,6 @@ import com.intellij.ui.layout.ComboBoxPredicate import com.intellij.ui.layout.Row import com.intellij.ui.layout.enableIf import com.intellij.ui.layout.panel -import com.intellij.ui.layout.selectedValueMatches import com.intellij.util.IncorrectOperationException import com.intellij.util.io.exists import com.intellij.util.lang.JavaVersion @@ -120,7 +119,6 @@ import org.utbot.framework.plugin.api.MockFramework import org.utbot.framework.plugin.api.MockFramework.MOCKITO import org.utbot.framework.plugin.api.MockStrategyApi import org.utbot.framework.plugin.api.TreatOverflowAsError -import org.utbot.framework.plugin.api.isSummarizationCompatible import org.utbot.framework.plugin.api.utils.MOCKITO_EXTENSIONS_FILE_CONTENT import org.utbot.framework.plugin.api.utils.MOCKITO_EXTENSIONS_FOLDER import org.utbot.framework.plugin.api.utils.MOCKITO_MOCKMAKER_FILE_NAME @@ -152,8 +150,10 @@ 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.SpringConfigurationsHelper import org.utbot.intellij.plugin.util.extractFirstLevelMembers import org.utbot.intellij.plugin.util.findSdkVersion +import java.io.File import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.util.* @@ -192,12 +192,10 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m private val codegenLanguages = createComboBox(CodegenLanguage.values()) private val testFrameworks = createComboBox(TestFramework.allItems.toTypedArray()) - private val modelSpringConfigs = setOf( - null to listOf(NO_SPRING_CONFIGURATION_OPTION), - "Java-based configurations" to model.getSortedSpringConfigurationClasses(), - "XML-based configurations" to model.getSpringXMLConfigurationFiles() - ) - private val springConfig = createComboBoxWithSeparatorsForSpringConfigs(modelSpringConfigs) + private val javaConfigurationHelper = SpringConfigurationsHelper(".") + private val xmlConfigurationHelper = SpringConfigurationsHelper(File.separator) + + private val springConfig = createComboBoxWithSeparatorsForSpringConfigs(shortenConfigurationNames()) private val mockStrategies = createComboBox(MockStrategyApi.values()) private val staticsMocking = JCheckBox("Mock static methods") @@ -223,6 +221,20 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m parametrizedTestSources to null ) + private fun shortenConfigurationNames(): Set>> { + val shortenedSortedSpringConfigurationClasses = + javaConfigurationHelper.shortenSpringConfigNames(model.getSortedSpringConfigurationClasses()) + + val shortenedSpringXMLConfigurationFiles = + xmlConfigurationHelper.shortenSpringConfigNames(model.getSpringXMLConfigurationFiles()) + + return setOf( + null to listOf(NO_SPRING_CONFIGURATION_OPTION), + "Java-based configurations" to shortenedSortedSpringConfigurationClasses, + "XML-based configurations" to shortenedSpringXMLConfigurationFiles + ) + } + private fun createComboBox(values: Array) : ComboBox { val comboBox = object:ComboBox(DefaultComboBoxModel(values)) { var maxWidth = 0 @@ -580,7 +592,17 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m model.typeReplacementApproach = when (springConfig.item.getItem()) { NO_SPRING_CONFIGURATION_OPTION -> TypeReplacementApproach.DoNotReplace - else -> TypeReplacementApproach.ReplaceIfPossible(springConfig.item.getItem().toString()) + else -> { + val shortConfigName = springConfig.item.getItem().toString() + //TODO: avoid this check on xml here, merge two helpers into one + val fullConfigName = if (shortConfigName.endsWith(".xml")) { + xmlConfigurationHelper.restoreFullName(shortConfigName) + } else { + javaConfigurationHelper.restoreFullName(shortConfigName) + } + + TypeReplacementApproach.ReplaceIfPossible(fullConfigName) + } } val settings = model.project.service() @@ -1115,8 +1137,7 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m private fun updateSpringConfigurationEnabled() { // We check for > 1 because there is already extra-dummy NO_SPRING_CONFIGURATION_OPTION option - springConfig.isEnabled = model.projectType == ProjectType.Spring - && modelSpringConfigs.size > 1 + springConfig.isEnabled = model.projectType == ProjectType.Spring && springConfig.itemCount > 1 } private fun staticsMockingConfigured(): Boolean { diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/SpringConfigurationsHelper.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/SpringConfigurationsHelper.kt new file mode 100644 index 0000000000..01fb77dc4b --- /dev/null +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/SpringConfigurationsHelper.kt @@ -0,0 +1,92 @@ +package org.utbot.intellij.plugin.util + +/** + * This class is a converter between full Spring configuration names and shortened versions. + * + * Shortened versions are represented on UI. + * Full names are used in further analysis in utbot-spring-analyzer. + * + * The idea of this implementation is to append parent directories to the file name until all names become unique. + * + * Example: + * - [["config.web.WebConfig", "config.web2.WebConfig", "config.web.AnotherConfig"]] + * -> + * [["web.WebConfig", "web2.WebConfig", "AnotherConfig"]] + */ +class SpringConfigurationsHelper(val separator: String) { + + private val nameToInfo = mutableMapOf() + + inner class NameInfo(val fullName: String) { + val shortenedName: String + get() = innerShortName + + private val pathFragments: MutableList = fullName.split(separator).toMutableList() + private var innerShortName = pathFragments.removeLast() + + fun enlargeShortName(): Boolean { + if (pathFragments.isEmpty()) { + return false + } + + val lastElement = pathFragments.removeLast() + innerShortName = "${lastElement}$separator$innerShortName" + return true + } + } + + fun restoreFullName(shortenedName: String): String = + nameToInfo + .values + .singleOrNull { it.shortenedName == shortenedName } + ?.fullName + ?: error("Full name of configuration file cannot be restored by shortened name $shortenedName") + + fun shortenSpringConfigNames(fullNames: Set): Set { + fullNames.forEach { nameToInfo[it] = NameInfo(it) } + var nameInfoCollection = nameToInfo.values + + // this cycle continues until all shortenedNames become unique + while (nameInfoCollection.size != nameInfoCollection.distinctBy { it.shortenedName }.size) { + nameInfoCollection = nameInfoCollection.sortedBy { it.shortenedName }.toMutableList() + + var index = 0 + while(index < nameInfoCollection.size){ + val curShortenedPath = nameInfoCollection[index].shortenedName + + // here we search a block of shortened paths that are equivalent + // and must be enlarged with new fragment so on. + var maxIndexWithSamePath = index + while (maxIndexWithSamePath < nameInfoCollection.size) { + if (nameInfoCollection[maxIndexWithSamePath].shortenedName == curShortenedPath) { + maxIndexWithSamePath++ + } + else { + break + } + } + + // if the size of this block is one, we should not enlarge it + if (index == maxIndexWithSamePath - 1) { + index++ + continue + } + + // otherwise, enlarge the block of shortened names with one new fragment + for (i in index until maxIndexWithSamePath) { + if (!nameInfoCollection[i].enlargeShortName()) { + return collectShortenedNames() + } + } + + // after enlarging the block, we proceed to search for the next block + index = maxIndexWithSamePath + } + } + + return collectShortenedNames() + } + + private fun collectShortenedNames() = nameToInfo.values.mapTo(mutableSetOf()) { it.shortenedName } + +} \ No newline at end of file