From 4bb432959f640b1e3e62a0b8b721536e2cce219e Mon Sep 17 00:00:00 2001 From: Vassiliy Kudryashov Date: Thu, 5 Oct 2023 10:12:23 +0300 Subject: [PATCH 1/3] Make JDK names more readable (in unsupported version warning) (#2646) Use better JDK names in unsupported version warning #2635 --- .../org/utbot/intellij/plugin/ui/GenerateTestsDialogWindow.kt | 4 +++- .../src/test/kotlin/org/utbot/tests/UnitTestBotActionTest.kt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) 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 8076c2208a..e0cc22c797 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 @@ -520,7 +520,7 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m text = run { val sdkVersion = findSdkVersionOrNull(this@GenerateTestsDialogWindow.model.srcModule)?.feature if (sdkVersion != null) { - "SDK version $sdkVersion is not supported, use ${JavaSdkVersion.JDK_1_8}, ${JavaSdkVersion.JDK_11} or ${JavaSdkVersion.JDK_17}" + "SDK version $sdkVersion is not supported, use ${JavaSdkVersion.JDK_1_8.toReadableString()}, ${JavaSdkVersion.JDK_11.toReadableString()} or ${JavaSdkVersion.JDK_17.toReadableString()} instead." } else { "SDK is not defined" } @@ -533,6 +533,8 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m }) } + fun JavaSdkVersion.toReadableString() : String = toString().replace("JDK_", "").replace('_', '.') + override fun getBackground(): Color? = EditorColorsManager.getInstance().globalScheme.getColor(HintUtil.ERROR_COLOR_KEY) ?: super.getBackground() diff --git a/utbot-intellij/src/test/kotlin/org/utbot/tests/UnitTestBotActionTest.kt b/utbot-intellij/src/test/kotlin/org/utbot/tests/UnitTestBotActionTest.kt index 3d5f7acb87..e06b403bc8 100644 --- a/utbot-intellij/src/test/kotlin/org/utbot/tests/UnitTestBotActionTest.kt +++ b/utbot-intellij/src/test/kotlin/org/utbot/tests/UnitTestBotActionTest.kt @@ -68,7 +68,7 @@ class UnitTestBotActionTest : BaseTest() { textEditor().typeAdditionFunction(newClassName) openUTBotDialogFromProjectViewForClass(newClassName) assertThat(unitTestBotDialog.generateTestsButton.isEnabled().not()) - assertThat(unitTestBotDialog.sdkNotificationLabel.hasText("SDK version 19 is not supported, use JDK_1_8, JDK_11 or JDK_17")) + assertThat(unitTestBotDialog.sdkNotificationLabel.hasText("SDK version 19 is not supported, use 1.8, 11 or 17 instead.")) assertThat(unitTestBotDialog.setupSdkLink.isShowing) unitTestBotDialog.closeButton.click() } From 389fe664c7a25ccb49a591ac3574ce5e754836a0 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov <71839386+IlyaMuravjov@users.noreply.github.com> Date: Thu, 5 Oct 2023 15:44:29 +0300 Subject: [PATCH 2/3] Introduce and test `UtBotSpringApi` (#2632) * Introduce and test `UtBotSpringApi` * Remove repeated dependency * Make `UtBotSpringApi` validate that integration tests are only used with `PresentSpringSettings` * Add `META-INF/spring` to spring-sample shadow jar * Add `UtBotSpringApi` tests for `AbsentSpringSettings` and profiles --- .../org/utbot/framework/plugin/api/Api.kt | 5 +- .../framework/process/EngineProcessMain.kt | 2 +- .../framework/process/EngineProcessTask.kt | 4 +- .../generator/UtTestsDialogProcessor.kt | 2 +- .../plugin/ui/GenerateTestsDialogWindow.kt | 2 +- .../org/utbot/external/api/UtBotSpringApi.kt | 66 +++++++ ...CgSpringIntegrationTestClassConstructor.kt | 2 +- .../spring/SpringApplicationContextImpl.kt | 20 +- .../framework/process/SpringAnalyzerTask.kt | 3 +- utbot-spring-sample/build.gradle | 19 -- utbot-spring-sample/build.gradle.kts | 45 +++++ .../config/boot/ExampleSpringBootConfig.java | 7 + .../config/boot/ExampleSpringBootService.java | 11 ++ .../config/pure/ExamplePureSpringConfig.java | 34 ++++ .../config/pure/ExamplePureSpringService.java | 4 + .../spring/config/utils/SafetyUtils.java | 15 ++ .../spring/config/xml/ExampleXmlService.java | 9 + .../src/main/resources/xml-spring-config.xml | 9 + utbot-spring-test/build.gradle | 14 ++ .../spring/manual/UtBotSpringApiTest.java | 178 ++++++++++++++++++ .../SpringNoConfigUtValueTestCaseChecker.kt | 2 +- 21 files changed, 421 insertions(+), 32 deletions(-) create mode 100644 utbot-spring-framework/src/main/kotlin/org/utbot/external/api/UtBotSpringApi.kt delete mode 100644 utbot-spring-sample/build.gradle create mode 100644 utbot-spring-sample/build.gradle.kts create mode 100644 utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/boot/ExampleSpringBootConfig.java create mode 100644 utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/boot/ExampleSpringBootService.java create mode 100644 utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/pure/ExamplePureSpringConfig.java create mode 100644 utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/pure/ExamplePureSpringService.java create mode 100644 utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/utils/SafetyUtils.java create mode 100644 utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/xml/ExampleXmlService.java create mode 100644 utbot-spring-sample/src/main/resources/xml-spring-config.xml create mode 100644 utbot-spring-test/src/test/java/org/utbot/examples/spring/manual/UtBotSpringApiTest.java 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 6759311300..88979a6d68 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 @@ -1430,7 +1430,10 @@ sealed class SpringConfiguration(val fullDisplayName: String) { class JavaConfiguration(override val configBinaryName: String) : JavaBasedConfiguration(configBinaryName) - class SpringBootConfiguration(override val configBinaryName: String, val isUnique: Boolean): JavaBasedConfiguration(configBinaryName) + class SpringBootConfiguration( + override val configBinaryName: String, + val isDefinitelyUnique: Boolean + ) : JavaBasedConfiguration(configBinaryName) class XMLConfiguration(val absolutePath: String) : SpringConfiguration(absolutePath) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt index 3b2f98e51c..2be3d2f84d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt @@ -250,7 +250,7 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch } watchdog.measureTimeForActiveCall(perform, "Performing dynamic task") { params -> val task = kryoHelper.readObject>(params.engineProcessTask) - val result = task.perform(kryoHelper) + val result = task.perform() kryoHelper.writeObject(result) } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessTask.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessTask.kt index 7a2b0d6618..d42c3f65b0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessTask.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessTask.kt @@ -1,7 +1,5 @@ package org.utbot.framework.process -import org.utbot.framework.process.kryo.KryoHelper - /** * Implementations of this interface can be passed to engine process for execution and should * be used for adding feature-specific (e.g. Spring-specific) tasks without inflating core UtBot codebase. @@ -12,5 +10,5 @@ import org.utbot.framework.process.kryo.KryoHelper * @param R result type of the task (should be present on the classpath of both processes). */ interface EngineProcessTask { - fun perform(kryoHelper: KryoHelper): R + fun perform(): R } \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt index 4b8ff4c0e8..1d43bd9163 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt @@ -292,7 +292,7 @@ object UtTestsDialogProcessor { val clarifiedBeanDefinitions = clarifyBeanDefinitionReturnTypes(beanDefinitions, project) - SpringApplicationContextImpl( + SpringApplicationContextImpl.internalCreate( simpleApplicationContext, clarifiedBeanDefinitions, model.springTestType, 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 e0cc22c797..c5d71d36f0 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 @@ -727,7 +727,7 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m if (springBootConfigs.contains(classBinaryName)) { SpringConfiguration.SpringBootConfiguration( configBinaryName = classBinaryName, - isUnique = springBootConfigs.size == 1, + isDefinitelyUnique = springBootConfigs.size == 1, ) } else { SpringConfiguration.JavaConfiguration(classBinaryName) diff --git a/utbot-spring-framework/src/main/kotlin/org/utbot/external/api/UtBotSpringApi.kt b/utbot-spring-framework/src/main/kotlin/org/utbot/external/api/UtBotSpringApi.kt new file mode 100644 index 0000000000..0442ef1092 --- /dev/null +++ b/utbot-spring-framework/src/main/kotlin/org/utbot/external/api/UtBotSpringApi.kt @@ -0,0 +1,66 @@ +package org.utbot.external.api + +import org.utbot.framework.context.ApplicationContext +import org.utbot.framework.context.simple.SimpleApplicationContext +import org.utbot.framework.context.spring.SpringApplicationContext +import org.utbot.framework.context.spring.SpringApplicationContextImpl +import org.utbot.framework.plugin.api.SpringConfiguration +import org.utbot.framework.plugin.api.SpringSettings +import org.utbot.framework.plugin.api.SpringTestType +import org.utbot.framework.process.SpringAnalyzerTask +import java.io.File + +object UtBotSpringApi { + private val springBootConfigAnnotations = setOf( + "org.springframework.boot.autoconfigure.SpringBootApplication", + "org.springframework.boot.SpringBootConfiguration" + ) + + /** + * NOTE: [classpath] should include project under test classpath (with all dependencies) as well as + * `spring-test`, `spring-boot-test`, and `spring-security-test` if respectively `spring-beans`, + * `spring-boot`, and `spring-security-core` are dependencies of project under test. + * + * UtBot doesn't add Spring test modules to classpath automatically to let API users control their versions. + */ + @JvmOverloads + @JvmStatic + fun createSpringApplicationContext( + springSettings: SpringSettings, + springTestType: SpringTestType, + classpath: List, + delegateContext: ApplicationContext = SimpleApplicationContext() + ): SpringApplicationContext { + if (springTestType == SpringTestType.INTEGRATION_TEST) { + require(springSettings is SpringSettings.PresentSpringSettings) { + "Integration tests can't be generated without Spring settings" + } + val configuration = springSettings.configuration + require(configuration !is SpringConfiguration.XMLConfiguration) { + "Integration tests aren't supported for XML configurations, consider using Java " + + "configuration that imports your XML configuration with @ImportResource" + } + } + return SpringApplicationContextImpl.internalCreate( + delegateContext = delegateContext, + beanDefinitions = when (springSettings) { + SpringSettings.AbsentSpringSettings -> listOf() + is SpringSettings.PresentSpringSettings -> SpringAnalyzerTask(classpath, springSettings).perform() + }, + springTestType = springTestType, + springSettings = springSettings, + ) + } + + @JvmStatic + fun createXmlSpringConfiguration(xmlConfig: File): SpringConfiguration.XMLConfiguration = + SpringConfiguration.XMLConfiguration(xmlConfig.absolutePath) + + @JvmStatic + fun createJavaSpringConfiguration(javaConfig: Class<*>): SpringConfiguration.JavaBasedConfiguration = + if (javaConfig.annotations.any { it.annotationClass.java.name in springBootConfigAnnotations }) { + SpringConfiguration.SpringBootConfiguration(javaConfig.name, isDefinitelyUnique = false) + } else { + SpringConfiguration.JavaConfiguration(javaConfig.name) + } +} \ No newline at end of file diff --git a/utbot-spring-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringIntegrationTestClassConstructor.kt b/utbot-spring-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringIntegrationTestClassConstructor.kt index fa6ed5fbaf..7dbcfce6ac 100644 --- a/utbot-spring-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringIntegrationTestClassConstructor.kt +++ b/utbot-spring-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringIntegrationTestClassConstructor.kt @@ -194,7 +194,7 @@ class CgSpringIntegrationTestClassConstructor( // TODO: we support only JavaBasedConfiguration in integration tests. // Adapt for XMLConfigurations when supported. val configClass = springSettings.configuration as JavaBasedConfiguration - if (configClass is JavaConfiguration || configClass is SpringBootConfiguration && !configClass.isUnique) { + if (configClass is JavaConfiguration || configClass is SpringBootConfiguration && !configClass.isDefinitelyUnique) { addAnnotation( classId = contextConfigurationClassId, namedArguments = listOf( diff --git a/utbot-spring-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContextImpl.kt b/utbot-spring-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContextImpl.kt index f70bc85ae3..bec479112e 100644 --- a/utbot-spring-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContextImpl.kt +++ b/utbot-spring-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContextImpl.kt @@ -5,6 +5,7 @@ import org.utbot.common.dynamicPropertiesOf import org.utbot.common.isAbstract import org.utbot.common.isStatic import org.utbot.common.withValue +import org.utbot.external.api.UtBotSpringApi import org.utbot.framework.UtSettings import org.utbot.framework.codegen.generator.AbstractCodeGenerator import org.utbot.framework.codegen.generator.CodeGeneratorParams @@ -39,14 +40,29 @@ import org.utbot.fuzzing.spring.unit.InjectMockValueProvider import org.utbot.fuzzing.toFuzzerType import org.utbot.instrumentation.instrumentation.execution.RemovingConstructFailsUtExecutionInstrumentation -class SpringApplicationContextImpl( +class SpringApplicationContextImpl private constructor( private val delegateContext: ApplicationContext, - override val beanDefinitions: List = emptyList(), + override val beanDefinitions: List, private val springTestType: SpringTestType, override val springSettings: SpringSettings, ): ApplicationContext by delegateContext, SpringApplicationContext { companion object { private val logger = KotlinLogging.logger {} + + /** + * Used internally by UtBot to create an instance of [SpringApplicationContextImpl] + * when [beanDefinitions] are already known. + * + * NOTE: Bean definitions defined in config from [springSettings] are IGNORED. + * + * API users should use [UtBotSpringApi.createSpringApplicationContext] + */ + fun internalCreate( + delegateContext: ApplicationContext, + beanDefinitions: List, + springTestType: SpringTestType, + springSettings: SpringSettings, + ) = SpringApplicationContextImpl(delegateContext, beanDefinitions, springTestType, springSettings) } private object ReplacedFuzzedTypeFlag : FuzzedTypeFlag diff --git a/utbot-spring-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerTask.kt b/utbot-spring-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerTask.kt index ab07f85d8f..b4f3ea2a2f 100644 --- a/utbot-spring-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerTask.kt +++ b/utbot-spring-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerTask.kt @@ -4,7 +4,6 @@ import mu.KotlinLogging import org.utbot.framework.plugin.api.BeanAdditionalData import org.utbot.framework.plugin.api.BeanDefinitionData import org.utbot.framework.plugin.api.SpringSettings.PresentSpringSettings -import org.utbot.framework.process.kryo.KryoHelper import org.utbot.rd.use import org.utbot.spring.process.SpringAnalyzerProcess @@ -16,7 +15,7 @@ class SpringAnalyzerTask( private val logger = KotlinLogging.logger {} } - override fun perform(kryoHelper: KryoHelper): List = try { + override fun perform(): List = try { SpringAnalyzerProcess.createBlocking(classpath).use { it.getBeanDefinitions(settings) }.beanDefinitions diff --git a/utbot-spring-sample/build.gradle b/utbot-spring-sample/build.gradle deleted file mode 100644 index df512a7209..0000000000 --- a/utbot-spring-sample/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id 'java-library' -} - -dependencies { - implementation 'org.projectlombok:lombok:1.18.20' - annotationProcessor 'org.projectlombok:lombok:1.18.20' - - implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion - implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: springBootVersion -} - -// This is required to avoid conflict between SpringBoot standard logger and the logger of our project. -// See https://stackoverflow.com/a/28735604 for more details. -configurations { - all { - exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' - } -} \ No newline at end of file diff --git a/utbot-spring-sample/build.gradle.kts b/utbot-spring-sample/build.gradle.kts new file mode 100644 index 0000000000..bd0b3b4ea3 --- /dev/null +++ b/utbot-spring-sample/build.gradle.kts @@ -0,0 +1,45 @@ +import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer + +plugins { + id("com.github.johnrengelman.shadow") version "7.1.2" + id("java") +} + +val springBootVersion: String by rootProject + +dependencies { + implementation("org.projectlombok:lombok:1.18.20") + annotationProcessor("org.projectlombok:lombok:1.18.20") + + implementation(group = "org.springframework.boot", name = "spring-boot-starter-web", version = springBootVersion) + implementation(group = "org.springframework.boot", name = "spring-boot-starter-data-jpa", version = springBootVersion) + implementation(group = "org.springframework.boot", name = "spring-boot-starter-test", version = springBootVersion) +} + +tasks.shadowJar { + isZip64 = true + + transform(Log4j2PluginsCacheFileTransformer::class.java) + archiveFileName.set("utbot-spring-sample-shadow.jar") + + // Required for Spring to run properly when using shadowJar + // More details: https://github.com/spring-projects/spring-boot/issues/1828 + mergeServiceFiles() + append("META-INF/spring.handlers") + append("META-INF/spring.schemas") + append("META-INF/spring.tooling") + transform(PropertiesFileTransformer().apply { + paths = listOf("META-INF/spring.factories") + mergeStrategy = "append" + }) +} + +val springSampleJar: Configuration by configurations.creating { + isCanBeResolved = false + isCanBeConsumed = true +} + +artifacts { + add(springSampleJar.name, tasks.shadowJar) +} \ No newline at end of file diff --git a/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/boot/ExampleSpringBootConfig.java b/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/boot/ExampleSpringBootConfig.java new file mode 100644 index 0000000000..b437460c7d --- /dev/null +++ b/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/boot/ExampleSpringBootConfig.java @@ -0,0 +1,7 @@ +package org.utbot.examples.spring.config.boot; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ExampleSpringBootConfig { +} diff --git a/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/boot/ExampleSpringBootService.java b/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/boot/ExampleSpringBootService.java new file mode 100644 index 0000000000..99e89e7b9d --- /dev/null +++ b/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/boot/ExampleSpringBootService.java @@ -0,0 +1,11 @@ +package org.utbot.examples.spring.config.boot; + +import org.springframework.stereotype.Service; +import org.utbot.examples.spring.config.utils.SafetyUtils; + +@Service +public class ExampleSpringBootService { + public ExampleSpringBootService() { + SafetyUtils.shouldNeverBeCalled(); + } +} diff --git a/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/pure/ExamplePureSpringConfig.java b/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/pure/ExamplePureSpringConfig.java new file mode 100644 index 0000000000..e0fa208ee8 --- /dev/null +++ b/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/pure/ExamplePureSpringConfig.java @@ -0,0 +1,34 @@ +package org.utbot.examples.spring.config.pure; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Profile; +import org.utbot.examples.spring.config.utils.SafetyUtils; + +public class ExamplePureSpringConfig { + @Bean(name = "exampleService0") + public ExamplePureSpringService exampleService() { + SafetyUtils.shouldNeverBeCalled(); + return null; + } + + @Bean(name = "exampleServiceTest1") + @Profile("test1") + public ExamplePureSpringService exampleServiceTest1() { + SafetyUtils.shouldNeverBeCalled(); + return null; + } + + @Bean(name = "exampleServiceTest2") + @Profile("test2") + public ExamplePureSpringService exampleServiceTest2() { + SafetyUtils.shouldNeverBeCalled(); + return null; + } + + @Bean(name = "exampleServiceTest3") + @Profile("test3") + public ExamplePureSpringService exampleServiceTest3() { + SafetyUtils.shouldNeverBeCalled(); + return null; + } +} diff --git a/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/pure/ExamplePureSpringService.java b/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/pure/ExamplePureSpringService.java new file mode 100644 index 0000000000..bed899616d --- /dev/null +++ b/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/pure/ExamplePureSpringService.java @@ -0,0 +1,4 @@ +package org.utbot.examples.spring.config.pure; + +public class ExamplePureSpringService { +} diff --git a/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/utils/SafetyUtils.java b/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/utils/SafetyUtils.java new file mode 100644 index 0000000000..94f12774bb --- /dev/null +++ b/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/utils/SafetyUtils.java @@ -0,0 +1,15 @@ +package org.utbot.examples.spring.config.utils; + +public class SafetyUtils { + + private static final int UNEXPECTED_CALL_EXIT_STATUS = -1182; + + /** + * Bean constructors and factory methods should never be executed during bean analysis, + * hence call to this method is added into them to ensure they are actually never called. + */ + public static void shouldNeverBeCalled() { + System.err.println("shouldNeverBeCalled() is unexpectedly called"); + System.exit(UNEXPECTED_CALL_EXIT_STATUS); + } +} diff --git a/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/xml/ExampleXmlService.java b/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/xml/ExampleXmlService.java new file mode 100644 index 0000000000..d2f57e7d32 --- /dev/null +++ b/utbot-spring-sample/src/main/java/org/utbot/examples/spring/config/xml/ExampleXmlService.java @@ -0,0 +1,9 @@ +package org.utbot.examples.spring.config.xml; + +import org.utbot.examples.spring.config.utils.SafetyUtils; + +public class ExampleXmlService { + public ExampleXmlService() { + SafetyUtils.shouldNeverBeCalled(); + } +} diff --git a/utbot-spring-sample/src/main/resources/xml-spring-config.xml b/utbot-spring-sample/src/main/resources/xml-spring-config.xml new file mode 100644 index 0000000000..5118152230 --- /dev/null +++ b/utbot-spring-sample/src/main/resources/xml-spring-config.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/utbot-spring-test/build.gradle b/utbot-spring-test/build.gradle index f9941479cf..631e4a6d19 100644 --- a/utbot-spring-test/build.gradle +++ b/utbot-spring-test/build.gradle @@ -1,3 +1,7 @@ +configurations { + fetchSpringSampleJar +} + dependencies { testImplementation(project(":utbot-framework")) testImplementation(project(":utbot-spring-framework")) @@ -16,7 +20,11 @@ dependencies { testImplementation group: 'org.mockito', name: 'mockito-inline', version: mockitoInlineVersion testImplementation group: 'org.jacoco', name: 'org.jacoco.report', version: jacocoVersion + testImplementation group: 'commons-io', name: 'commons-io', version: commonsIoVersion + testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: springBootVersion + + fetchSpringSampleJar project(path: ':utbot-spring-sample', configuration: 'springSampleJar') } configurations { @@ -30,3 +38,9 @@ test { jvmArgs '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9009' } } + +processTestResources { + from(configurations.fetchSpringSampleJar) { + into "lib" + } +} diff --git a/utbot-spring-test/src/test/java/org/utbot/examples/spring/manual/UtBotSpringApiTest.java b/utbot-spring-test/src/test/java/org/utbot/examples/spring/manual/UtBotSpringApiTest.java new file mode 100644 index 0000000000..94a62cbc0f --- /dev/null +++ b/utbot-spring-test/src/test/java/org/utbot/examples/spring/manual/UtBotSpringApiTest.java @@ -0,0 +1,178 @@ +package org.utbot.examples.spring.manual; + +import org.apache.commons.io.FileUtils; +import org.junit.Test; +import org.utbot.common.FileUtil; +import org.utbot.common.JarUtils; +import org.utbot.examples.spring.config.boot.ExampleSpringBootConfig; +import org.utbot.examples.spring.config.pure.ExamplePureSpringConfig; +import org.utbot.external.api.UtBotSpringApi; +import org.utbot.framework.context.spring.SpringApplicationContext; +import org.utbot.framework.plugin.api.*; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; + +public class UtBotSpringApiTest { + private static final List DEFAULT_PROFILES = Collections.singletonList("default"); + + @Test + public void testOnAbsentSpringSettings() { + SpringApplicationContext springApplicationContext = + UtBotSpringApi.createSpringApplicationContext( + SpringSettings.AbsentSpringSettings.INSTANCE, + SpringTestType.UNIT_TEST, + new ArrayList<>() + ); + + assertEquals(new ArrayList<>(), springApplicationContext.getBeanDefinitions()); + } + + @Test + public void testOnSpringBootConfig() { + List actual = getBeansFromSampleProject( + UtBotSpringApi.createJavaSpringConfiguration(ExampleSpringBootConfig.class), + DEFAULT_PROFILES + ); + + List expected = new ArrayList<>(); + expected.add(new BeanDefinitionData( + "exampleSpringBootConfig", + "org.utbot.examples.spring.config.boot.ExampleSpringBootConfig", + null + )); + expected.add(new BeanDefinitionData( + "exampleSpringBootService", + "org.utbot.examples.spring.config.boot.ExampleSpringBootService", + null + )); + + assertEquals(expected, actual); + } + + @Test + public void testOnPureSpringConfig() { + List actual = getBeansFromSampleProject( + UtBotSpringApi.createJavaSpringConfiguration(ExamplePureSpringConfig.class), + DEFAULT_PROFILES + ); + + List expected = new ArrayList<>(); + expected.add(new BeanDefinitionData("examplePureSpringConfig", "org.utbot.examples.spring.config.pure.ExamplePureSpringConfig", null)); + expected.add(new BeanDefinitionData( + "exampleService0", + "org.utbot.examples.spring.config.pure.ExamplePureSpringService", + new BeanAdditionalData( + "exampleService", + Collections.emptyList(), + "org.utbot.examples.spring.config.pure.ExamplePureSpringConfig" + ) + )); + + assertEquals(expected, actual); + } + + @Test + public void testOnPureSpringConfigWithProfiles() { + List profiles = new ArrayList<>(); + profiles.add("test1"); + profiles.add("test3"); + + List actual = getBeansFromSampleProject( + UtBotSpringApi.createJavaSpringConfiguration(ExamplePureSpringConfig.class), + profiles + ); + + List expected = new ArrayList<>(); + expected.add(new BeanDefinitionData("examplePureSpringConfig", "org.utbot.examples.spring.config.pure.ExamplePureSpringConfig", null)); + expected.add(new BeanDefinitionData( + "exampleService0", + "org.utbot.examples.spring.config.pure.ExamplePureSpringService", + new BeanAdditionalData( + "exampleService", + Collections.emptyList(), + "org.utbot.examples.spring.config.pure.ExamplePureSpringConfig" + ) + )); + expected.add(new BeanDefinitionData( + "exampleServiceTest1", + "org.utbot.examples.spring.config.pure.ExamplePureSpringService", + new BeanAdditionalData( + "exampleServiceTest1", + Collections.emptyList(), + "org.utbot.examples.spring.config.pure.ExamplePureSpringConfig" + ) + )); + expected.add(new BeanDefinitionData( + "exampleServiceTest3", + "org.utbot.examples.spring.config.pure.ExamplePureSpringService", + new BeanAdditionalData( + "exampleServiceTest3", + Collections.emptyList(), + "org.utbot.examples.spring.config.pure.ExamplePureSpringConfig" + ) + )); + + assertEquals(expected, actual); + } + + @Test + public void testOnXmlConfig() throws IOException { + URL configUrl = Objects.requireNonNull(getClass().getClassLoader().getResource("xml-spring-config.xml")); + File configDir = FileUtil.INSTANCE.createTempDirectory("xml-config").toFile(); + File configFile = new File(configDir, "xml-spring-config.xml"); + FileUtils.copyURLToFile(configUrl, configFile); + List additionalClasspath = Collections.singletonList(configDir.getAbsolutePath()); + + List actual = getBeansFromSampleProject( + UtBotSpringApi.createXmlSpringConfiguration(configFile), + additionalClasspath + ); + + List expected = new ArrayList<>(); + expected.add(new BeanDefinitionData( + "xmlService", + "org.utbot.examples.spring.config.xml.ExampleXmlService", + null + )); + + assertEquals(expected, actual); + } + + private List getBeansFromSampleProject( + SpringConfiguration configuration, + List profiles + ) { + return getBeansFromSampleProject(configuration, profiles, new ArrayList<>()); + } + + private List getBeansFromSampleProject( + SpringConfiguration configuration, + List profiles, + List additionalClasspath + ) { + SpringSettings springSettings = new SpringSettings.PresentSpringSettings(configuration, profiles); + SpringTestType springTestType = SpringTestType.UNIT_TEST; + List classpath = new ArrayList<>(additionalClasspath); + classpath.add( + JarUtils.INSTANCE.extractJarFileFromResources( + "utbot-spring-sample-shadow.jar", + "lib/utbot-spring-sample-shadow.jar", + "spring-sample" + ).getAbsolutePath() + ); + SpringApplicationContext springApplicationContext = + UtBotSpringApi.createSpringApplicationContext(springSettings, springTestType, classpath); + return springApplicationContext.getBeanDefinitions().stream() + .filter(beanDef -> beanDef.getBeanTypeName().startsWith("org.utbot.examples")) + .collect(Collectors.toList()); + } +} diff --git a/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/autowiring/SpringNoConfigUtValueTestCaseChecker.kt b/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/autowiring/SpringNoConfigUtValueTestCaseChecker.kt index 5a713137c5..a0977e9e35 100644 --- a/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/autowiring/SpringNoConfigUtValueTestCaseChecker.kt +++ b/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/autowiring/SpringNoConfigUtValueTestCaseChecker.kt @@ -8,7 +8,7 @@ import org.utbot.testing.UtValueTestCaseChecker import org.utbot.testing.defaultApplicationContext import kotlin.reflect.KClass -val springNoConfigApplicationContext = SpringApplicationContextImpl( +val springNoConfigApplicationContext = SpringApplicationContextImpl.internalCreate( delegateContext = defaultApplicationContext, springTestType = SpringTestType.UNIT_TEST, springSettings = SpringSettings.AbsentSpringSettings, From 9242d6e61666ba93e51a8a8e56e199284bb9adf0 Mon Sep 17 00:00:00 2001 From: Alena Lisevych Date: Thu, 5 Oct 2023 17:36:59 +0300 Subject: [PATCH 3/3] plugin.xml changes for release 2023.10 (#2651) * Update change notes in plugin.xml for release 2023.10 --- .../src/main/resources/META-INF/plugin.xml | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/utbot-intellij/src/main/resources/META-INF/plugin.xml b/utbot-intellij/src/main/resources/META-INF/plugin.xml index c21e12800d..5cc33db475 100644 --- a/utbot-intellij/src/main/resources/META-INF/plugin.xml +++ b/utbot-intellij/src/main/resources/META-INF/plugin.xml @@ -56,8 +56,6 @@
  • generating SARIF reports
  • innovative symbolic execution engine combined with a smart fuzzing platform
  • - UnitTestBot supports the latest JDKs, JUnit 4, JUnit 5, TestNG, Mockito and is suitable for all popular operational systems. -
    Try UnitTestBot online demo to see how it generates tests for your code in real time.
    Contribute to UnitTestBot via GitHub. @@ -69,21 +67,28 @@ +
  • It automatically detects if you use the Spring framework and provides you with necessary options right in the dialog window.
  • +
  • You can choose from the three approaches to Spring test generation:
  • +
      +
    • standard unit tests that mock environmental interactions,
    • +
    • Spring-specific unit tests that use information about the Spring application context,
    • +
    • and integration tests that validate interactions between Spring components.
    • +
    + + Find more improvements and bug fixes:
      -
    • Generating tests for Python and JavaScript
    • -
    • New fuzzing platform providing support for multiple languages
    • -
    • Improved test generation for Kotlin code
    • -
    • Multiprocess architecture based on the Reactive Distributed communication framework with advanced logging and debugging options
    • -
    • Symbolic execution engine with higher priority and fewer false UNSAT verdicts
    • -
    • UI/UX improvements for test sources root, setting.properties file, cancellation, timeout settings, notifications
    • -
    • Summaries with fine-grained settings
    • -
    • SARIF reports enabled by default, displaying sandbox-related and timeout test failures, properly addressing source files
    • -
    • Improved monitoring visualized with Grafana
    • -
    • Test generation bug fixes
    • -
    • Detailed documentation on UnitTestBot components including UnitTestBot architecture overview
    • +
    • Support for IntelliJ IDEA 2023.2
    • +
    • Taint analysis feature (experimental)
    • +
    • Improved mocking in symbolic execution engine
    • +
    • Enhanced fuzzing mechanism: improved domain-specific API and mutation processes; support for generic fields and resolving generic parameter types; single branch detection, and ability to use all public methods of a class under test
    • +
    • Improved UIs for standard Java, Spring, and Python test generation
    • +
    • Fixed bugs for symbolic execution engine, fuzzing, code generation and instrumented process, summaries, SARIF reports, and more
    • +
    • Multiple improvements for Python support related to rendering constructors; mastering exceptions, timed out tests, and regular expressions; fixes for coverage and shutting down behavior
    • +
    • Enhanced Go test generation: support for maps and user-defined types
    - ]]> + ]]>