From b728899c410cbd4449ea468a2b3540427da47233 Mon Sep 17 00:00:00 2001 From: Ekaterina Tochilina Date: Thu, 2 Mar 2023 12:15:11 +0300 Subject: [PATCH 1/2] Renamed OK button; 'test sources root' --- .../language/python/PythonDialogWindow.kt | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogWindow.kt b/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogWindow.kt index 7989c2aa14..89a40fce0f 100644 --- a/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogWindow.kt +++ b/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogWindow.kt @@ -1,10 +1,8 @@ package org.utbot.intellij.plugin.language.python +import com.intellij.openapi.components.service import com.intellij.openapi.project.Project -import com.intellij.openapi.ui.ComboBox -import com.intellij.openapi.ui.DialogPanel -import com.intellij.openapi.ui.DialogWrapper -import com.intellij.openapi.ui.ValidationInfo +import com.intellij.openapi.ui.* import com.intellij.ui.ContextHelpLabel import com.intellij.ui.JBIntSpinner import com.intellij.ui.components.Panel @@ -21,11 +19,13 @@ import java.awt.BorderLayout import java.util.concurrent.TimeUnit import org.utbot.intellij.plugin.ui.components.TestSourceDirectoryChooser import org.utbot.intellij.plugin.ui.utils.createTestFrameworksRenderer +import java.awt.event.ActionEvent import javax.swing.* private const val WILL_BE_INSTALLED_LABEL = " (will be installed)" private const val MINIMUM_TIMEOUT_VALUE_IN_SECONDS = 1 +private const val ACTION_GENERATE = "Generate Tests" class PythonDialogWindow(val model: PythonTestsModel) : DialogWrapper(model.project) { @@ -64,7 +64,7 @@ class PythonDialogWindow(val model: PythonTestsModel) : DialogWrapper(model.proj override fun createCenterPanel(): JComponent { panel = panel { - row("Test source root:") { + row("Test sources root:") { component(testSourceFolderField) } row("Test framework:") { @@ -165,6 +165,19 @@ class PythonDialogWindow(val model: PythonTestsModel) : DialogWrapper(model.proj contextHelpLabel?.let { add(it, BorderLayout.LINE_END) } }) + class OKOptionAction(private val okAction: Action) : AbstractAction(ACTION_GENERATE) { + init { + putValue(DEFAULT_ACTION, java.lang.Boolean.TRUE) + putValue(FOCUSED_ACTION, java.lang.Boolean.TRUE) + } + override fun actionPerformed(e: ActionEvent?) { + okAction.actionPerformed(e) + } + } + + private val okOptionAction: OKOptionAction get() = OKOptionAction(super.getOKAction()) + override fun getOKAction() = okOptionAction + override fun doOKAction() { val selectedMembers = functionsTable.selectedMemberInfos model.selectedFunctions = selectedMembers.mapNotNull { it.member as? PyFunction }.toSet() From 0252ca9993f5fd31b74b317f3026de7a8e4cf4a2 Mon Sep 17 00:00:00 2001 From: Ekaterina Tochilina Date: Thu, 2 Mar 2023 17:03:11 +0300 Subject: [PATCH 2/2] Moved plugin settings to utbot-ui-commons --- .../language/python/PythonDialogWindow.kt | 21 +- .../intellij/plugin/settings/Settings.kt | 308 ++---------------- .../plugin/ui/GenerateTestsDialogWindow.kt | 3 +- .../plugin/settings/CommonSettings.kt | 272 ++++++++++++++++ .../intellij/plugin/util/UtSettingsHelper.kt | 0 5 files changed, 302 insertions(+), 302 deletions(-) create mode 100644 utbot-ui-commons/src/main/kotlin/org/utbot/intellij/plugin/settings/CommonSettings.kt rename {utbot-intellij => utbot-ui-commons}/src/main/kotlin/org/utbot/intellij/plugin/util/UtSettingsHelper.kt (100%) diff --git a/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogWindow.kt b/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogWindow.kt index 89a40fce0f..375bc3fa86 100644 --- a/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogWindow.kt +++ b/utbot-intellij-python/src/main/kotlin/org/utbot/intellij/plugin/language/python/PythonDialogWindow.kt @@ -15,6 +15,7 @@ import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo import com.jetbrains.python.refactoring.classes.ui.PyMemberSelectionTable import org.utbot.framework.UtSettings +import org.utbot.intellij.plugin.settings.Settings import java.awt.BorderLayout import java.util.concurrent.TimeUnit import org.utbot.intellij.plugin.ui.components.TestSourceDirectoryChooser @@ -38,13 +39,6 @@ class PythonDialogWindow(val model: PythonTestsModel) : DialogWrapper(model.proj Int.MAX_VALUE, MINIMUM_TIMEOUT_VALUE_IN_SECONDS ) - private val timeoutSpinnerForOneRun = - JBIntSpinner( - TimeUnit.MILLISECONDS.toSeconds(DEFAULT_TIMEOUT_FOR_RUN_IN_MILLIS).toInt(), - MINIMUM_TIMEOUT_VALUE_IN_SECONDS, - Int.MAX_VALUE, - MINIMUM_TIMEOUT_VALUE_IN_SECONDS - ) private val testFrameworks = ComboBox(DefaultComboBoxModel(model.cgLanguageAssistant.getLanguageTestFrameworkManager().testFrameworks.toTypedArray())) @@ -80,13 +74,6 @@ class PythonDialogWindow(val model: PythonTestsModel) : DialogWrapper(model.proj component(ContextHelpLabel.create("Set the timeout for all test generation processes.")) } } - row("Timeout for one function run:") { - cell { - component(timeoutSpinnerForOneRun) - label("seconds") - component(ContextHelpLabel.create("Set the timeout for one function execution.")) - } - } row("Generate test methods for:") {} row { scrollPane(functionsTable) @@ -183,9 +170,13 @@ class PythonDialogWindow(val model: PythonTestsModel) : DialogWrapper(model.proj model.selectedFunctions = selectedMembers.mapNotNull { it.member as? PyFunction }.toSet() model.testFramework = testFrameworks.item model.timeout = TimeUnit.SECONDS.toMillis(timeoutSpinnerForTotalTimeout.number.toLong()) - model.timeoutForRun = TimeUnit.SECONDS.toMillis(timeoutSpinnerForOneRun.number.toLong()) model.testSourceRootPath = testSourceFolderField.text + val settings = model.project.service() + with(settings) { + model.timeoutForRun = hangingTestsTimeout.timeoutMs + } + super.doOKAction() } diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/settings/Settings.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/settings/Settings.kt index f1e028ed5e..58b2e28428 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/settings/Settings.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/settings/Settings.kt @@ -2,294 +2,30 @@ package org.utbot.intellij.plugin.settings -import com.intellij.openapi.components.PersistentStateComponent -import com.intellij.openapi.components.State -import com.intellij.openapi.components.Storage -import com.intellij.openapi.project.Project -import com.intellij.util.xmlb.Converter -import com.intellij.util.xmlb.annotations.OptionTag -import java.io.IOException -import java.nio.file.Files -import java.nio.file.Paths -import org.utbot.common.FileUtil -import org.utbot.engine.Mocker -import org.utbot.framework.UtSettings -import org.utbot.framework.codegen.domain.ForceStaticMocking -import org.utbot.framework.codegen.domain.HangingTestsTimeout -import org.utbot.framework.codegen.domain.Junit4 -import org.utbot.framework.codegen.domain.Junit5 -import org.utbot.framework.codegen.domain.MockitoStaticMocking -import org.utbot.framework.codegen.domain.NoStaticMocking -import org.utbot.framework.codegen.domain.ParametrizedTestSource -import org.utbot.framework.codegen.domain.RuntimeExceptionTestsBehaviour -import org.utbot.framework.codegen.domain.StaticsMocking -import org.utbot.framework.codegen.domain.TestFramework -import org.utbot.framework.codegen.domain.TestNg -import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.CodeGenerationSettingItem -import org.utbot.framework.plugin.api.CodegenLanguage -import org.utbot.framework.plugin.api.JavaDocCommentStyle import org.utbot.framework.plugin.api.MockFramework -import org.utbot.framework.plugin.api.MockStrategyApi -import org.utbot.framework.plugin.api.TreatOverflowAsError import org.utbot.intellij.plugin.models.GenerateTestsModel -import java.util.concurrent.CompletableFuture -import kotlin.reflect.KClass -import org.utbot.common.isWindows -import org.utbot.framework.SummariesGenerationType -import org.utbot.framework.plugin.api.isSummarizationCompatible -@State( - name = "UtBotSettings", - storages = [Storage("utbot-settings.xml")] -) -class Settings(val project: Project) : PersistentStateComponent { - data class State( - var sourceRootHistory: MutableList = mutableListOf(), - var codegenLanguage: CodegenLanguage = CodegenLanguage.defaultItem, - @OptionTag(converter = TestFrameworkConverter::class) - var testFramework: TestFramework = TestFramework.defaultItem, - var mockStrategy: MockStrategyApi = MockStrategyApi.defaultItem, - var mockFramework: MockFramework = MockFramework.defaultItem, - @OptionTag(converter = StaticsMockingConverter::class) - var staticsMocking: StaticsMocking = StaticsMocking.defaultItem, - var runtimeExceptionTestsBehaviour: RuntimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.defaultItem, - @OptionTag(converter = HangingTestsTimeoutConverter::class) - var hangingTestsTimeout: HangingTestsTimeout = HangingTestsTimeout(), - var runInspectionAfterTestGeneration: Boolean = true, - var forceStaticMocking: ForceStaticMocking = ForceStaticMocking.defaultItem, - var treatOverflowAsError: TreatOverflowAsError = TreatOverflowAsError.defaultItem, - var parametrizedTestSource: ParametrizedTestSource = ParametrizedTestSource.defaultItem, - var classesToMockAlways: Array = Mocker.defaultSuperClassesToMockAlwaysNames.toTypedArray(), - var fuzzingValue: Double = 0.05, - var runGeneratedTestsWithCoverage: Boolean = false, - var commentStyle: JavaDocCommentStyle = JavaDocCommentStyle.defaultItem, - var summariesGenerationType: SummariesGenerationType = UtSettings.summaryGenerationType, - var enableExperimentalLanguagesSupport: Boolean = false, - ) { - constructor(model: GenerateTestsModel) : this( - sourceRootHistory = model.sourceRootHistory, - codegenLanguage = model.codegenLanguage, - testFramework = model.testFramework, - mockStrategy = model.mockStrategy, - mockFramework = model.mockFramework ?: MockFramework.defaultItem, - staticsMocking = model.staticsMocking, - runtimeExceptionTestsBehaviour = model.runtimeExceptionTestsBehaviour, - hangingTestsTimeout = model.hangingTestsTimeout, - runInspectionAfterTestGeneration = model.runInspectionAfterTestGeneration, - forceStaticMocking = model.forceStaticMocking, - parametrizedTestSource = model.parametrizedTestSource, - classesToMockAlways = model.chosenClassesToMockAlways.mapTo(mutableSetOf()) { it.name }.toTypedArray(), - fuzzingValue = model.fuzzingValue, - runGeneratedTestsWithCoverage = model.runGeneratedTestsWithCoverage, - commentStyle = model.commentStyle, - summariesGenerationType = model.summariesGenerationType - ) - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as State - - if (sourceRootHistory != other.sourceRootHistory) return false - if (codegenLanguage != other.codegenLanguage) return false - if (testFramework != other.testFramework) return false - if (mockStrategy != other.mockStrategy) return false - if (mockFramework != other.mockFramework) return false - if (staticsMocking != other.staticsMocking) return false - if (runtimeExceptionTestsBehaviour != other.runtimeExceptionTestsBehaviour) return false - if (hangingTestsTimeout != other.hangingTestsTimeout) return false - if (runInspectionAfterTestGeneration != other.runInspectionAfterTestGeneration) return false - if (forceStaticMocking != other.forceStaticMocking) return false - if (treatOverflowAsError != other.treatOverflowAsError) return false - if (parametrizedTestSource != other.parametrizedTestSource) return false - if (!classesToMockAlways.contentEquals(other.classesToMockAlways)) return false - if (fuzzingValue != other.fuzzingValue) return false - if (runGeneratedTestsWithCoverage != other.runGeneratedTestsWithCoverage) return false - if (commentStyle != other.commentStyle) return false - if (summariesGenerationType != other.summariesGenerationType) return false - - return true - } - override fun hashCode(): Int { - var result = sourceRootHistory.hashCode() - result = 31 * result + codegenLanguage.hashCode() - result = 31 * result + testFramework.hashCode() - result = 31 * result + mockStrategy.hashCode() - result = 31 * result + mockFramework.hashCode() - result = 31 * result + staticsMocking.hashCode() - result = 31 * result + runtimeExceptionTestsBehaviour.hashCode() - result = 31 * result + hangingTestsTimeout.hashCode() - result = 31 * result + runInspectionAfterTestGeneration.hashCode() - result = 31 * result + forceStaticMocking.hashCode() - result = 31 * result + treatOverflowAsError.hashCode() - result = 31 * result + parametrizedTestSource.hashCode() - result = 31 * result + classesToMockAlways.contentHashCode() - result = 31 * result + fuzzingValue.hashCode() - result = 31 * result + if (runGeneratedTestsWithCoverage) 1 else 0 - result = 31 * result + summariesGenerationType.hashCode() - - return result - } - } - - private var state = State() - val sourceRootHistory: MutableList get() = state.sourceRootHistory - - val codegenLanguage: CodegenLanguage get() = state.codegenLanguage - - val testFramework: TestFramework get() = state.testFramework - - val mockStrategy: MockStrategyApi get() = state.mockStrategy - - val runtimeExceptionTestsBehaviour: RuntimeExceptionTestsBehaviour get() = state.runtimeExceptionTestsBehaviour - - var hangingTestsTimeout: HangingTestsTimeout - get() = state.hangingTestsTimeout - set(value) { - state.hangingTestsTimeout = value - } - - val staticsMocking: StaticsMocking get() = state.staticsMocking - - val runInspectionAfterTestGeneration: Boolean get() = state.runInspectionAfterTestGeneration - - val forceStaticMocking: ForceStaticMocking get() = state.forceStaticMocking - - val experimentalLanguagesSupport: Boolean get () = state.enableExperimentalLanguagesSupport - - val treatOverflowAsError: TreatOverflowAsError get() = state.treatOverflowAsError - - val parametrizedTestSource: ParametrizedTestSource get() = state.parametrizedTestSource - - val classesToMockAlways: Set get() = state.classesToMockAlways.toSet() - - val javaDocCommentStyle: JavaDocCommentStyle get() = state.commentStyle - - var fuzzingValue: Double - get() = state.fuzzingValue - set(value) { - state.fuzzingValue = value.coerceIn(0.0, 1.0) - } - var runGeneratedTestsWithCoverage = state.runGeneratedTestsWithCoverage - - var enableSummariesGeneration = state.summariesGenerationType - - fun setClassesToMockAlways(classesToMockAlways: List) { - state.classesToMockAlways = classesToMockAlways.distinct().toTypedArray() - } - - override fun getState(): State = state - - override fun initializeComponent() { - super.initializeComponent() - CompletableFuture.runAsync { - FileUtil.clearTempDirectory(UtSettings.daysLimitForTempFiles) - - // Don't replace file with custom user's settings - if (UtSettings.areCustomized()) return@runAsync - // In case settings.properties file is not yet presented - // (or stays with all default template values) in {homeDir}/.utbot folder - // we copy (or re-write) it from plugin resource file - val settingsClass = javaClass - Paths.get(UtSettings.defaultSettingsPath()).toFile().apply { - try { - this.parentFile.apply { - if (this.mkdirs() && isWindows) Files.setAttribute(this.toPath(), "dos:hidden", true) - } - settingsClass.getResource("../../../../../settings.properties")?.let { - this.writeBytes(it.openStream().readBytes()) - } - } catch (ignored: IOException) { - } - } - } - } - - override fun loadState(state: State) { - this.state = state - if (!state.codegenLanguage.isSummarizationCompatible()) { - this.state.summariesGenerationType = SummariesGenerationType.NONE - } - } - - fun loadStateFromModel(model: GenerateTestsModel) { - loadState(State(model)) - } - - // these classes are all ref types so we can use only names here - fun chosenClassesToMockAlways(): Set = state.classesToMockAlways.mapTo(mutableSetOf()) { ClassId(it) } - - fun setProviderByLoader(loader: KClass<*>, provider: CodeGenerationSettingItem) = - when (loader) { - // TODO: service loaders for test generator and code generator are removed from settings temporarily -// TestGeneratorServiceLoader::class -> setGeneratorName(provider) -// CodeGeneratorServiceLoader::class -> setCodeGeneratorName(provider) - MockStrategyApi::class -> state.mockStrategy = provider as MockStrategyApi - CodegenLanguage::class -> state.codegenLanguage = provider as CodegenLanguage - RuntimeExceptionTestsBehaviour::class -> { - state.runtimeExceptionTestsBehaviour = provider as RuntimeExceptionTestsBehaviour - } - ForceStaticMocking::class -> state.forceStaticMocking = provider as ForceStaticMocking - TreatOverflowAsError::class -> { - // TODO: SAT-1566 - state.treatOverflowAsError = provider as TreatOverflowAsError - UtSettings.treatOverflowAsError = provider == TreatOverflowAsError.AS_ERROR - } - JavaDocCommentStyle::class -> state.commentStyle = provider as JavaDocCommentStyle - // TODO: add error processing - else -> error("Unknown class [$loader] to map value [$provider]") - } - - fun providerNameByServiceLoader(loader: KClass<*>): CodeGenerationSettingItem = - when (loader) { - // TODO: service loaders for test generator and code generator are removed from settings temporarily -// TestGeneratorServiceLoader::class -> generatorName -// CodeGeneratorServiceLoader::class -> codeGeneratorName - MockStrategyApi::class -> mockStrategy - CodegenLanguage::class -> codegenLanguage - RuntimeExceptionTestsBehaviour::class -> runtimeExceptionTestsBehaviour - ForceStaticMocking::class -> forceStaticMocking - TreatOverflowAsError::class -> treatOverflowAsError - JavaDocCommentStyle::class -> javaDocCommentStyle - // TODO: add error processing - else -> error("Unknown service loader: $loader") - } +fun loadStateFromModel(settings: Settings, model: GenerateTestsModel) { + settings.loadState(fromGenerateTestsModel(model)) } -// use it to serialize testFramework in State -private class TestFrameworkConverter : Converter() { - override fun toString(value: TestFramework): String = "$value" - - override fun fromString(value: String): TestFramework = when (value) { - Junit4.id -> Junit4 - Junit5.id -> Junit5 - TestNg.id -> TestNg - else -> error("Unknown TestFramework $value") - } -} - -// use it to serialize staticsMocking in State -private class StaticsMockingConverter : Converter() { - override fun toString(value: StaticsMocking): String = "$value" - - override fun fromString(value: String): StaticsMocking = when (value) { - NoStaticMocking.id -> NoStaticMocking - MockitoStaticMocking.id -> MockitoStaticMocking - else -> error("Unknown StaticsMocking $value") - } -} - -// TODO is it better to use kotlinx.serialization? -// use it to serialize hangingTestsTimeout in State -private class HangingTestsTimeoutConverter : Converter() { - override fun toString(value: HangingTestsTimeout): String = - "HangingTestsTimeout:${value.timeoutMs}" - - override fun fromString(value: String): HangingTestsTimeout { - val arguments = value.substringAfter("HangingTestsTimeout:") - val timeoutMs = arguments.toLong() - return HangingTestsTimeout(timeoutMs) - } -} +private fun fromGenerateTestsModel(model: GenerateTestsModel): Settings.State { + return Settings.State( + sourceRootHistory = model.sourceRootHistory, + codegenLanguage = model.codegenLanguage, + testFramework = model.testFramework, + mockStrategy = model.mockStrategy, + mockFramework = model.mockFramework ?: MockFramework.defaultItem, + staticsMocking = model.staticsMocking, + runtimeExceptionTestsBehaviour = model.runtimeExceptionTestsBehaviour, + hangingTestsTimeout = model.hangingTestsTimeout, + runInspectionAfterTestGeneration = model.runInspectionAfterTestGeneration, + forceStaticMocking = model.forceStaticMocking, + parametrizedTestSource = model.parametrizedTestSource, + classesToMockAlways = model.chosenClassesToMockAlways.mapTo(mutableSetOf()) { it.name }.toTypedArray(), + fuzzingValue = model.fuzzingValue, + runGeneratedTestsWithCoverage = model.runGeneratedTestsWithCoverage, + commentStyle = model.commentStyle, + summariesGenerationType = model.summariesGenerationType + ) +} \ 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 649c674df0..0e67c14516 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 @@ -126,6 +126,7 @@ import org.utbot.intellij.plugin.models.packageName import org.utbot.intellij.plugin.models.testNgNewLibraryDescriptor import org.utbot.intellij.plugin.models.testNgOldLibraryDescriptor import org.utbot.intellij.plugin.settings.Settings +import org.utbot.intellij.plugin.settings.loadStateFromModel import org.utbot.intellij.plugin.ui.components.CodeGenerationSettingItemRenderer import org.utbot.intellij.plugin.ui.components.TestFolderComboWithBrowseButton import org.utbot.intellij.plugin.ui.utils.LibrarySearchScope @@ -535,7 +536,7 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m } // firstly save settings - settings.loadStateFromModel(model) + loadStateFromModel(settings, model) // then process force static mocking case model.generateWarningsForStaticMocking = model.staticsMocking is NoStaticMocking if (model.forceStaticMocking == ForceStaticMocking.FORCE) { diff --git a/utbot-ui-commons/src/main/kotlin/org/utbot/intellij/plugin/settings/CommonSettings.kt b/utbot-ui-commons/src/main/kotlin/org/utbot/intellij/plugin/settings/CommonSettings.kt new file mode 100644 index 0000000000..4d369c94fc --- /dev/null +++ b/utbot-ui-commons/src/main/kotlin/org/utbot/intellij/plugin/settings/CommonSettings.kt @@ -0,0 +1,272 @@ +@file:Suppress("MemberVisibilityCanBePrivate") + +package org.utbot.intellij.plugin.settings + +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage +import com.intellij.openapi.project.Project +import com.intellij.util.xmlb.Converter +import com.intellij.util.xmlb.annotations.OptionTag +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Paths +import org.utbot.common.FileUtil +import org.utbot.engine.Mocker +import org.utbot.framework.UtSettings +import org.utbot.framework.codegen.domain.ForceStaticMocking +import org.utbot.framework.codegen.domain.HangingTestsTimeout +import org.utbot.framework.codegen.domain.Junit4 +import org.utbot.framework.codegen.domain.Junit5 +import org.utbot.framework.codegen.domain.MockitoStaticMocking +import org.utbot.framework.codegen.domain.NoStaticMocking +import org.utbot.framework.codegen.domain.ParametrizedTestSource +import org.utbot.framework.codegen.domain.RuntimeExceptionTestsBehaviour +import org.utbot.framework.codegen.domain.StaticsMocking +import org.utbot.framework.codegen.domain.TestFramework +import org.utbot.framework.codegen.domain.TestNg +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.CodeGenerationSettingItem +import org.utbot.framework.plugin.api.CodegenLanguage +import org.utbot.framework.plugin.api.JavaDocCommentStyle +import org.utbot.framework.plugin.api.MockFramework +import org.utbot.framework.plugin.api.MockStrategyApi +import org.utbot.framework.plugin.api.TreatOverflowAsError +import java.util.concurrent.CompletableFuture +import kotlin.reflect.KClass +import org.utbot.common.isWindows +import org.utbot.framework.SummariesGenerationType +import org.utbot.framework.plugin.api.isSummarizationCompatible + +@State( + name = "UtBotSettings", + storages = [Storage("utbot-settings.xml")] +) +class Settings(val project: Project) : PersistentStateComponent { + data class State( + var sourceRootHistory: MutableList = mutableListOf(), + var codegenLanguage: CodegenLanguage = CodegenLanguage.defaultItem, + @OptionTag(converter = TestFrameworkConverter::class) + var testFramework: TestFramework = TestFramework.defaultItem, + var mockStrategy: MockStrategyApi = MockStrategyApi.defaultItem, + var mockFramework: MockFramework = MockFramework.defaultItem, + @OptionTag(converter = StaticsMockingConverter::class) + var staticsMocking: StaticsMocking = StaticsMocking.defaultItem, + var runtimeExceptionTestsBehaviour: RuntimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.defaultItem, + @OptionTag(converter = HangingTestsTimeoutConverter::class) + var hangingTestsTimeout: HangingTestsTimeout = HangingTestsTimeout(), + var runInspectionAfterTestGeneration: Boolean = true, + var forceStaticMocking: ForceStaticMocking = ForceStaticMocking.defaultItem, + var treatOverflowAsError: TreatOverflowAsError = TreatOverflowAsError.defaultItem, + var parametrizedTestSource: ParametrizedTestSource = ParametrizedTestSource.defaultItem, + var classesToMockAlways: Array = Mocker.defaultSuperClassesToMockAlwaysNames.toTypedArray(), + var fuzzingValue: Double = 0.05, + var runGeneratedTestsWithCoverage: Boolean = false, + var commentStyle: JavaDocCommentStyle = JavaDocCommentStyle.defaultItem, + var summariesGenerationType: SummariesGenerationType = UtSettings.summaryGenerationType, + var enableExperimentalLanguagesSupport: Boolean = false, + ) { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as State + + if (sourceRootHistory != other.sourceRootHistory) return false + if (codegenLanguage != other.codegenLanguage) return false + if (testFramework != other.testFramework) return false + if (mockStrategy != other.mockStrategy) return false + if (mockFramework != other.mockFramework) return false + if (staticsMocking != other.staticsMocking) return false + if (runtimeExceptionTestsBehaviour != other.runtimeExceptionTestsBehaviour) return false + if (hangingTestsTimeout != other.hangingTestsTimeout) return false + if (runInspectionAfterTestGeneration != other.runInspectionAfterTestGeneration) return false + if (forceStaticMocking != other.forceStaticMocking) return false + if (treatOverflowAsError != other.treatOverflowAsError) return false + if (parametrizedTestSource != other.parametrizedTestSource) return false + if (!classesToMockAlways.contentEquals(other.classesToMockAlways)) return false + if (fuzzingValue != other.fuzzingValue) return false + if (runGeneratedTestsWithCoverage != other.runGeneratedTestsWithCoverage) return false + if (commentStyle != other.commentStyle) return false + if (summariesGenerationType != other.summariesGenerationType) return false + + return true + } + override fun hashCode(): Int { + var result = sourceRootHistory.hashCode() + result = 31 * result + codegenLanguage.hashCode() + result = 31 * result + testFramework.hashCode() + result = 31 * result + mockStrategy.hashCode() + result = 31 * result + mockFramework.hashCode() + result = 31 * result + staticsMocking.hashCode() + result = 31 * result + runtimeExceptionTestsBehaviour.hashCode() + result = 31 * result + hangingTestsTimeout.hashCode() + result = 31 * result + runInspectionAfterTestGeneration.hashCode() + result = 31 * result + forceStaticMocking.hashCode() + result = 31 * result + treatOverflowAsError.hashCode() + result = 31 * result + parametrizedTestSource.hashCode() + result = 31 * result + classesToMockAlways.contentHashCode() + result = 31 * result + fuzzingValue.hashCode() + result = 31 * result + if (runGeneratedTestsWithCoverage) 1 else 0 + result = 31 * result + summariesGenerationType.hashCode() + + return result + } + } + + private var state = State() + val sourceRootHistory: MutableList get() = state.sourceRootHistory + + val codegenLanguage: CodegenLanguage get() = state.codegenLanguage + + val testFramework: TestFramework get() = state.testFramework + + val mockStrategy: MockStrategyApi get() = state.mockStrategy + + val runtimeExceptionTestsBehaviour: RuntimeExceptionTestsBehaviour get() = state.runtimeExceptionTestsBehaviour + + var hangingTestsTimeout: HangingTestsTimeout + get() = state.hangingTestsTimeout + set(value) { + state.hangingTestsTimeout = value + } + + val staticsMocking: StaticsMocking get() = state.staticsMocking + + val runInspectionAfterTestGeneration: Boolean get() = state.runInspectionAfterTestGeneration + + val forceStaticMocking: ForceStaticMocking get() = state.forceStaticMocking + + val experimentalLanguagesSupport: Boolean get () = state.enableExperimentalLanguagesSupport + + val treatOverflowAsError: TreatOverflowAsError get() = state.treatOverflowAsError + + val parametrizedTestSource: ParametrizedTestSource get() = state.parametrizedTestSource + + val classesToMockAlways: Set get() = state.classesToMockAlways.toSet() + + val javaDocCommentStyle: JavaDocCommentStyle get() = state.commentStyle + + var fuzzingValue: Double + get() = state.fuzzingValue + set(value) { + state.fuzzingValue = value.coerceIn(0.0, 1.0) + } + var runGeneratedTestsWithCoverage = state.runGeneratedTestsWithCoverage + + var enableSummariesGeneration = state.summariesGenerationType + + fun setClassesToMockAlways(classesToMockAlways: List) { + state.classesToMockAlways = classesToMockAlways.distinct().toTypedArray() + } + + override fun getState(): State = state + + override fun initializeComponent() { + super.initializeComponent() + CompletableFuture.runAsync { + FileUtil.clearTempDirectory(UtSettings.daysLimitForTempFiles) + + // Don't replace file with custom user's settings + if (UtSettings.areCustomized()) return@runAsync + // In case settings.properties file is not yet presented + // (or stays with all default template values) in {homeDir}/.utbot folder + // we copy (or re-write) it from plugin resource file + val settingsClass = javaClass + Paths.get(UtSettings.defaultSettingsPath()).toFile().apply { + try { + this.parentFile.apply { + if (this.mkdirs() && isWindows) Files.setAttribute(this.toPath(), "dos:hidden", true) + } + settingsClass.getResource("../../../../../settings.properties")?.let { + this.writeBytes(it.openStream().readBytes()) + } + } catch (ignored: IOException) { + } + } + } + } + + override fun loadState(state: State) { + this.state = state + if (!state.codegenLanguage.isSummarizationCompatible()) { + this.state.summariesGenerationType = SummariesGenerationType.NONE + } + } + + // these classes are all ref types so we can use only names here + fun chosenClassesToMockAlways(): Set = state.classesToMockAlways.mapTo(mutableSetOf()) { ClassId(it) } + + fun setProviderByLoader(loader: KClass<*>, provider: CodeGenerationSettingItem) = + when (loader) { + // TODO: service loaders for test generator and code generator are removed from settings temporarily +// TestGeneratorServiceLoader::class -> setGeneratorName(provider) +// CodeGeneratorServiceLoader::class -> setCodeGeneratorName(provider) + MockStrategyApi::class -> state.mockStrategy = provider as MockStrategyApi + CodegenLanguage::class -> state.codegenLanguage = provider as CodegenLanguage + RuntimeExceptionTestsBehaviour::class -> { + state.runtimeExceptionTestsBehaviour = provider as RuntimeExceptionTestsBehaviour + } + ForceStaticMocking::class -> state.forceStaticMocking = provider as ForceStaticMocking + TreatOverflowAsError::class -> { + // TODO: SAT-1566 + state.treatOverflowAsError = provider as TreatOverflowAsError + UtSettings.treatOverflowAsError = provider == TreatOverflowAsError.AS_ERROR + } + JavaDocCommentStyle::class -> state.commentStyle = provider as JavaDocCommentStyle + // TODO: add error processing + else -> error("Unknown class [$loader] to map value [$provider]") + } + + fun providerNameByServiceLoader(loader: KClass<*>): CodeGenerationSettingItem = + when (loader) { + // TODO: service loaders for test generator and code generator are removed from settings temporarily +// TestGeneratorServiceLoader::class -> generatorName +// CodeGeneratorServiceLoader::class -> codeGeneratorName + MockStrategyApi::class -> mockStrategy + CodegenLanguage::class -> codegenLanguage + RuntimeExceptionTestsBehaviour::class -> runtimeExceptionTestsBehaviour + ForceStaticMocking::class -> forceStaticMocking + TreatOverflowAsError::class -> treatOverflowAsError + JavaDocCommentStyle::class -> javaDocCommentStyle + // TODO: add error processing + else -> error("Unknown service loader: $loader") + } +} + +// use it to serialize testFramework in State +private class TestFrameworkConverter : Converter() { + override fun toString(value: TestFramework): String = "$value" + + override fun fromString(value: String): TestFramework = when (value) { + Junit4.id -> Junit4 + Junit5.id -> Junit5 + TestNg.id -> TestNg + else -> error("Unknown TestFramework $value") + } +} + +// use it to serialize staticsMocking in State +private class StaticsMockingConverter : Converter() { + override fun toString(value: StaticsMocking): String = "$value" + + override fun fromString(value: String): StaticsMocking = when (value) { + NoStaticMocking.id -> NoStaticMocking + MockitoStaticMocking.id -> MockitoStaticMocking + else -> error("Unknown StaticsMocking $value") + } +} + +// TODO is it better to use kotlinx.serialization? +// use it to serialize hangingTestsTimeout in State +private class HangingTestsTimeoutConverter : Converter() { + override fun toString(value: HangingTestsTimeout): String = + "HangingTestsTimeout:${value.timeoutMs}" + + override fun fromString(value: String): HangingTestsTimeout { + val arguments = value.substringAfter("HangingTestsTimeout:") + val timeoutMs = arguments.toLong() + return HangingTestsTimeout(timeoutMs) + } +} diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/UtSettingsHelper.kt b/utbot-ui-commons/src/main/kotlin/org/utbot/intellij/plugin/util/UtSettingsHelper.kt similarity index 100% rename from utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/UtSettingsHelper.kt rename to utbot-ui-commons/src/main/kotlin/org/utbot/intellij/plugin/util/UtSettingsHelper.kt