diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index 51d8f06cd0..879dfc1037 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -1053,13 +1053,13 @@ enum class MockStrategyApi( override val displayName: String, override val description: String ) : CodeGenerationSettingItem { - NO_MOCKS("No mocks", "Do not use mock frameworks at all"), + NO_MOCKS("Do not mock", "Do not use mock frameworks at all"), OTHER_PACKAGES( - "Other packages: $MOCKITO", + "Mock package environment", "Mock all classes outside the current package except system ones" ), OTHER_CLASSES( - "Other classes: $MOCKITO", + "Mock class environment", "Mock all classes outside the class under test except system ones" ); diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/Domain.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/Domain.kt index 2e8fe3d200..2bad966103 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/Domain.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/Domain.kt @@ -593,11 +593,11 @@ enum class RuntimeExceptionTestsBehaviour( override val description: String ) : CodeGenerationSettingItem { PASS( - displayName = "Passing", + displayName = "Pass", description = "Tests that produce Runtime exceptions should pass (by inserting throwable assertion)" ), FAIL( - displayName = "Failing", + displayName = "Fail", description = "Tests that produce Runtime exceptions should fail" + "(WARNING!: failing tests may appear in testing class)" ); 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 7f801b59e6..fd0e7ab992 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 @@ -11,37 +11,36 @@ import com.intellij.ui.layout.labelTable import com.intellij.ui.layout.panel import com.intellij.ui.layout.slider import com.intellij.ui.layout.withValueBinding +import com.intellij.util.ui.UIUtil +import javax.swing.DefaultComboBoxModel +import javax.swing.JCheckBox +import javax.swing.JLabel +import javax.swing.JPanel +import kotlin.reflect.KClass import org.utbot.framework.UtSettings import org.utbot.framework.codegen.ForceStaticMocking import org.utbot.framework.codegen.HangingTestsTimeout import org.utbot.framework.codegen.RuntimeExceptionTestsBehaviour import org.utbot.framework.plugin.api.CodeGenerationSettingItem import org.utbot.framework.plugin.api.CodegenLanguage -import org.utbot.framework.plugin.api.MockStrategyApi import org.utbot.framework.plugin.api.TreatOverflowAsError -import javax.swing.DefaultComboBoxModel -import javax.swing.JLabel -import javax.swing.JPanel -import kotlin.reflect.KClass -@Suppress("UNCHECKED_CAST") class SettingsWindow(val project: Project) { private val settings = project.service() + // TODO it is better to use something like SearchEverywhere for classes but it is complicated to implement private val excludeTable = MockAlwaysClassesTable(project) + private lateinit var forceMockCheckBox: JCheckBox val panel: JPanel = panel { val valuesComboBox: LayoutBuilder.(KClass<*>, Array<*>) -> Unit = { loader, values -> val serviceLabels = mapOf( - MockStrategyApi::class to "Mock strategy:", - CodegenLanguage::class to "Language generation:", + CodegenLanguage::class to "Generated test language:", RuntimeExceptionTestsBehaviour::class to "Test with exceptions:", - ForceStaticMocking::class to "Force static mocking:", TreatOverflowAsError::class to "Overflow detection:", ) - - val serviceComments = mapOf( - RuntimeExceptionTestsBehaviour::class to "Test behavior when runtime exception occurs", + val tooltipLabels = mapOf( + CodegenLanguage::class to "You can generate test methods in Java or Kotlin regardless of your source code language." ) row(serviceLabels[loader] ?: error("Unknown service loader: $loader")) { @@ -50,24 +49,12 @@ class SettingsWindow(val project: Project) { DefaultComboBoxModel(values), getter = { settings.providerNameByServiceLoader(loader) }, setter = { settings.setProviderByLoader(loader, it as CodeGenerationSettingItem) }, - ).apply { - val comment = serviceComments[loader] - if (comment != null) { - ContextHelpLabel.create(comment)() - } - } + ).apply { ContextHelpLabel.create(tooltipLabels[loader] ?: return@apply)() } } } } - mapOf( - MockStrategyApi::class to MockStrategyApi.values(), - CodegenLanguage::class to CodegenLanguage.values(), - RuntimeExceptionTestsBehaviour::class to RuntimeExceptionTestsBehaviour.values(), - TreatOverflowAsError::class to TreatOverflowAsError.values() - ).forEach { (loader, values) -> - valuesComboBox(loader, values) - } + valuesComboBox(CodegenLanguage::class, CodegenLanguage.values()) row("Hanging test timeout:") { cell { @@ -76,26 +63,62 @@ class SettingsWindow(val project: Project) { settings.hangingTestsTimeout.timeoutMs .coerceIn(HangingTestsTimeout.MIN_TIMEOUT_MS, HangingTestsTimeout.MAX_TIMEOUT_MS).toInt() }, - setter = { settings.hangingTestsTimeout = HangingTestsTimeout(it.toLong()) }, + setter = { + settings.hangingTestsTimeout = HangingTestsTimeout(it.toLong()) + }, minValue = HangingTestsTimeout.MIN_TIMEOUT_MS.toInt(), maxValue = HangingTestsTimeout.MAX_TIMEOUT_MS.toInt(), step = 50, ) - comment("milliseconds") + + label("milliseconds") + .apply { + ContextHelpLabel.create( + "Test generation may hang due to infinite loops or other code conditions. " + + "Set timeout to stop waiting for hanging process." + )() + } } } - mapOf( - ForceStaticMocking::class to ForceStaticMocking.values(), + RuntimeExceptionTestsBehaviour::class to RuntimeExceptionTestsBehaviour.values(), + TreatOverflowAsError::class to TreatOverflowAsError.values() ).forEach { (loader, values) -> valuesComboBox(loader, values) } + + row { - excludeTable.component(CCFlags.grow) + cell { + forceMockCheckBox = checkBox("Force mocking static methods") + .onApply { + settings.state.forceStaticMocking = + if (forceMockCheckBox.isSelected) ForceStaticMocking.FORCE else ForceStaticMocking.DO_NOT_FORCE + } + .onReset { forceMockCheckBox.isSelected = settings.forceStaticMocking == ForceStaticMocking.FORCE } + .onIsModified { forceMockCheckBox.isSelected xor (settings.forceStaticMocking != ForceStaticMocking.DO_NOT_FORCE) } + .apply { ContextHelpLabel.create("Overrides other mocking settings")() } + .component + } + } + + row("Classes to be forcedly mocked:") {} + row { + val excludeTableCellBuilder = excludeTable.component(CCFlags.grow) + val updater = Runnable { + UIUtil.setEnabled(excludeTableCellBuilder.component, forceMockCheckBox.isSelected, true) + } + excludeTableCellBuilder .onApply { excludeTable.apply() } - .onReset { excludeTable.reset() } + .onReset { + excludeTable.reset() + updater.run() + } .onIsModified { excludeTable.isModified() } + forceMockCheckBox.addActionListener { updater.run() } + + } row("Code analysis:") { @@ -128,4 +151,4 @@ class SettingsWindow(val project: Project) { excludeTable.reset() (panel as DialogPanel).reset() } -} \ No newline at end of file +} 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 d4417bff1a..9d813d6765 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 @@ -85,6 +85,7 @@ import javax.swing.AbstractAction import javax.swing.Action import javax.swing.DefaultComboBoxModel import javax.swing.JButton +import javax.swing.JCheckBox import javax.swing.JComboBox import javax.swing.JComponent import javax.swing.JList @@ -98,6 +99,7 @@ import org.utbot.framework.UtSettings import org.utbot.framework.codegen.ForceStaticMocking import org.utbot.framework.codegen.Junit4 import org.utbot.framework.codegen.Junit5 +import org.utbot.framework.codegen.MockitoStaticMocking import org.utbot.framework.codegen.NoStaticMocking import org.utbot.framework.codegen.ParametrizedTestSource import org.utbot.framework.codegen.StaticsMocking @@ -169,7 +171,7 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m private val codegenLanguages = ComboBox(DefaultComboBoxModel(CodegenLanguage.values())) private val testFrameworks = ComboBox(DefaultComboBoxModel(TestFramework.allItems.toTypedArray())) private val mockStrategies = ComboBox(DefaultComboBoxModel(MockStrategyApi.values())) - private val staticsMocking = ComboBox(DefaultComboBoxModel(StaticsMocking.allItems.toTypedArray())) + private val staticsMocking = JCheckBox("Mock static methods") private val timeoutSpinner = JBIntSpinner( TimeUnit.MILLISECONDS.toSeconds(UtSettings.utBotGenerationTimeoutInMillis).toInt(), @@ -177,24 +179,24 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m Int.MAX_VALUE, MINIMUM_TIMEOUT_VALUE_IN_SECONDS ) - private val parametrizedTestSources = ComboBox(DefaultComboBoxModel(ParametrizedTestSource.values())) + private val parametrizedTestSources = JCheckBox("Parametrized tests") - private lateinit var mockExtensionRow: Row private lateinit var panel: DialogPanel @Suppress("UNCHECKED_CAST") private val itemsToHelpTooltip = hashMapOf( (codegenLanguages as ComboBox) to createHelpLabel(), (testFrameworks as ComboBox) to createHelpLabel(), - (mockStrategies as ComboBox) to createHelpLabel(), - (staticsMocking as ComboBox) to createHelpLabel(), - (parametrizedTestSources as ComboBox) to createHelpLabel() + staticsMocking to null, + parametrizedTestSources to null ) - private fun createHelpLabel() = JBLabel(AllIcons.General.ContextHelp) + private fun createHelpLabel(commonTooltip: String? = null) = JBLabel(AllIcons.General.ContextHelp).apply { + if (!commonTooltip.isNullOrEmpty()) toolTipText = commonTooltip + } init { - title = "Generate tests with UtBot" + title = "Generate Tests with UnitTestBot" setResizable(false) TestFramework.allItems.forEach { @@ -242,37 +244,26 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m codegenLanguages as ComboBox, itemsToHelpTooltip[codegenLanguages] ) - } - row("Test framework:") { + }.visible = false + row("Testing framework:") { makePanelWithHelpTooltip( testFrameworks as ComboBox, - itemsToHelpTooltip[testFrameworks] + null ) } + row { component(parametrizedTestSources) } row("Mock strategy:") { makePanelWithHelpTooltip( mockStrategies as ComboBox, - itemsToHelpTooltip[mockStrategies] - ) - } - mockExtensionRow = row("Mock static:") { - makePanelWithHelpTooltip( - staticsMocking as ComboBox, - itemsToHelpTooltip[staticsMocking] + ContextHelpLabel.create("Mock everything around the target class or the whole package except the system classes. Otherwise mock nothing.") ) } - row("Timeout for class:") { - panelWithHelpTooltip("The execution timeout specifies time for symbolic and concrete analysis") { + row { component(staticsMocking)} + row("Test generation timeout:") { + cell{ component(timeoutSpinner) - component(JBLabel("sec")) + label("seconds per class") } - - } - row("Parametrized test:") { - makePanelWithHelpTooltip( - parametrizedTestSources as ComboBox, - itemsToHelpTooltip[parametrizedTestSources] - ) } row { component(cbSpecifyTestPackage) @@ -281,7 +272,7 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m component(testPackageField) }.apply { visible = false } - row("Generate test methods for:") {} + row("Generate tests for:") {} row { scrollPane(membersTable) } @@ -489,10 +480,11 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m model.testFramework = testFrameworks.item model.mockStrategy = mockStrategies.item - model.parametrizedTestSource = parametrizedTestSources.item + model.parametrizedTestSource = + if (parametrizedTestSources.isSelected) ParametrizedTestSource.PARAMETRIZE else ParametrizedTestSource.DO_NOT_PARAMETRIZE model.mockFramework = MOCKITO - model.staticsMocking = staticsMocking.item + model.staticsMocking = if (staticsMocking.isSelected) MockitoStaticMocking else NoStaticMocking model.codegenLanguage = codegenLanguages.item try { timeoutSpinner.commitEdit() @@ -635,8 +627,8 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m val settings = model.project.service() mockStrategies.item = settings.mockStrategy - staticsMocking.item = settings.staticsMocking - parametrizedTestSources.item = settings.parametrizedTestSource + staticsMocking.isSelected = settings.staticsMocking == MockitoStaticMocking + parametrizedTestSources.isSelected = settings.parametrizedTestSource == ParametrizedTestSource.PARAMETRIZE val areMocksSupported = settings.parametrizedTestSource == ParametrizedTestSource.DO_NOT_PARAMETRIZE mockStrategies.isEnabled = areMocksSupported @@ -646,9 +638,9 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m if (model.srcClasses.all { it is KtUltraLightClass }) CodegenLanguage.KOTLIN else CodegenLanguage.JAVA val installedTestFramework = TestFramework.allItems.singleOrNull { it.isInstalled } - currentFrameworkItem = when (parametrizedTestSources.item) { - ParametrizedTestSource.DO_NOT_PARAMETRIZE -> installedTestFramework ?: settings.testFramework - ParametrizedTestSource.PARAMETRIZE -> installedTestFramework + currentFrameworkItem = when (parametrizedTestSources.isSelected) { + false -> installedTestFramework ?: settings.testFramework + true -> installedTestFramework ?: if (settings.testFramework != Junit4) settings.testFramework else TestFramework.parametrizedDefaultItem } @@ -656,9 +648,15 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m updateParametrizationEnabled(currentFrameworkItem) updateMockStrategyList() - updateStaticMockingStrategyList() - itemsToHelpTooltip.forEach { (box, tooltip) -> tooltip.toolTipText = box.item.description } + itemsToHelpTooltip.forEach { (box, tooltip) -> + if (tooltip != null && box is ComboBox<*>) { + val item = box.item + if (item is CodeGenerationSettingItem) { + tooltip.toolTipText = item.description + } + } + } } /** @@ -690,13 +688,13 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m } private fun configureStaticMockingIfRequired() { - if (staticsMocking.item != NoStaticMocking && !staticsMocking.item.isConfigured) { + if (staticsMocking.isSelected && !MockitoStaticMocking.isConfigured) { configureStaticMocking() } } private fun configureParametrizedTestsIfRequired() { - if (parametrizedTestSources.item != ParametrizedTestSource.DO_NOT_PARAMETRIZE && !testFrameworks.item.isParametrizedTestsConfigured) { + if (parametrizedTestSources.isSelected && !testFrameworks.item.isParametrizedTestsConfigured) { configureParametrizedTests() } } @@ -735,8 +733,7 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m val testResourcesUrl = model.testModule.getOrCreateTestResourcesPath(model.testSourceRoot) configureMockitoResources(testResourcesUrl) - val staticsMockingValue = staticsMocking.item - staticsMockingValue.isConfigured = true + MockitoStaticMocking.isConfigured = true } /** @@ -827,11 +824,8 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m * Note that now we need it for Kotlin plugin only. */ private fun configureJvmTargetIfRequired() { - val codegenLanguage = codegenLanguages.item - val parametrization = parametrizedTestSources.item - - if (codegenLanguage == CodegenLanguage.KOTLIN - && parametrization == ParametrizedTestSource.PARAMETRIZE + if (codegenLanguages.item == CodegenLanguage.KOTLIN + && parametrizedTestSources.isSelected && createKotlinJvmTargetNotificationDialog() == Messages.YES ) { configureKotlinJvmTarget() @@ -873,7 +867,9 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m private val actualKotlinJvmTarget = "1.8" private fun setListeners() { - itemsToHelpTooltip.forEach { (box, tooltip) -> box.setHelpTooltipTextChanger(tooltip) } + itemsToHelpTooltip.forEach { (box, tooltip) -> if (box is ComboBox<*> && tooltip != null) { + box.setHelpTooltipTextChanger(tooltip) + } } testSourceFolderField.childComponent.addActionListener { event -> with((event.source as JComboBox<*>).selectedItem) { @@ -892,7 +888,7 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m staticsMocking.isEnabled = item != MockStrategyApi.NO_MOCKS if (!staticsMocking.isEnabled) { - staticsMocking.item = NoStaticMocking + staticsMocking.isSelected = false } } @@ -916,7 +912,7 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m mockStrategies.item = MockStrategyApi.NO_MOCKS } if (!staticsMocking.isEnabled) { - staticsMocking.item = NoStaticMocking + staticsMocking.isSelected = false } updateTestFrameworksList(parametrizedTestSource) @@ -993,20 +989,6 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m } } - private fun updateStaticMockingStrategyList() { - staticsMocking.renderer = object : ColoredListCellRenderer() { - override fun customizeCellRenderer( - list: JList, value: StaticsMocking?, - index: Int, selected: Boolean, hasFocus: Boolean - ) { - this.append(value.toString(), SimpleTextAttributes.REGULAR_ATTRIBUTES) - if (value != NoStaticMocking && value?.isConfigured != true) { - this.append(WILL_BE_CONFIGURED_LABEL, SimpleTextAttributes.ERROR_ATTRIBUTES) - } - } - } - } - private fun staticsMockingConfigured(): Boolean { val entries = ModuleRootManager.getInstance(model.testModule).contentEntries val hasEntriesWithoutResources = entries @@ -1038,10 +1020,12 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m fun GenerateTestsModel.getActionText() : String = if (this.runGeneratedTestsWithCoverage) ACTION_GENERATE_AND_RUN else ACTION_GENERATE -private fun ComboBox.setHelpTooltipTextChanger(helpLabel: JBLabel) { +private fun ComboBox<*>.setHelpTooltipTextChanger(helpLabel: JBLabel) { addActionListener { event -> val comboBox = event.source as ComboBox<*> - val item = comboBox.item as CodeGenerationSettingItem - helpLabel.toolTipText = item.description + val item = comboBox.item + if (item is CodeGenerationSettingItem) { + helpLabel.toolTipText = item.description + } } } \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/actions/GenerateTestsAction.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/actions/GenerateTestsAction.kt index 06a1c999fe..34e7540c5f 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/actions/GenerateTestsAction.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/actions/GenerateTestsAction.kt @@ -1,5 +1,6 @@ package org.utbot.intellij.plugin.ui.actions +import com.intellij.openapi.actionSystem.ActionPlaces import org.utbot.intellij.plugin.generator.UtTestsDialogProcessor import org.utbot.intellij.plugin.ui.utils.PsiElementHandler import com.intellij.openapi.actionSystem.AnAction @@ -33,6 +34,9 @@ class GenerateTestsAction : AnAction(), UpdateInBackground { } override fun update(e: AnActionEvent) { + if (e.place == ActionPlaces.POPUP) { + e.presentation.text = "Tests with UnitTestBot..." + } e.presentation.isEnabled = getPsiTargets(e) != null } diff --git a/utbot-intellij/src/main/resources/META-INF/plugin.xml b/utbot-intellij/src/main/resources/META-INF/plugin.xml index 63afe73d1e..a5972aa021 100644 --- a/utbot-intellij/src/main/resources/META-INF/plugin.xml +++ b/utbot-intellij/src/main/resources/META-INF/plugin.xml @@ -17,7 +17,7 @@ @@ -29,7 +29,7 @@ + displayName="UnitTestBot"/>