Skip to content

Improve the appearance of paths in Spring Config Selection ComboBox #2096

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.*
Expand Down Expand Up @@ -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")
Expand All @@ -223,6 +221,20 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m
parametrizedTestSources to null
)

private fun shortenConfigurationNames(): Set<Pair<String?, Collection<String>>> {
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 <T : CodeGenerationSettingItem> createComboBox(values: Array<T>) : ComboBox<T> {
val comboBox = object:ComboBox<T>(DefaultComboBoxModel(values)) {
var maxWidth = 0
Expand Down Expand Up @@ -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<Settings>()
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, NameInfo>()

inner class NameInfo(val fullName: String) {
val shortenedName: String
get() = innerShortName

private val pathFragments: MutableList<String> = 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<String>): Set<String> {
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 }

}