Skip to content

Commit

Permalink
Change settings order and fix percentage display for Fuzzing/Symbolic…
Browse files Browse the repository at this point in the history
… ratio (#2198)

* Reorder controls on UI in plugin settings
* Corrected labels for Fuzzing/Symbolic percentage on UI in plugin settings
---------

Co-authored-by: Alena Lisevych <[email protected]>
Co-authored-by: Vassiliy.Kudryashov <[email protected]>
  • Loading branch information
3 people authored May 11, 2023
1 parent a9e2fe8 commit 0cceaa4
Showing 1 changed file with 123 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<Settings>()
Expand All @@ -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
Expand All @@ -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")
Expand All @@ -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 {
Expand All @@ -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 =
"<html><body>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.</body></html>"
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 =
"<html><body>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.</body></html>"
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.")
Expand Down

0 comments on commit 0cceaa4

Please sign in to comment.