diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/settings/SettingsWindow.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/settings/SettingsWindow.kt index 9353e5a07a..9a4ed73dbb 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/settings/SettingsWindow.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/settings/SettingsWindow.kt @@ -6,15 +6,13 @@ import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.ui.DialogPanel import com.intellij.ui.ContextHelpLabel import com.intellij.ui.components.JBLabel -import com.intellij.ui.dsl.builder.Align -import com.intellij.ui.dsl.builder.bindIntValue -import com.intellij.ui.dsl.builder.bindItem -import com.intellij.ui.dsl.builder.bindValue -import com.intellij.ui.dsl.builder.labelTable -import com.intellij.ui.dsl.builder.panel +import com.intellij.ui.dsl.builder.* +import com.intellij.ui.layout.selected import com.intellij.ui.layout.selectedValueMatches import com.intellij.util.ui.UIUtil import com.intellij.util.ui.components.BorderLayoutPanel +import javax.swing.* +import kotlin.reflect.KClass import org.utbot.framework.SummariesGenerationType import org.utbot.framework.UtSettings import org.utbot.framework.codegen.domain.ForceStaticMocking @@ -27,10 +25,6 @@ import org.utbot.framework.plugin.api.TreatOverflowAsError import org.utbot.framework.plugin.api.isSummarizationCompatible import org.utbot.intellij.plugin.ui.components.CodeGenerationSettingItemRenderer import org.utbot.intellij.plugin.util.showSettingsEditor -import javax.swing.DefaultComboBoxModel -import javax.swing.JCheckBox -import javax.swing.JPanel -import kotlin.reflect.KClass class SettingsWindow(val project: Project) { private val settings = project.service() @@ -43,89 +37,74 @@ class SettingsWindow(val project: Project) { private lateinit var enableSummarizationGenerationCheckBox: JCheckBox private lateinit var enableExperimentalLanguagesCheckBox: JCheckBox + private fun Row.createCombo(loader: KClass<*>, values: Array<*>) { + comboBox(DefaultComboBoxModel(values)) + .bindItem( + getter = { settings.providerNameByServiceLoader(loader) }, + setter = { settings.setProviderByLoader(loader, it as CodeGenerationSettingItem) }, + ).component.renderer = CodeGenerationSettingItemRenderer() + } + val panel: JPanel = panel { row("Generated test language:") { - codegenLanguageCombo = comboBox(DefaultComboBoxModel(CodegenLanguage.values())) + codegenLanguageCombo = comboBox(DefaultComboBoxModel(CodegenLanguage.values())).gap(RightGap.COLUMNS) .apply { - component.renderer = CodeGenerationSettingItemRenderer() - ContextHelpLabel.create("You can generate test methods in Java or Kotlin regardless of your source code language.") - }.bindItem( - getter = { settings.providerNameByServiceLoader(CodegenLanguage::class) as CodegenLanguage }, - setter = { settings.setProviderByLoader(CodegenLanguage::class, it as CodeGenerationSettingItem) } - ).component + component.renderer = CodeGenerationSettingItemRenderer() + ContextHelpLabel.create("You can generate test methods in Java or Kotlin regardless of your source code language.") + }.bindItem( + getter = { settings.providerNameByServiceLoader(CodegenLanguage::class) as CodegenLanguage }, + setter = { + settings.setProviderByLoader( + CodegenLanguage::class, + it as CodeGenerationSettingItem + ) + } + ).component codegenLanguageCombo.addActionListener { if (!codegenLanguageCombo.item.isSummarizationCompatible()) { enableSummarizationGenerationCheckBox.isSelected = false } } - } - val valuesComboBox: (KClass<*>, Array<*>) -> Unit = { loader, values -> - val serviceLabels = mapOf( - RuntimeExceptionTestsBehaviour::class to "Tests with exceptions:", - TreatOverflowAsError::class to "Overflow detection:", - JavaDocCommentStyle::class to "Javadoc comment style:" - ) - - row(serviceLabels[loader] ?: error("Unknown service loader: $loader")) { - comboBox(DefaultComboBoxModel(values)) - .bindItem( - getter = { settings.providerNameByServiceLoader(loader) }, - setter = { settings.setProviderByLoader(loader, it as CodeGenerationSettingItem) }, - ).component.renderer = CodeGenerationSettingItemRenderer() - } - } - - row("Hanging test timeout:") { - spinner( - range = IntRange( - HangingTestsTimeout.MIN_TIMEOUT_MS.toInt(), - HangingTestsTimeout.MAX_TIMEOUT_MS.toInt() - ), - step = 50 - ).bindIntValue( - getter = { - settings.hangingTestsTimeout.timeoutMs - .coerceIn(HangingTestsTimeout.MIN_TIMEOUT_MS, HangingTestsTimeout.MAX_TIMEOUT_MS).toInt() - }, - setter = { - settings.hangingTestsTimeout = HangingTestsTimeout(it.toLong()) - } - ) - label("milliseconds per method") - contextHelp( - "Set this timeout to define which test is \"hanging\". Increase it to test the " + - "time-consuming method or decrease if the execution speed is critical for you." - ) - } - - mapOf( - RuntimeExceptionTestsBehaviour::class to RuntimeExceptionTestsBehaviour.values(), - TreatOverflowAsError::class to TreatOverflowAsError.values(), - JavaDocCommentStyle::class to JavaDocCommentStyle.values() - ).forEach { (loader, values) -> - valuesComboBox(loader, values) - } - - row { - runInspectionAfterTestGenerationCheckBox = checkBox("Display detected errors on the Problems tool window") + enableExperimentalLanguagesCheckBox = checkBox("Experimental languages support") .onApply { - settings.state.runInspectionAfterTestGeneration = - runInspectionAfterTestGenerationCheckBox.isSelected + settings.state.enableExperimentalLanguagesSupport = + enableExperimentalLanguagesCheckBox.isSelected } .onReset { - runInspectionAfterTestGenerationCheckBox.isSelected = - settings.state.runInspectionAfterTestGeneration - } - .onIsModified { - runInspectionAfterTestGenerationCheckBox.isSelected xor settings.state.runInspectionAfterTestGeneration + enableExperimentalLanguagesCheckBox.isSelected = + settings.experimentalLanguagesSupport == true } + .onIsModified { enableExperimentalLanguagesCheckBox.isSelected xor settings.experimentalLanguagesSupport } .component + contextHelp("Enable JavaScript and Python if IDE supports them") + }.bottomGap(BottomGap.MEDIUM) + + row("Tests with exceptions:") { + createCombo(RuntimeExceptionTestsBehaviour::class, RuntimeExceptionTestsBehaviour.values()) + } + row("Overflow detection:") { + createCombo(TreatOverflowAsError::class, TreatOverflowAsError.values()) + } + row { + runInspectionAfterTestGenerationCheckBox = + checkBox("Display detected errors on the Problems tool window") + .onApply { + settings.state.runInspectionAfterTestGeneration = + runInspectionAfterTestGenerationCheckBox.isSelected + } + .onReset { + runInspectionAfterTestGenerationCheckBox.isSelected = + settings.state.runInspectionAfterTestGeneration + } + .onIsModified { + runInspectionAfterTestGenerationCheckBox.isSelected xor settings.state.runInspectionAfterTestGeneration + } + .component contextHelp("Automatically run code inspection after test generation") } - row { - enableSummarizationGenerationCheckBox = checkBox("Enable Summaries Generation") + enableSummarizationGenerationCheckBox = checkBox("Enable summaries generation") .onApply { settings.state.summariesGenerationType = if (enableSummarizationGenerationCheckBox.isSelected) SummariesGenerationType.FULL else SummariesGenerationType.NONE @@ -139,7 +118,11 @@ class SettingsWindow(val project: Project) { }.enabledIf(codegenLanguageCombo.selectedValueMatches(CodegenLanguage?::isSummarizationCompatible)) .component } - + indent { + row("Javadoc comment style:") { + createCombo(JavaDocCommentStyle::class, JavaDocCommentStyle.values()) + }.enabledIf(enableSummarizationGenerationCheckBox.selected).bottomGap(BottomGap.MEDIUM) + } row { forceMockCheckBox = checkBox("Force mocking static methods") @@ -152,22 +135,6 @@ class SettingsWindow(val project: Project) { .component contextHelp("Overrides other mocking settings") } - - row { - enableExperimentalLanguagesCheckBox = checkBox("Experimental languages support") - .onApply { - settings.state.enableExperimentalLanguagesSupport = - enableExperimentalLanguagesCheckBox.isSelected - } - .onReset { - enableExperimentalLanguagesCheckBox.isSelected = - settings.experimentalLanguagesSupport == true - } - .onIsModified { enableExperimentalLanguagesCheckBox.isSelected xor settings.experimentalLanguagesSupport } - .component - contextHelp("Enable JavaScript and Python if IDE supports them") - } - row("Classes to be forcedly mocked:") {} row { val updater = Runnable { @@ -183,35 +150,75 @@ class SettingsWindow(val project: Project) { .onIsModified { excludeTable.isModified() } forceMockCheckBox.addActionListener { updater.run() } - } + }.bottomGap(BottomGap.MEDIUM) + row("Hanging test timeout:") { + spinner( + range = IntRange( + HangingTestsTimeout.MIN_TIMEOUT_MS.toInt(), + HangingTestsTimeout.MAX_TIMEOUT_MS.toInt() + ), + step = 50 + ).bindIntValue( + getter = { + settings.hangingTestsTimeout.timeoutMs + .coerceIn(HangingTestsTimeout.MIN_TIMEOUT_MS, HangingTestsTimeout.MAX_TIMEOUT_MS).toInt() + }, + setter = { + settings.hangingTestsTimeout = HangingTestsTimeout(it.toLong()) + } + ) + + label("milliseconds per method") + contextHelp( + "Set this timeout to define which test is \"hanging\". Increase it to test the " + + "time-consuming method or decrease if the execution speed is critical for you." + ) + } val fuzzLabel = JBLabel("Fuzzing") val symLabel = JBLabel("Symbolic execution") - row("Test generation method:") { - val granularity = 20 - slider(0, granularity, 1, granularity / 4).apply { - // clear all labels - labelTable(emptyMap()) - }.bindValue( - getter = { ((1 - settings.fuzzingValue) * granularity).toInt() }, - setter = { settings.fuzzingValue = 1 - it / granularity.toDouble() } - ) - .align(Align.FILL) - .component.apply { - this.toolTipText = - "While fuzzer \"guesses\" the values to enter as much execution paths as possible, symbolic executor tries to \"deduce\" them. Choose the proportion of generation time allocated for each of these methods within Test generation timeout. The slide has no effect for Spring Projects." - addChangeListener { - fuzzLabel.text = "Fuzzing " + "%.0f %%".format(100.0 * (granularity - value) / granularity) - symLabel.text = "%.0f %%".format(100.0 * value / granularity) + " Symbolic execution" - } - } - }.enabled(UtSettings.useFuzzing) row { cell(BorderLayoutPanel().apply { - addToLeft(fuzzLabel) - addToRight(symLabel) + topGap(TopGap.SMALL) + addToLeft(JBLabel("Test generation method:").apply { verticalAlignment = SwingConstants.TOP }) + addToCenter(BorderLayoutPanel().apply { + val granularity = 20 + val slider = object : JSlider() { + val updater = Runnable() { + val fuzzingPercent = 100.0 * (granularity - value) / granularity + fuzzLabel.text = "Fuzzing %.0f %%".format(fuzzingPercent) + symLabel.text = "%.0f %% Symbolic execution".format(100.0 - fuzzingPercent) + } + + override fun getValue() = ((1 - settings.fuzzingValue) * granularity).toInt() + + override fun setValue(n: Int) { + val tmp = value + settings.fuzzingValue = 1 - n / granularity.toDouble() + if (tmp != n) { + updater.run() + } + } + } + UIUtil.setSliderIsFilled(slider, true) + slider.minimum = 0 + slider.maximum = granularity + slider.minorTickSpacing = 1 + slider.majorTickSpacing = granularity / 4 + slider.paintTicks = true + slider.paintTrack = true + slider.paintLabels = false + slider.toolTipText = + "While fuzzer \"guesses\" the values to enter as much execution paths as possible, symbolic executor tries to \"deduce\" them. Choose the proportion of generation time allocated for each of these methods within Test generation timeout. The slide has no effect for Spring Projects." + slider.updater.run() + addToTop(slider) + addToBottom(BorderLayoutPanel().apply { + addToLeft(fuzzLabel) + addToRight(symLabel) + }) + }) }).align(Align.FILL) - } + }.enabled(UtSettings.useFuzzing) if (!UtSettings.useFuzzing) { row { comment("Fuzzing is disabled in configuration file.")