Skip to content

Commit

Permalink
Merge branch 'main' into unit-test-bot/rc2023.10
Browse files Browse the repository at this point in the history
  • Loading branch information
Vassiliy-Kudryashov authored Oct 9, 2023
2 parents f51df99 + 9242d6e commit 20ee749
Show file tree
Hide file tree
Showing 21 changed files with 421 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
}
watchdog.measureTimeForActiveCall(perform, "Performing dynamic task") { params ->
val task = kryoHelper.readObject<EngineProcessTask<Any?>>(params.engineProcessTask)
val result = task.perform(kryoHelper)
val result = task.perform()
kryoHelper.writeObject(result)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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<R> {
fun perform(kryoHelper: KryoHelper): R
fun perform(): R
}
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ object UtTestsDialogProcessor {
val clarifiedBeanDefinitions =
clarifyBeanDefinitionReturnTypes(beanDefinitions, project)

SpringApplicationContextImpl(
SpringApplicationContextImpl.internalCreate(
simpleApplicationContext,
clarifiedBeanDefinitions,
model.springTestType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String>,
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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<BeanDefinitionData> = emptyList(),
override val beanDefinitions: List<BeanDefinitionData>,
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<BeanDefinitionData>,
springTestType: SpringTestType,
springSettings: SpringSettings,
) = SpringApplicationContextImpl(delegateContext, beanDefinitions, springTestType, springSettings)
}

private object ReplacedFuzzedTypeFlag : FuzzedTypeFlag
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -16,7 +15,7 @@ class SpringAnalyzerTask(
private val logger = KotlinLogging.logger {}
}

override fun perform(kryoHelper: KryoHelper): List<BeanDefinitionData> = try {
override fun perform(): List<BeanDefinitionData> = try {
SpringAnalyzerProcess.createBlocking(classpath).use {
it.getBeanDefinitions(settings)
}.beanDefinitions
Expand Down
19 changes: 0 additions & 19 deletions utbot-spring-sample/build.gradle

This file was deleted.

45 changes: 45 additions & 0 deletions utbot-spring-sample/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -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)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.utbot.examples.spring.config.boot;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ExampleSpringBootConfig {
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.utbot.examples.spring.config.pure;

public class ExamplePureSpringService {
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
9 changes: 9 additions & 0 deletions utbot-spring-sample/src/main/resources/xml-spring-config.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">

<beans>

<bean id="xmlService" class="org.utbot.examples.spring.config.xml.ExampleXmlService"/>

</beans>
14 changes: 14 additions & 0 deletions utbot-spring-test/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
configurations {
fetchSpringSampleJar
}

dependencies {
testImplementation(project(":utbot-framework"))
testImplementation(project(":utbot-spring-framework"))
Expand All @@ -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 {
Expand All @@ -30,3 +38,9 @@ test {
jvmArgs '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9009'
}
}

processTestResources {
from(configurations.fetchSpringSampleJar) {
into "lib"
}
}
Loading

0 comments on commit 20ee749

Please sign in to comment.