Skip to content
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

Change settings order and fix percentage display for Fuzzing/Symbolic ratio #2198

Merged
merged 8 commits into from
May 11, 2023
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