From 09386dc37365568602fbac8892828f0097d79193 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Tue, 25 Jul 2023 15:22:27 +0300 Subject: [PATCH 01/19] Use controllers via `MockMvc` in concrete execution and update result models --- .../plugin/api/util/SpringModelUtils.kt | 349 ++++++++++++++++++ .../instrumentation/Instrumentation.kt | 1 + .../SpringUtExecutionInstrumentation.kt | 52 ++- .../instrumentation/rd/InstrumentedProcess.kt | 5 +- .../utbot/spring/api/MockMvcResponseData.kt | 11 + .../kotlin/org/utbot/spring/api/SpringApi.kt | 9 + utbot-spring-commons/build.gradle.kts | 2 + .../kotlin/org/utbot/spring/SpringApiImpl.kt | 4 + .../DummySpringBootIntegrationTestClass.kt | 8 + .../spring/provider/SpringBootApiProvider.kt | 11 +- .../org/utbot/spring/utils/DependencyUtils.kt | 8 + .../org/utbot/spring/utils/MockMvcUtils.kt | 17 + 12 files changed, 471 insertions(+), 6 deletions(-) create mode 100644 utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/MockMvcResponseData.kt create mode 100644 utbot-spring-commons/src/main/kotlin/org/utbot/spring/utils/MockMvcUtils.kt diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt index 5da925bc3b..3c07bde60d 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt @@ -1,11 +1,14 @@ package org.utbot.framework.plugin.api.util +import org.utbot.common.tryLoadClass import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.MethodId import org.utbot.framework.plugin.api.SpringRepositoryId +import org.utbot.framework.plugin.api.UtArrayModel import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtExecutableCallModel import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtNullModel import org.utbot.framework.plugin.api.UtPrimitiveModel import org.utbot.framework.plugin.api.UtSpringContextModel @@ -93,4 +96,350 @@ object SpringModelUtils { } else { null } + + ///region spring-web + private val requestMappingClassId = ClassId("org.springframework.web.bind.annotation.RequestMapping") + private val pathVariableClassId = ClassId("org.springframework.web.bind.annotation.PathVariable") + private val requestBodyClassId = ClassId("org.springframework.web.bind.annotation.RequestBody") + private val uriComponentsBuilderClassId = ClassId("org.springframework.web.util.UriComponentsBuilder") + private val mediaTypeClassId = ClassId("org.springframework.http.MediaType") + private val mockHttpServletResponseClassId = ClassId("org.springframework.mock.web.MockHttpServletResponse") + + private val mockMvcRequestBuildersClassId = ClassId("org.springframework.test.web.servlet.request.MockMvcRequestBuilders") + private val requestBuilderClassId = ClassId("org.springframework.test.web.servlet.RequestBuilder") + private val resultActionsClassId = ClassId("org.springframework.test.web.servlet.ResultActions") + private val mockMvcClassId = ClassId("org.springframework.test.web.servlet.MockMvc") + private val mvcResultClassId = ClassId("org.springframework.test.web.servlet.MvcResult") + private val mockHttpServletRequestBuilderClassId = ClassId("org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder") + + private val objectMapperClassId = ClassId("com.fasterxml.jackson.databind.ObjectMapper") + + fun createMockMvcModel(idGenerator: () -> Int) = + createBeanModel("mockMvc", idGenerator(), mockMvcClassId) + + fun createGetMockMvcResponseModel(requestBuilderModel: UtModel, idGenerator: () -> Int): UtModel { + val mockMvcModel = createMockMvcModel(idGenerator) + + val performModel = UtAssembleModel( + id = idGenerator(), + classId = resultActionsClassId, + modelName = "perform", + instantiationCall = UtExecutableCallModel( + instance = mockMvcModel, + executable = MethodId( + classId = mockMvcClassId, + name = "perform", + parameters = listOf(requestBuilderClassId), + returnType = resultActionsClassId + ), + params = listOf(requestBuilderModel) + ) + ) + // TODO add `andDo(print())` + val andReturnModel = UtAssembleModel( + id = idGenerator(), + classId = mvcResultClassId, + modelName = "andReturn", + instantiationCall = UtExecutableCallModel( + instance = performModel, + executable = MethodId( + classId = mvcResultClassId, + name = "andReturn", + parameters = listOf(), + returnType = mvcResultClassId + ), + params = listOf() + ) + ) + return UtAssembleModel( + id = idGenerator(), + classId = mockHttpServletResponseClassId, + modelName = "getResponse", + instantiationCall = UtExecutableCallModel( + instance = andReturnModel, + executable = MethodId( + classId = mvcResultClassId, + name = "getResponse", + parameters = listOf(), + returnType = mockHttpServletResponseClassId + ), + params = listOf() + ) + ) + } + + fun createRequestBuilderModelOrNull(methodId: MethodId, arguments: List, idGenerator: () -> Int): UtModel? { + check(methodId.parameters.size == arguments.size) + + if (methodId.isStatic) return null + + val requestMappingAnnotation = getRequestMappingAnnotationOrNull(methodId) ?: return null + val requestMethod = getRequestMethodOrNull(requestMappingAnnotation) ?: return null + + @Suppress("UNCHECKED_CAST") + val classRequestMappingAnnotation = methodId.classId.jClass.getAnnotation(requestMappingClassId.jClass as Class) + val cassRequestPath = getRequestPathOrNull(classRequestMappingAnnotation).orEmpty() + + val requestPath = cassRequestPath + (getRequestPathOrNull(requestMappingAnnotation) ?: return null) + + val pathVariablesModel = createPathVariablesModel(methodId, arguments, idGenerator) + + val urlTemplateModel = createUrlTemplateModel(pathVariablesModel, requestPath, idGenerator) + + val requestBuilderModel = UtAssembleModel( + id = idGenerator(), + classId = mockHttpServletRequestBuilderClassId, + modelName = "requestBuilder", + instantiationCall = UtExecutableCallModel( + instance = null, + executable = requestMethod.requestBuilderMethodId, + params = listOf( + urlTemplateModel, + UtArrayModel( + id = idGenerator(), + classId = objectArrayClassId, + length = 0, + constModel = UtNullModel(objectClassId), + stores = mutableMapOf() + ) + ) + ) + ) + + // TODO support @RequestParam, @RequestHeader, @CookieValue, @RequestAttribute + return addContentToRequestBuilderModel(methodId, arguments, requestBuilderModel, idGenerator) + } + + private fun addContentToRequestBuilderModel( + methodId: MethodId, + arguments: List, + requestBuilderModel: UtAssembleModel, + idGenerator: () -> Int + ): UtAssembleModel? { + @Suppress("NAME_SHADOWING") + var requestBuilderModel = requestBuilderModel + methodId.method.parameters.zip(arguments).forEach { (param, arg) -> + @Suppress("UNCHECKED_CAST") + param.getAnnotation(requestBodyClassId.jClass as Class) ?: return@forEach + + // TODO filter out `null` and `Optional.empty()` values of `arg` + val mediaTypeModel = UtAssembleModel( + id = idGenerator(), + classId = mediaTypeClassId, + modelName = "mediaType", + instantiationCall = UtExecutableCallModel( + instance = null, + executable = MethodId( + classId = mediaTypeClassId, + name = "valueOf", + returnType = mediaTypeClassId, + parameters = listOf(stringClassId) + ), + // TODO detect actual media type ("application/json" is very common default) + params = listOf(UtPrimitiveModel("application/json")) + ), + ) + requestBuilderModel = UtAssembleModel( + id = idGenerator(), + classId = mockHttpServletRequestBuilderClassId, + modelName = "requestBuilder", + instantiationCall = UtExecutableCallModel( + instance = requestBuilderModel, + executable = MethodId( + classId = mockHttpServletRequestBuilderClassId, + name = "contentType", + returnType = mockHttpServletRequestBuilderClassId, + parameters = listOf(mediaTypeClassId) + ), + params = listOf( + mediaTypeModel + ) + ) + ) + val content = UtAssembleModel( + id = idGenerator(), + classId = stringClassId, + modelName = "content", + instantiationCall = UtExecutableCallModel( + instance = + // TODO support libraries other than Jackson + if (utContext.classLoader.tryLoadClass(objectMapperClassId.name) == null) + return@addContentToRequestBuilderModel null + // TODO `getBean(ObjectMapper.class)`, name may change depending on Spring version + else createBeanModel("jacksonObjectMapper", idGenerator(), objectMapperClassId), + executable = MethodId( + classId = objectMapperClassId, + name = "writeValueAsString", + returnType = stringClassId, + parameters = listOf(objectClassId) + ), + params = listOf(arg) + ) + ) + requestBuilderModel = UtAssembleModel( + id = idGenerator(), + classId = mockHttpServletRequestBuilderClassId, + modelName = "requestBuilder", + instantiationCall = UtExecutableCallModel( + instance = requestBuilderModel, + executable = MethodId( + classId = mockHttpServletRequestBuilderClassId, + name = "content", + returnType = mockHttpServletRequestBuilderClassId, + parameters = listOf(stringClassId) + ), + params = listOf(content) + ) + ) + } + return requestBuilderModel + } + + private fun createPathVariablesModel( + methodId: MethodId, + arguments: List, + idGenerator: () -> Int + ): UtAssembleModel { + val pathVariables = mutableMapOf() + + methodId.method.parameters.zip(arguments).forEach { (param, arg) -> + @Suppress("UNCHECKED_CAST") val pathVariableAnnotation = + param.getAnnotation(pathVariableClassId.jClass as Class) ?: return@forEach + val name = (pathVariableClassId.jClass.getMethod("name").invoke(pathVariableAnnotation) as? String).orEmpty() + .ifEmpty { pathVariableClassId.jClass.getMethod("value").invoke(pathVariableAnnotation) as? String }.orEmpty() + .ifEmpty { param.name } + pathVariables[name] = arg + } + + // TODO filter out `null` and `Optional.empty()` values of `arg` + return UtAssembleModel( + id = idGenerator(), + classId = Map::class.java.id, + modelName = "pathVariables", + instantiationCall = UtExecutableCallModel( + instance = null, + executable = HashMap::class.java.getConstructor().executableId, + params = emptyList() + ), + modificationsChainProvider = { + pathVariables.map { (name, value) -> + UtExecutableCallModel( + instance = this, + executable = Map::class.java.getMethod( + "put", + Object::class.java, + Object::class.java + ).executableId, + params = listOf(UtPrimitiveModel(name), value) + ) + } + } + ) + } + + private fun createUrlTemplateModel( + pathVariablesModel: UtAssembleModel, + requestPath: String, + idGenerator: () -> Int + ): UtModel { + val requestPathModel = UtPrimitiveModel(requestPath) + return if (pathVariablesModel.modificationsChain.isEmpty()) requestPathModel + else { + val uriBuilderFromPath = UtAssembleModel( + id = idGenerator(), + classId = uriComponentsBuilderClassId, + modelName = "uriBuilderFromPath", + instantiationCall = UtExecutableCallModel( + instance = null, + executable = MethodId( + classId = uriComponentsBuilderClassId, + name = "fromPath", + parameters = listOf(stringClassId), + returnType = uriComponentsBuilderClassId + ), + params = listOf(requestPathModel), + ) + ) + val uriBuilderWithPathVariables = UtAssembleModel( + id = idGenerator(), + classId = uriComponentsBuilderClassId, + modelName = "uriBuilderWithPathVariables", + instantiationCall = UtExecutableCallModel( + instance = uriBuilderFromPath, + executable = MethodId( + classId = uriComponentsBuilderClassId, + name = "uriVariables", + parameters = listOf(Map::class.java.id), + returnType = uriComponentsBuilderClassId + ), + params = listOf(pathVariablesModel), + ) + ) + UtAssembleModel( + id = idGenerator(), + classId = stringClassId, + modelName = "uriString", + instantiationCall = UtExecutableCallModel( + instance = uriBuilderWithPathVariables, + executable = MethodId( + classId = uriComponentsBuilderClassId, + name = "toUriString", + parameters = emptyList(), + returnType = stringClassId + ), + params = emptyList(), + ) + ) + } + } + + // TODO handle multiple annotations on one method + private fun getRequestMappingAnnotationOrNull(methodId: MethodId): Annotation? = + methodId.method.annotations + .firstOrNull { it.annotationClass.id in UtRequestMethod.annotationClassIds } + + // TODO support placeholders (e.g. "/${profile_path}"). + private fun getRequestPathOrNull(requestMappingAnnotation: Annotation): String? = + ((requestMappingAnnotation.annotationClass.java.getMethod("path") + .invoke(requestMappingAnnotation) as Array<*>) + .getOrNull(0) ?: + (requestMappingAnnotation.annotationClass.java.getMethod("value") // TODO separate support for @AliasFor + .invoke(requestMappingAnnotation) as Array<*>) + .getOrNull(0)) as? String // TODO support multiple paths + + private fun getRequestMethodOrNull(requestMappingAnnotation: Annotation): UtRequestMethod? = + UtRequestMethod.values().firstOrNull { requestMappingAnnotation.annotationClass.id == it.annotationClassId } ?: + (requestMappingClassId.jClass.getMethod("method").invoke(requestMappingAnnotation) as Array<*>) + .getOrNull(0)?.let { + UtRequestMethod.valueOf(it.toString()) + } + + private enum class UtRequestMethod { + GET, + HEAD, + POST, + PUT, + PATCH, + DELETE, + OPTIONS, + TRACE; + + val annotationClassId get() = ClassId( + "org.springframework.web.bind.annotation.${name.lowercase().capitalize()}Mapping" + ) + + val requestBuilderMethodId = MethodId( + classId = mockMvcRequestBuildersClassId, + name = name.lowercase(), + returnType = mockHttpServletRequestBuilderClassId, + parameters = listOf(stringClassId, objectArrayClassId) + ) + + companion object { + val annotationClassIds = values().map { it.annotationClassId } + + requestMappingClassId + } + } + + ///endregion } \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt index 4adcb58992..e2c604b38b 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt @@ -29,6 +29,7 @@ interface Instrumentation : ClassFileTransformer interface Factory> { val additionalRuntimeClasspath: Set get() = emptySet() + val forceDisableSandbox: Boolean get() = false fun create(): TInstrumentation } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt index b30c60d931..3a6c2c3af2 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt @@ -10,8 +10,15 @@ import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.ConcreteContextLoadingResult import org.utbot.framework.plugin.api.SpringRepositoryId import org.utbot.framework.plugin.api.SpringSettings.* +import org.utbot.framework.plugin.api.util.SpringModelUtils +import org.utbot.framework.plugin.api.util.executableId +import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.plugin.api.util.isStatic import org.utbot.framework.plugin.api.util.jClass +import org.utbot.framework.plugin.api.util.method +import org.utbot.framework.plugin.api.util.signature import org.utbot.instrumentation.instrumentation.ArgumentList +import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionData import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation import org.utbot.instrumentation.instrumentation.execution.context.InstrumentationContext @@ -63,8 +70,42 @@ class SpringUtExecutionInstrumentation( parameters: Any?, phasesWrapper: PhasesController.(invokeBasePhases: () -> UtConcreteExecutionResult) -> UtConcreteExecutionResult ): UtConcreteExecutionResult { + if (parameters !is UtConcreteExecutionData) + throw IllegalArgumentException("Argument parameters must be of type UtConcreteExecutionData, but was: ${parameters?.javaClass}") + getRelevantBeans(clazz).forEach { beanName -> springApi.resetBean(beanName) } + @Suppress("NAME_SHADOWING") var clazz = clazz + @Suppress("NAME_SHADOWING") var methodSignature = methodSignature + @Suppress("NAME_SHADOWING") var parameters: UtConcreteExecutionData = parameters + clazz.id.allMethods.firstOrNull { it.signature == methodSignature }?.method?.executableId?.let { methodId -> + if (!methodId.isStatic) { + + // TODO try coming up with better approach to generating ids + var lastId = 1378139720 // random int (around 0.642 * Int.MAX_VALUE) + val idGenerator = { lastId++ } + + SpringModelUtils.createRequestBuilderModelOrNull( + methodId, + parameters.stateBefore.parameters, + idGenerator + )?.let { requestBuilderModel -> + parameters = parameters.copy(stateBefore = parameters.stateBefore.copy( + thisInstance = null, + parameters = listOf( + parameters.stateBefore.thisInstance!!, + SpringModelUtils.createMockMvcModel(idGenerator), + requestBuilderModel, + ) + )) + + val getMockMvcResponseMethod = springApi.getMockMvcResponseDataMethod + clazz = getMockMvcResponseMethod.declaringClass + methodSignature = getMockMvcResponseMethod.signature + } + } + } + return delegateInstrumentation.invoke(clazz, methodSignature, arguments, parameters) { invokeBasePhases -> phasesWrapper { // NB! beforeTestMethod() and afterTestMethod() are intentionally called inside phases, @@ -115,17 +156,21 @@ class SpringUtExecutionInstrumentation( classBeingRedefined: Class<*>?, protectionDomain: ProtectionDomain, classfileBuffer: ByteArray - ): ByteArray? = + ): ByteArray? { + // always transform `MockMvc` to avoid empty coverage when testing controllers + val isMockMvc = className == "org/springframework/test/web/servlet/MockMvc" + // we do not transform Spring classes as it takes too much time // maybe we should still transform classes related to data validation // (e.g. from packages "javax/persistence" and "jakarta/persistence"), // since traces from such classes can be particularly useful for feedback to fuzzer - if (userSourcesClassLoader.hasOnClasspath(className.replace("/", "."))) { + return if (isMockMvc || userSourcesClassLoader.hasOnClasspath(className.replace("/", "."))) { delegateInstrumentation.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer) } else { null } + } class Factory( private val delegateInstrumentationFactory: UtExecutionInstrumentation.Factory<*>, @@ -140,6 +185,9 @@ class SpringUtExecutionInstrumentation( targetDirectoryName = "spring-commons" ).path + override val forceDisableSandbox: Boolean + get() = true + override fun create(instrumentationContext: InstrumentationContext): SpringUtExecutionInstrumentation = SpringUtExecutionInstrumentation( instrumentationContext, diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt index baef39ac5f..86e6cdbc2f 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt @@ -11,7 +11,6 @@ import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.services.WorkingDirService import org.utbot.framework.process.AbstractRDProcessCompanion import org.utbot.framework.process.kryo.KryoHelper -import org.utbot.instrumentation.agent.DynamicClassTransformer import org.utbot.instrumentation.instrumentation.Instrumentation import org.utbot.instrumentation.process.DISABLE_SANDBOX_OPTION import org.utbot.instrumentation.process.generated.AddPathsParams @@ -95,7 +94,9 @@ class InstrumentedProcess private constructor( val rdProcess: ProcessWithRdServer = startUtProcessWithRdServer( lifetime = lifetime ) { port -> - val cmd = obtainProcessCommandLine(port) + val cmd = obtainProcessCommandLine(port) + listOfNotNull( + DISABLE_SANDBOX_OPTION.takeIf { instrumentationFactory.forceDisableSandbox } + ) logger.debug { "Starting instrumented process: $cmd" } val directory = WorkingDirService.provide().toFile() val processBuilder = ProcessBuilder(cmd) diff --git a/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/MockMvcResponseData.kt b/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/MockMvcResponseData.kt new file mode 100644 index 0000000000..cf536b0ea0 --- /dev/null +++ b/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/MockMvcResponseData.kt @@ -0,0 +1,11 @@ +package org.utbot.spring.api + +/** + * Zero argument methods of `MockHttpServletResponse` that extract all data out of + * `MockHttpServletResponse` instance, that is needed for assertion generation + * + * @see SpringApi.getMockMvcResponseDataMethod + */ +val relevantMockMvcResponseDataGetterNames = listOf( + "getStatus", "getContentAsString", "getErrorMessage" +) diff --git a/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/SpringApi.kt b/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/SpringApi.kt index 3457204c5e..f5494128f3 100644 --- a/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/SpringApi.kt +++ b/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/SpringApi.kt @@ -1,5 +1,6 @@ package org.utbot.spring.api +import java.lang.reflect.Method import java.net.URLClassLoader //TODO: `userSourcesClassLoader` must not be passed as a method argument, requires refactoring @@ -30,6 +31,14 @@ interface SpringApi { * because transactions are bound to threads */ fun afterTestMethod() + + /** + * Returns static method that should be defined like this: + * `fun getMockMvcResponse(controllerInstance: Any, mockMvc: MockMvc, requestBuilder: RequestBuilder): Map` + * + * @see relevantMockMvcResponseDataGetterNames + */ + val getMockMvcResponseDataMethod: Method } data class RepositoryDescription( diff --git a/utbot-spring-commons/build.gradle.kts b/utbot-spring-commons/build.gradle.kts index 61131d1a9c..d6e5a9d5e5 100644 --- a/utbot-spring-commons/build.gradle.kts +++ b/utbot-spring-commons/build.gradle.kts @@ -23,7 +23,9 @@ dependencies { compileOnly("org.springframework.boot:spring-boot-test-autoconfigure:$springBootVersion") compileOnly("org.springframework:spring-test:$springVersion") compileOnly("org.springframework:spring-tx:$springVersion") + compileOnly("org.springframework:spring-web:$springVersion") compileOnly("org.springframework.data:spring-data-commons:$springBootVersion") + implementation("com.jetbrains.rd:rd-core:$rdVersion") { exclude(group = "org.slf4j", module = "slf4j-api") } } diff --git a/utbot-spring-commons/src/main/kotlin/org/utbot/spring/SpringApiImpl.kt b/utbot-spring-commons/src/main/kotlin/org/utbot/spring/SpringApiImpl.kt index 687654cbae..15c735a6c0 100644 --- a/utbot-spring-commons/src/main/kotlin/org/utbot/spring/SpringApiImpl.kt +++ b/utbot-spring-commons/src/main/kotlin/org/utbot/spring/SpringApiImpl.kt @@ -17,6 +17,7 @@ import org.utbot.spring.api.provider.InstantiationSettings import org.utbot.spring.dummy.DummySpringIntegrationTestClass import org.utbot.spring.utils.DependencyUtils.isSpringDataOnClasspath import org.utbot.spring.utils.RepositoryUtils +import org.utbot.spring.utils.getMockMvcResponseData import java.lang.reflect.Method import java.net.URLClassLoader import kotlin.reflect.jvm.javaMethod @@ -157,6 +158,9 @@ class SpringApiImpl( isInsideTestMethod = false } + override val getMockMvcResponseDataMethod: Method + get() = ::getMockMvcResponseData.javaMethod!! + private fun describesRepository(bean: Any): Boolean = try { bean is CrudRepository<*, *> diff --git a/utbot-spring-commons/src/main/kotlin/org/utbot/spring/dummy/DummySpringBootIntegrationTestClass.kt b/utbot-spring-commons/src/main/kotlin/org/utbot/spring/dummy/DummySpringBootIntegrationTestClass.kt index 0efc7128b0..0337e5a835 100644 --- a/utbot-spring-commons/src/main/kotlin/org/utbot/spring/dummy/DummySpringBootIntegrationTestClass.kt +++ b/utbot-spring-commons/src/main/kotlin/org/utbot/spring/dummy/DummySpringBootIntegrationTestClass.kt @@ -1,6 +1,7 @@ package org.utbot.spring.dummy import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.context.SpringBootTestContextBootstrapper import org.springframework.test.context.BootstrapWith @@ -11,3 +12,10 @@ open class DummySpringBootIntegrationTestClass : DummySpringIntegrationTestClass @AutoConfigureTestDatabase class DummySpringBootIntegrationTestClassAutoconfigTestDB : DummySpringBootIntegrationTestClass() + +@AutoConfigureMockMvc +class DummySpringBootIntegrationTestClassAutoconfigMockMvc : DummySpringBootIntegrationTestClass() + +@AutoConfigureMockMvc +@AutoConfigureTestDatabase +class DummySpringBootIntegrationTestClassAutoconfigMockMvcAndTestDB : DummySpringBootIntegrationTestClass() diff --git a/utbot-spring-commons/src/main/kotlin/org/utbot/spring/provider/SpringBootApiProvider.kt b/utbot-spring-commons/src/main/kotlin/org/utbot/spring/provider/SpringBootApiProvider.kt index 42166664c5..ed28bcac4c 100644 --- a/utbot-spring-commons/src/main/kotlin/org/utbot/spring/provider/SpringBootApiProvider.kt +++ b/utbot-spring-commons/src/main/kotlin/org/utbot/spring/provider/SpringBootApiProvider.kt @@ -3,9 +3,12 @@ package org.utbot.spring.provider import org.utbot.spring.api.provider.InstantiationSettings import org.utbot.spring.dummy.DummySpringBootIntegrationTestClass import org.utbot.spring.SpringApiImpl +import org.utbot.spring.dummy.DummySpringBootIntegrationTestClassAutoconfigMockMvc +import org.utbot.spring.dummy.DummySpringBootIntegrationTestClassAutoconfigMockMvcAndTestDB import org.utbot.spring.dummy.DummySpringBootIntegrationTestClassAutoconfigTestDB import org.utbot.spring.utils.DependencyUtils.isSpringBootTestOnClasspath import org.utbot.spring.utils.DependencyUtils.isSpringDataOnClasspath +import org.utbot.spring.utils.DependencyUtils.isSpringWebOnClasspath class SpringBootApiProvider : SpringApiProvider { @@ -14,7 +17,11 @@ class SpringBootApiProvider : SpringApiProvider { override fun provideAPI(instantiationSettings: InstantiationSettings) = SpringApiImpl( instantiationSettings, - if (isSpringDataOnClasspath) DummySpringBootIntegrationTestClassAutoconfigTestDB::class.java - else DummySpringBootIntegrationTestClass::class.java + when { + isSpringDataOnClasspath && isSpringWebOnClasspath -> DummySpringBootIntegrationTestClassAutoconfigMockMvcAndTestDB::class + isSpringDataOnClasspath && !isSpringWebOnClasspath -> DummySpringBootIntegrationTestClassAutoconfigTestDB::class + !isSpringDataOnClasspath && isSpringWebOnClasspath -> DummySpringBootIntegrationTestClassAutoconfigMockMvc::class + else -> DummySpringBootIntegrationTestClass::class + }.java ) } \ No newline at end of file diff --git a/utbot-spring-commons/src/main/kotlin/org/utbot/spring/utils/DependencyUtils.kt b/utbot-spring-commons/src/main/kotlin/org/utbot/spring/utils/DependencyUtils.kt index 4be1dd4612..68eb45e2e4 100644 --- a/utbot-spring-commons/src/main/kotlin/org/utbot/spring/utils/DependencyUtils.kt +++ b/utbot-spring-commons/src/main/kotlin/org/utbot/spring/utils/DependencyUtils.kt @@ -2,6 +2,7 @@ package org.utbot.spring.utils import org.springframework.boot.test.context.SpringBootTestContextBootstrapper import org.springframework.data.repository.CrudRepository +import org.springframework.web.bind.annotation.RequestMapping object DependencyUtils { val isSpringDataOnClasspath = try { @@ -11,6 +12,13 @@ object DependencyUtils { false } + val isSpringWebOnClasspath = try { + RequestMapping::class.java.name + true + } catch (e: Throwable) { + false + } + val isSpringBootTestOnClasspath = try { SpringBootTestContextBootstrapper::class.java.name true diff --git a/utbot-spring-commons/src/main/kotlin/org/utbot/spring/utils/MockMvcUtils.kt b/utbot-spring-commons/src/main/kotlin/org/utbot/spring/utils/MockMvcUtils.kt new file mode 100644 index 0000000000..7265c1c0d9 --- /dev/null +++ b/utbot-spring-commons/src/main/kotlin/org/utbot/spring/utils/MockMvcUtils.kt @@ -0,0 +1,17 @@ +package org.utbot.spring.utils + +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.RequestBuilder +import org.utbot.spring.api.relevantMockMvcResponseDataGetterNames + +fun getMockMvcResponseData( + // we ask for it, to force its initialization during value construction phase + @Suppress("UNUSED_PARAMETER") controllerInstance: Any, + mockMvc: MockMvc, + requestBuilder: RequestBuilder +): Map { + val response = mockMvc.perform(requestBuilder).andReturn().response + return relevantMockMvcResponseDataGetterNames.associateWith { getterName -> + response::class.java.getMethod(getterName).invoke(response) + } +} \ No newline at end of file From 3ad9a6406ddc3ce60ac2f7314292dbaa0fc79795 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Wed, 26 Jul 2023 13:21:29 +0300 Subject: [PATCH 02/19] Make `createGetMockMvcResponseModel` work when there's no `@RequestMapping` on controller class --- .../org/utbot/framework/plugin/api/util/SpringModelUtils.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt index 3c07bde60d..0a8802f721 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt @@ -177,8 +177,9 @@ object SpringModelUtils { val requestMethod = getRequestMethodOrNull(requestMappingAnnotation) ?: return null @Suppress("UNCHECKED_CAST") - val classRequestMappingAnnotation = methodId.classId.jClass.getAnnotation(requestMappingClassId.jClass as Class) - val cassRequestPath = getRequestPathOrNull(classRequestMappingAnnotation).orEmpty() + val classRequestMappingAnnotation: Annotation? = + methodId.classId.jClass.getAnnotation(requestMappingClassId.jClass as Class) + val cassRequestPath = classRequestMappingAnnotation?.let { getRequestPathOrNull(it) }.orEmpty() val requestPath = cassRequestPath + (getRequestPathOrNull(requestMappingAnnotation) ?: return null) From 488de3c7a534326e43835430a9a1a0ff73226b0f Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Wed, 26 Jul 2023 18:26:43 +0300 Subject: [PATCH 03/19] Introduce `executableToCall` --- .../org/utbot/framework/plugin/api/Api.kt | 32 +++++++++++++-- .../org/utbot/engine/UtBotSymbolicEngine.kt | 4 +- .../codegen/domain/context/CgContext.kt | 18 ++++++--- .../access/CgCallableAccessManager.kt | 4 +- .../CgAbstractSpringTestClassConstructor.kt | 4 +- .../codegen/tree/CgMethodConstructor.kt | 40 +++++++++---------- .../tree/CgSimpleTestClassConstructor.kt | 4 +- .../codegen/tree/ConstructorUtils.kt | 2 +- .../util/UtConcreteExecutionResultUtils.kt | 8 ++-- .../SimpleUtExecutionInstrumentation.kt | 19 ++++++++- .../src/main/kotlin/api/JsTestGenerator.kt | 2 +- .../constructor/tree/JsCgMethodConstructor.kt | 4 +- .../kotlin/org/utbot/python/PythonEngine.kt | 12 +++--- .../tree/PythonCgMethodConstructor.kt | 6 +-- 14 files changed, 101 insertions(+), 58 deletions(-) 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 a3fa74b66f..e76cea5178 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 @@ -287,11 +287,33 @@ class UtFailedExecution( } } +/** + * @property executableToCall executable that is called in the test body and whose result is used in asserts as `actual`. + * + * Most often [executableToCall] is just method under test, but it may be changed to different executable if more + * appropriate way of calling specific method is found (for example, Spring controller methods are called via `MockMvc`). + * + * `null` value of [executableToCall] indicates that method under test should be called in the test body. + */ open class EnvironmentModels( val thisInstance: UtModel?, val parameters: List, - val statics: Map + val statics: Map, + val executableToCall: ExecutableId?, ) { + @Deprecated("Now `executableToCall` also need to be passed to `EnvironmentModels` constructor " + + "(see more details in `EnvironmentModels` class documentation)", level = DeprecationLevel.ERROR) + constructor( + thisInstance: UtModel?, + parameters: List, + statics: Map, + ) : this( + thisInstance = thisInstance, + parameters = parameters, + statics = statics, + executableToCall = null, + ) + override fun toString() = buildString { append("this=$thisInstance") appendOptional("parameters", parameters) @@ -305,8 +327,9 @@ open class EnvironmentModels( fun copy( thisInstance: UtModel? = this.thisInstance, parameters: List = this.parameters, - statics: Map = this.statics - ) = EnvironmentModels(thisInstance, parameters, statics) + statics: Map = this.statics, + executableToCall: ExecutableId? = this.executableToCall, + ) = EnvironmentModels(thisInstance, parameters, statics, executableToCall) } /** @@ -315,7 +338,8 @@ open class EnvironmentModels( object MissingState : EnvironmentModels( thisInstance = null, parameters = emptyList(), - statics = emptyMap() + statics = emptyMap(), + executableToCall = null, ) /** diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index 89d7ea8acf..ff50c0e898 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -474,7 +474,7 @@ class UtBotSymbolicEngine( return@runJavaFuzzing BaseFeedback(Trie.emptyNode(), Control.PASS) } - val stateBefore = EnvironmentModels(thisInstance?.model, values.map { it.model }, mapOf()) + val stateBefore = EnvironmentModels(thisInstance?.model, values.map { it.model }, mapOf(), methodUnderTest) val concreteExecutionResult: UtConcreteExecutionResult? = try { val timeoutMillis = min(UtSettings.concreteExecutionDefaultTimeoutInInstrumentedProcessMillis, diff) @@ -772,7 +772,7 @@ private fun ResolvedModels.constructStateForMethod(methodUnderTest: ExecutableId methodUnderTest.isConstructor -> null to parameters.drop(1) else -> parameters.first() to parameters.drop(1) } - return EnvironmentModels(thisInstanceBefore, paramsBefore, statics) + return EnvironmentModels(thisInstanceBefore, paramsBefore, statics, methodUnderTest) } private suspend fun ConcreteExecutor>.executeConcretely( diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt index 4aa883bf97..3791d3428a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt @@ -45,7 +45,7 @@ import org.utbot.framework.plugin.api.util.jClass * Although, some of the properties are declared as 'var' so that * they can be reassigned as well as modified * - * For example, [outerMostTestClass] and [currentExecutable] can be reassigned + * For example, [outerMostTestClass] and [currentExecution] can be reassigned * when we start generating another method or test class * * [existingVariableNames] is a 'var' property @@ -70,7 +70,13 @@ interface CgContextOwner { val utilMethodProvider: UtilMethodProvider // current executable under test - var currentExecutable: ExecutableId? + // NOTE: may differ from `executableToCall` + var currentExecutableUnderTest: ExecutableId? + + // executable that is called in the current test method body and whose result is used in asserts as `actual` + // NOTE: may differ from `executableUnderTest` + val currentExecutableToCall: ExecutableId? get() = + currentExecution?.stateBefore?.executableToCall ?: currentExecutableUnderTest // ClassInfo for the outermost class currently being generated val outerMostTestClassContext: TestClassContext @@ -259,8 +265,8 @@ interface CgContextOwner { currentBlock = currentBlock.add(it) } - fun updateCurrentExecutable(executableId: ExecutableId) { - currentExecutable = executableId + fun updateExecutableUnderTest(executableId: ExecutableId) { + currentExecutableUnderTest = executableId } fun withTestSetIdScope(testSetId: Int, block: () -> R): R { @@ -461,7 +467,7 @@ class CgContext( override val classUnderTest: ClassId, override val projectType: ProjectType, val generateUtilClassFile: Boolean = false, - override var currentExecutable: ExecutableId? = null, + override var currentExecutableUnderTest: ExecutableId? = null, override val collectedExceptions: MutableSet = mutableSetOf(), override val collectedMethodAnnotations: MutableSet = mutableSetOf(), override val collectedImports: MutableSet = mutableSetOf(), @@ -628,7 +634,7 @@ class CgContext( classUnderTest = this.classUnderTest, projectType = this.projectType, generateUtilClassFile = this.generateUtilClassFile, - currentExecutable = this.currentExecutable, + currentExecutableUnderTest = this.currentExecutableUnderTest, collectedExceptions =this.collectedExceptions, collectedMethodAnnotations = this.collectedMethodAnnotations, collectedImports = this.collectedImports, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/access/CgCallableAccessManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/access/CgCallableAccessManager.kt index 96006a2033..cb29b17f6f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/access/CgCallableAccessManager.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/access/CgCallableAccessManager.kt @@ -160,13 +160,13 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA addExceptionIfNeeded(Throwable::class.id) } - val methodIsUnderTestAndThrowsExplicitly = methodId == currentExecutable + val methodIsToCallAndThrowsExplicitly = methodId == currentExecutableToCall && currentExecution?.result is UtExplicitlyThrownException val frameworkSupportsAssertThrows = testFramework == Junit5 || testFramework == TestNg //If explicit exception is wrapped with assertThrows, // no "throws" in test method signature is required. - if (methodIsUnderTestAndThrowsExplicitly && frameworkSupportsAssertThrows) { + if (methodIsToCallAndThrowsExplicitly && frameworkSupportsAssertThrows) { return } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractSpringTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractSpringTestClassConstructor.kt index 81ce6e6feb..d3135d297a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractSpringTestClassConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractSpringTestClassConstructor.kt @@ -40,11 +40,11 @@ abstract class CgAbstractSpringTestClassConstructor(context: CgContext) : constructAdditionalTestMethods()?.let { methodRegions += it } for ((testSetIndex, testSet) in testClassModel.methodTestSets.withIndex()) { - updateCurrentExecutable(testSet.executableId) + updateExecutableUnderTest(testSet.executableId) withTestSetIdScope(testSetIndex) { val currentMethodUnderTestRegions = constructTestSet(testSet) ?: return@withTestSetIdScope val executableUnderTestCluster = CgMethodsCluster( - "Test suites for executable $currentExecutable", + "Test suites for executable $currentExecutableUnderTest", currentMethodUnderTestRegions ) methodRegions += executableUnderTestCluster diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt index 71a54f470a..69a47e82ad 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt @@ -303,12 +303,11 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte * Generates result assertions for unit tests. */ protected open fun generateResultAssertions() { - when (currentExecutable) { - is ConstructorId -> generateConstructorCall(currentExecutable!!, currentExecution!!) - is BuiltinMethodId -> error("Unexpected BuiltinMethodId $currentExecutable while generating result assertions") + when (val executable = currentExecutableToCall) { + is ConstructorId -> generateConstructorCall(executable, currentExecution!!) + is BuiltinMethodId -> error("Unexpected BuiltinMethodId $executable while generating result assertions") is MethodId -> { emptyLineIfNeeded() - val method = currentExecutable as MethodId val currentExecution = currentExecution!! val executionResult = currentExecution.result @@ -318,8 +317,8 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte methodType = SUCCESSFUL // TODO possible engine bug - void method return type and result model not UtVoidModel - if (resultModel.isUnit() || method.returnType == voidClassId) { - +thisInstance[method](*methodArguments.toTypedArray()) + if (resultModel.isUnit() || executable.returnType == voidClassId) { + +thisInstance[executable](*methodArguments.toTypedArray()) } else { this.resultModel = resultModel val expected = variableConstructor.getOrCreateVariable(resultModel, "expected") @@ -381,7 +380,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte private fun prepareArtificialFailureMessage(executionResult: UtExecutionResult): CgLiteral { when (executionResult) { is UtOverflowFailure -> { - val failureMessage = "Overflow detected in \'${currentExecutable!!.name}\' call" + val failureMessage = "Overflow detected in \'${currentExecutableToCall!!.name}\' call" return CgLiteral(stringClassId, failureMessage) } is UtTaintAnalysisFailure -> { @@ -400,7 +399,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte } return { - with(currentExecutable) { + with(currentExecutableToCall) { when (this) { is MethodId -> thisInstance[this](*methodArguments.toTypedArray()).intercepted() is ConstructorId -> this(*methodArguments.toTypedArray()).intercepted() @@ -411,7 +410,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte } private fun constructStreamConsumingBlock(): () -> Unit { - val executable = currentExecutable + val executable = currentExecutableToCall require((executable is MethodId) && (executable.returnType isSubtypeOf baseStreamClassId)) { "Unexpected non-stream returning executable $executable" @@ -460,8 +459,8 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte } protected fun writeWarningAboutFailureTest(exception: Throwable) { - require(currentExecutable is ExecutableId) - val executableName = "${currentExecutable!!.classId.name}.${currentExecutable!!.name}" + require(currentExecutableToCall is ExecutableId) + val executableName = "${currentExecutableToCall!!.classId.name}.${currentExecutableToCall!!.name}" val warningLine = "This test fails because method [$executableName] produces [$exception]" .lines() @@ -496,16 +495,15 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte private fun generateAssertionsForParameterizedTest() { emptyLineIfNeeded() - when (currentExecutable) { - is ConstructorId -> generateConstructorCall(currentExecutable!!, currentExecution!!) + when (val executable = currentExecutableToCall) { + is ConstructorId -> generateConstructorCall(executable, currentExecution!!) is MethodId -> { - val method = currentExecutable as MethodId val executionResult = currentExecution!!.result executionResult .onSuccess { resultModel -> if (resultModel.isUnit()) { - +thisInstance[method](*methodArguments.toTypedArray()) + +thisInstance[executable](*methodArguments.toTypedArray()) } else { //"generic" expected variable is represented with a wrapper if //actual result is primitive to support cases with exceptions. @@ -522,7 +520,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte if (containsStreamConsumingFailureForParametrizedTests) { constructStreamConsumingBlock().invoke() } else { - thisInstance[method](*methodArguments.toTypedArray()).intercepted() + thisInstance[executable](*methodArguments.toTypedArray()).intercepted() } } } @@ -1271,7 +1269,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte generateDeepEqualsOrNullAssertion(expected.expression, actual) } - private fun generateConstructorCall(currentExecutableId: ExecutableId, currentExecution: UtExecution) { + private fun generateConstructorCall(currentExecutableId: ConstructorId, currentExecution: UtExecution) { // we cannot generate any assertions for constructor testing // but we need to generate a constructor call val constructorCall = currentExecutableId as ConstructorId @@ -1358,12 +1356,12 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte val executionResult = currentExecution!!.result executionResult.onSuccess { resultModel -> - when (val executable = currentExecutable) { + when (val executable = currentExecutableToCall) { is ConstructorId -> { // there is nothing to generate for constructors return } - is BuiltinMethodId -> error("Unexpected BuiltinMethodId $currentExecutable while generating actual result") + is BuiltinMethodId -> error("Unexpected BuiltinMethodId $executable while generating actual result") is MethodId -> { // TODO possible engine bug - void method return type and result model not UtVoidModel if (resultModel.isUnit() || executable.returnType == voidClassId) return @@ -1392,7 +1390,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte } private fun processStreamConsumingException(innerException: Throwable) { - val executable = currentExecutable + val executable = currentExecutableToCall require((executable is MethodId) && (executable.returnType isSubtypeOf baseStreamClassId)) { "Unexpected exception $innerException during stream consuming in non-stream returning executable $executable" @@ -1743,7 +1741,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte arguments += variableConstructor.getOrCreateVariable(model, field.name) } - val method = currentExecutable!! + val method = currentExecutableToCall!! val needsReturnValue = method.returnType != voidClassId val containsFailureExecution = containsFailureExecution(testSet) execution.result diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt index 4903101249..10aa7739d0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt @@ -50,11 +50,11 @@ open class CgSimpleTestClassConstructor(context: CgContext): CgAbstractTestClass } for ((testSetIndex, testSet) in notYetConstructedTestSets.withIndex()) { - updateCurrentExecutable(testSet.executableId) + updateExecutableUnderTest(testSet.executableId) withTestSetIdScope(testSetIndex) { val currentMethodUnderTestRegions = constructTestSet(testSet) ?: return@withTestSetIdScope val executableUnderTestCluster = CgMethodsCluster( - "Test suites for executable $currentExecutable", + "Test suites for executable $currentExecutableUnderTest", currentMethodUnderTestRegions ) methodRegions += executableUnderTestCluster diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/ConstructorUtils.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/ConstructorUtils.kt index fe311bf04c..cf56a49cf3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/ConstructorUtils.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/ConstructorUtils.kt @@ -261,7 +261,7 @@ internal fun CgContextOwner.importIfNeeded(method: MethodId) { method.takeIf { it.isStatic && packageName != testClassPackageName && packageName != "java.lang" } .takeIf { importedStaticMethods.none { it.name == name } } // do not import method under test in order to specify the declaring class directly for its calls - .takeIf { currentExecutable != method } + .takeIf { currentExecutableUnderTest != method } ?.let { importedStaticMethods += method collectedImports += StaticImport(method.classId.canonicalName, method.name) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/util/UtConcreteExecutionResultUtils.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/util/UtConcreteExecutionResultUtils.kt index 9aef5f1f99..3df16701b4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/util/UtConcreteExecutionResultUtils.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/util/UtConcreteExecutionResultUtils.kt @@ -13,10 +13,10 @@ private fun UtConcreteExecutionResult.updateWithAssembleModels( ): UtConcreteExecutionResult { val toAssemble: (UtModel) -> UtModel = { assembledUtModels.getOrDefault(it, it) } - val resolvedStateAfter = if (stateAfter is MissingState) MissingState else EnvironmentModels( - stateAfter.thisInstance?.let { toAssemble(it) }, - stateAfter.parameters.map { toAssemble(it) }, - stateAfter.statics.mapValues { toAssemble(it.value) } + val resolvedStateAfter = if (stateAfter is MissingState) MissingState else stateAfter.copy( + thisInstance = stateAfter.thisInstance?.let { toAssemble(it) }, + parameters = stateAfter.parameters.map { toAssemble(it) }, + statics = stateAfter.statics.mapValues { toAssemble(it.value) }, ) val resolvedResult = (result as? UtExecutionSuccess)?.model?.let { UtExecutionSuccess(toAssemble(it)) } ?: result diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt index 7c21311731..00742b336d 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt @@ -3,6 +3,8 @@ package org.utbot.instrumentation.instrumentation.execution import org.utbot.framework.plugin.api.EnvironmentModels import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.util.executable +import org.utbot.framework.plugin.api.util.signature import org.utbot.framework.plugin.api.util.singleExecutableId import org.utbot.instrumentation.instrumentation.ArgumentList import org.utbot.instrumentation.instrumentation.InvokeInstrumentation @@ -32,6 +34,9 @@ class SimpleUtExecutionInstrumentation( * Ignores [arguments], because concrete arguments will be constructed * from models passed via [parameters]. * + * Ignores [clazz] and [methodSignature] if they can be constructed + * from [parameters] (see [EnvironmentModels.executableToCall]). + * * Argument [parameters] must be of type [UtConcreteExecutionData]. */ override fun invoke( @@ -61,7 +66,12 @@ class SimpleUtExecutionInstrumentation( // invocation val concreteResult = executePhaseInTimeout(invocationPhase) { - invoke(clazz, methodSignature, params.map { it.value }) + val executableToCall = stateBefore.executableToCall?.executable + invoke( + clazz = executableToCall?.declaringClass ?: clazz, + methodSignature = executableToCall?.signature ?: methodSignature, + params = params.map { it.value } + ) } // statistics collection @@ -93,7 +103,12 @@ class SimpleUtExecutionInstrumentation( } else { stateAfterParametersWithThis.first() to stateAfterParametersWithThis.drop(1) } - val stateAfter = EnvironmentModels(stateAfterThis, stateAfterParameters, stateAfterStatics) + val stateAfter = EnvironmentModels( + thisInstance = stateAfterThis, + parameters = stateAfterParameters, + statics = stateAfterStatics, + executableToCall = stateBefore.executableToCall + ) Triple(executionResult, stateAfter, newInstrumentation) } diff --git a/utbot-js/src/main/kotlin/api/JsTestGenerator.kt b/utbot-js/src/main/kotlin/api/JsTestGenerator.kt index 4446f4b52c..4b7be92daf 100644 --- a/utbot-js/src/main/kotlin/api/JsTestGenerator.kt +++ b/utbot-js/src/main/kotlin/api/JsTestGenerator.kt @@ -292,7 +292,7 @@ class JsTestGenerator( null to params } else params[0] to params.drop(1) val initEnv = - EnvironmentModels(thisObject, modelList, mapOf()) + EnvironmentModels(thisObject, modelList, mapOf(), execId) emit( JsValidExecution( JsUtFuzzedExecution( diff --git a/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt b/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt index 3ed9b1dfeb..fbed241d3d 100644 --- a/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt +++ b/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt @@ -60,7 +60,7 @@ class JsCgMethodConstructor(ctx: CgContext) : CgMethodConstructor(ctx) { override fun generateResultAssertions() { emptyLineIfNeeded() val currentExecution = currentExecution!! - val method = currentExecutable as MethodId + val method = currentExecutableToCall as MethodId // build assertions currentExecution.result .onSuccess { result -> @@ -80,7 +80,7 @@ class JsCgMethodConstructor(ctx: CgContext) : CgMethodConstructor(ctx) { private fun processExecutionFailure(execution: UtExecution, exception: Throwable) { val methodInvocationBlock = { - with(currentExecutable) { + with(currentExecutableToCall) { when (this) { is MethodId -> thisInstance[this](*methodArguments.toTypedArray()).intercepted() is ConstructorId -> this(*methodArguments.toTypedArray()).intercepted() diff --git a/utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt b/utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt index 5c00195421..847863995e 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt @@ -107,9 +107,9 @@ class PythonEngine( val coveredInstructions = coveredLinesToInstructions(coveredLines, methodUnderTest) val coverage = Coverage(coveredInstructions) val utFuzzedExecution = PythonUtExecution( - stateInit = EnvironmentModels(beforeThisObject, beforeModelList, emptyMap()), - stateBefore = EnvironmentModels(beforeThisObject, beforeModelList, emptyMap()), - stateAfter = EnvironmentModels(beforeThisObject, beforeModelList, emptyMap()), + stateInit = EnvironmentModels(beforeThisObject, beforeModelList, emptyMap(), executableToCall = null), + stateBefore = EnvironmentModels(beforeThisObject, beforeModelList, emptyMap(), executableToCall = null), + stateAfter = EnvironmentModels(beforeThisObject, beforeModelList, emptyMap(), executableToCall = null), diffIds = emptyList(), result = executionResult, coverage = coverage, @@ -162,9 +162,9 @@ class PythonEngine( val (afterThisObject, afterModelList) = transformModelList(hasThisObject, evaluationResult.stateAfter, evaluationResult.modelListIds) val utFuzzedExecution = PythonUtExecution( - stateInit = EnvironmentModels(thisObject, initModelList, emptyMap()), - stateBefore = EnvironmentModels(beforeThisObject, beforeModelList, emptyMap()), - stateAfter = EnvironmentModels(afterThisObject, afterModelList, emptyMap()), + stateInit = EnvironmentModels(thisObject, initModelList, emptyMap(), executableToCall = null), + stateBefore = EnvironmentModels(beforeThisObject, beforeModelList, emptyMap(), executableToCall = null), + stateAfter = EnvironmentModels(afterThisObject, afterModelList, emptyMap(), executableToCall = null), diffIds = evaluationResult.diffIds, result = executionResult, coverage = evaluationResult.coverage, diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt index 88ef4feb91..72756aa197 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt @@ -139,13 +139,13 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex } override fun generateResultAssertions() { - if (currentExecutable is MethodId) { + if (currentExecutableToCall is MethodId) { val currentExecution = currentExecution!! val executionResult = currentExecution.result if (executionResult is UtExecutionFailure) { val exceptionId = executionResult.rootCauseException.message?.let {PythonClassId(it)} ?: pythonExceptionClassId val executionBlock = { - with(currentExecutable) { + with(currentExecutableToCall) { when (this) { is MethodId -> thisInstance[this](*methodArguments.toTypedArray()).intercepted() else -> {} @@ -160,7 +160,7 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex return } CgTestMethodType.FAILING -> { - val executable = currentExecutable!! as PythonMethodId + val executable = currentExecutableToCall!! as PythonMethodId val executableName = "${executable.moduleName}.${executable.name}" val warningLine = "This test fails because function [$executableName] produces [${exceptionId.prettyName}]" From 8107fda04532a6267ea86fe9dc569beb6340af8a Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Wed, 26 Jul 2023 19:38:34 +0300 Subject: [PATCH 04/19] Replace `executableToCall` with `MockMvc.perform` for Spring controllers --- .../plugin/api/util/SpringModelUtils.kt | 17 ++++----- .../org/utbot/engine/UtBotSymbolicEngine.kt | 8 ++++- .../context/ConcreteExecutionContext.kt | 13 +++++++ .../simple/SimpleConcreteExecutionContext.kt | 18 ++++++++++ ...IntegrationTestConcreteExecutionContext.kt | 35 +++++++++++++++++++ 5 files changed, 82 insertions(+), 9 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt index 0a8802f721..9960d16ed8 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt @@ -108,30 +108,31 @@ object SpringModelUtils { private val mockMvcRequestBuildersClassId = ClassId("org.springframework.test.web.servlet.request.MockMvcRequestBuilders") private val requestBuilderClassId = ClassId("org.springframework.test.web.servlet.RequestBuilder") private val resultActionsClassId = ClassId("org.springframework.test.web.servlet.ResultActions") - private val mockMvcClassId = ClassId("org.springframework.test.web.servlet.MockMvc") + val mockMvcClassId = ClassId("org.springframework.test.web.servlet.MockMvc") private val mvcResultClassId = ClassId("org.springframework.test.web.servlet.MvcResult") private val mockHttpServletRequestBuilderClassId = ClassId("org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder") private val objectMapperClassId = ClassId("com.fasterxml.jackson.databind.ObjectMapper") + val mockMvcPerformMethodId = MethodId( + classId = mockMvcClassId, + name = "perform", + parameters = listOf(requestBuilderClassId), + returnType = resultActionsClassId + ) + fun createMockMvcModel(idGenerator: () -> Int) = createBeanModel("mockMvc", idGenerator(), mockMvcClassId) fun createGetMockMvcResponseModel(requestBuilderModel: UtModel, idGenerator: () -> Int): UtModel { val mockMvcModel = createMockMvcModel(idGenerator) - val performModel = UtAssembleModel( id = idGenerator(), classId = resultActionsClassId, modelName = "perform", instantiationCall = UtExecutableCallModel( instance = mockMvcModel, - executable = MethodId( - classId = mockMvcClassId, - name = "perform", - parameters = listOf(requestBuilderClassId), - returnType = resultActionsClassId - ), + executable = mockMvcPerformMethodId, params = listOf(requestBuilderModel) ) ) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index ff50c0e898..cfc15a9579 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -474,7 +474,13 @@ class UtBotSymbolicEngine( return@runJavaFuzzing BaseFeedback(Trie.emptyNode(), Control.PASS) } - val stateBefore = EnvironmentModels(thisInstance?.model, values.map { it.model }, mapOf(), methodUnderTest) + val stateBefore = concreteExecutionContext.createStateBefore( + thisInstance = thisInstance?.model, + parameters = values.map { it.model }, + statics = emptyMap(), + executableToCall = methodUnderTest, + idGenerator = defaultIdGenerator + ) val concreteExecutionResult: UtConcreteExecutionResult? = try { val timeoutMillis = min(UtSettings.concreteExecutionDefaultTimeoutInInstrumentedProcessMillis, diff) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/ConcreteExecutionContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ConcreteExecutionContext.kt index c9b8103345..d998474899 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/ConcreteExecutionContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ConcreteExecutionContext.kt @@ -2,7 +2,12 @@ package org.utbot.framework.context import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ConcreteContextLoadingResult +import org.utbot.framework.plugin.api.EnvironmentModels +import org.utbot.framework.plugin.api.ExecutableId +import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.UtExecution +import org.utbot.framework.plugin.api.UtModel +import org.utbot.fuzzer.IdGenerator import org.utbot.fuzzer.IdentityPreservingIdGenerator import org.utbot.fuzzing.JavaValueProvider import org.utbot.instrumentation.ConcreteExecutor @@ -26,4 +31,12 @@ interface ConcreteExecutionContext { classUnderTest: ClassId, idGenerator: IdentityPreservingIdGenerator, ): JavaValueProvider + + fun createStateBefore( + thisInstance: UtModel?, + parameters: List, + statics: Map, + executableToCall: ExecutableId, + idGenerator: IdGenerator + ): EnvironmentModels } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleConcreteExecutionContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleConcreteExecutionContext.kt index bdbd67c0c2..5d3b5260ec 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleConcreteExecutionContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleConcreteExecutionContext.kt @@ -3,7 +3,12 @@ package org.utbot.framework.context.simple import org.utbot.framework.context.ConcreteExecutionContext import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ConcreteContextLoadingResult +import org.utbot.framework.plugin.api.EnvironmentModels +import org.utbot.framework.plugin.api.ExecutableId +import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.UtExecution +import org.utbot.framework.plugin.api.UtModel +import org.utbot.fuzzer.IdGenerator import org.utbot.fuzzer.IdentityPreservingIdGenerator import org.utbot.fuzzing.JavaValueProvider import org.utbot.fuzzing.ValueProvider @@ -32,4 +37,17 @@ class SimpleConcreteExecutionContext(fullClassPath: String) : ConcreteExecutionC classUnderTest: ClassId, idGenerator: IdentityPreservingIdGenerator ): JavaValueProvider = ValueProvider.of(defaultValueProviders(idGenerator)) + + override fun createStateBefore( + thisInstance: UtModel?, + parameters: List, + statics: Map, + executableToCall: ExecutableId, + idGenerator: IdGenerator + ): EnvironmentModels = EnvironmentModels( + thisInstance = thisInstance, + parameters = parameters, + statics = statics, + executableToCall = executableToCall + ) } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringIntegrationTestConcreteExecutionContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringIntegrationTestConcreteExecutionContext.kt index 0df60cf02c..d936269c2a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringIntegrationTestConcreteExecutionContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringIntegrationTestConcreteExecutionContext.kt @@ -5,13 +5,23 @@ import org.utbot.common.tryLoadClass import org.utbot.framework.context.ConcreteExecutionContext import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ConcreteContextLoadingResult +import org.utbot.framework.plugin.api.ConstructorId +import org.utbot.framework.plugin.api.EnvironmentModels +import org.utbot.framework.plugin.api.ExecutableId +import org.utbot.framework.plugin.api.FieldId +import org.utbot.framework.plugin.api.MethodId import org.utbot.framework.plugin.api.SpringRepositoryId import org.utbot.framework.plugin.api.SpringSettings import org.utbot.framework.plugin.api.UtExecution +import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.util.SpringModelUtils +import org.utbot.framework.plugin.api.util.SpringModelUtils.createMockMvcModel +import org.utbot.framework.plugin.api.util.SpringModelUtils.createRequestBuilderModelOrNull +import org.utbot.framework.plugin.api.util.SpringModelUtils.mockMvcPerformMethodId import org.utbot.framework.plugin.api.util.allDeclaredFieldIds import org.utbot.framework.plugin.api.util.jField import org.utbot.framework.plugin.api.util.utContext +import org.utbot.fuzzer.IdGenerator import org.utbot.fuzzer.IdentityPreservingIdGenerator import org.utbot.fuzzing.JavaValueProvider import org.utbot.fuzzing.ValueProvider @@ -119,4 +129,29 @@ class SpringIntegrationTestConcreteExecutionContext( return ValueProvider.of(generatedValueFieldIds.map { FieldValueProvider(idGenerator, it) }) } + + override fun createStateBefore( + thisInstance: UtModel?, + parameters: List, + statics: Map, + executableToCall: ExecutableId, + idGenerator: IdGenerator + ): EnvironmentModels { + val delegateStateBefore = delegateContext.createStateBefore(thisInstance, parameters, statics, executableToCall, idGenerator) + return when (executableToCall) { + is ConstructorId -> delegateStateBefore + is MethodId -> { + val requestBuilderModel = createRequestBuilderModelOrNull( + methodId = executableToCall, + arguments = parameters, + idGenerator = { idGenerator.createId() } + ) ?: return delegateStateBefore + delegateStateBefore.copy( + thisInstance = createMockMvcModel { idGenerator.createId() }, + parameters = listOf(requestBuilderModel), + executableToCall = mockMvcPerformMethodId, + ) + } + } + } } \ No newline at end of file From fc72b628d027eeb3f2b5763b9a383beb35649420 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Wed, 26 Jul 2023 19:41:50 +0300 Subject: [PATCH 05/19] Remove handling of Spring controllers from `SpringUtExecutionInstrumentation` --- .../SpringUtExecutionInstrumentation.kt | 42 ------------------- .../utbot/spring/api/MockMvcResponseData.kt | 11 ----- .../kotlin/org/utbot/spring/api/SpringApi.kt | 9 ---- .../kotlin/org/utbot/spring/SpringApiImpl.kt | 4 -- .../org/utbot/spring/utils/MockMvcUtils.kt | 17 -------- 5 files changed, 83 deletions(-) delete mode 100644 utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/MockMvcResponseData.kt delete mode 100644 utbot-spring-commons/src/main/kotlin/org/utbot/spring/utils/MockMvcUtils.kt diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt index 3a6c2c3af2..525c972d30 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt @@ -10,15 +10,8 @@ import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.ConcreteContextLoadingResult import org.utbot.framework.plugin.api.SpringRepositoryId import org.utbot.framework.plugin.api.SpringSettings.* -import org.utbot.framework.plugin.api.util.SpringModelUtils -import org.utbot.framework.plugin.api.util.executableId -import org.utbot.framework.plugin.api.util.id -import org.utbot.framework.plugin.api.util.isStatic import org.utbot.framework.plugin.api.util.jClass -import org.utbot.framework.plugin.api.util.method -import org.utbot.framework.plugin.api.util.signature import org.utbot.instrumentation.instrumentation.ArgumentList -import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionData import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation import org.utbot.instrumentation.instrumentation.execution.context.InstrumentationContext @@ -70,42 +63,7 @@ class SpringUtExecutionInstrumentation( parameters: Any?, phasesWrapper: PhasesController.(invokeBasePhases: () -> UtConcreteExecutionResult) -> UtConcreteExecutionResult ): UtConcreteExecutionResult { - if (parameters !is UtConcreteExecutionData) - throw IllegalArgumentException("Argument parameters must be of type UtConcreteExecutionData, but was: ${parameters?.javaClass}") - getRelevantBeans(clazz).forEach { beanName -> springApi.resetBean(beanName) } - - @Suppress("NAME_SHADOWING") var clazz = clazz - @Suppress("NAME_SHADOWING") var methodSignature = methodSignature - @Suppress("NAME_SHADOWING") var parameters: UtConcreteExecutionData = parameters - clazz.id.allMethods.firstOrNull { it.signature == methodSignature }?.method?.executableId?.let { methodId -> - if (!methodId.isStatic) { - - // TODO try coming up with better approach to generating ids - var lastId = 1378139720 // random int (around 0.642 * Int.MAX_VALUE) - val idGenerator = { lastId++ } - - SpringModelUtils.createRequestBuilderModelOrNull( - methodId, - parameters.stateBefore.parameters, - idGenerator - )?.let { requestBuilderModel -> - parameters = parameters.copy(stateBefore = parameters.stateBefore.copy( - thisInstance = null, - parameters = listOf( - parameters.stateBefore.thisInstance!!, - SpringModelUtils.createMockMvcModel(idGenerator), - requestBuilderModel, - ) - )) - - val getMockMvcResponseMethod = springApi.getMockMvcResponseDataMethod - clazz = getMockMvcResponseMethod.declaringClass - methodSignature = getMockMvcResponseMethod.signature - } - } - } - return delegateInstrumentation.invoke(clazz, methodSignature, arguments, parameters) { invokeBasePhases -> phasesWrapper { // NB! beforeTestMethod() and afterTestMethod() are intentionally called inside phases, diff --git a/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/MockMvcResponseData.kt b/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/MockMvcResponseData.kt deleted file mode 100644 index cf536b0ea0..0000000000 --- a/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/MockMvcResponseData.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.utbot.spring.api - -/** - * Zero argument methods of `MockHttpServletResponse` that extract all data out of - * `MockHttpServletResponse` instance, that is needed for assertion generation - * - * @see SpringApi.getMockMvcResponseDataMethod - */ -val relevantMockMvcResponseDataGetterNames = listOf( - "getStatus", "getContentAsString", "getErrorMessage" -) diff --git a/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/SpringApi.kt b/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/SpringApi.kt index f5494128f3..3457204c5e 100644 --- a/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/SpringApi.kt +++ b/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/SpringApi.kt @@ -1,6 +1,5 @@ package org.utbot.spring.api -import java.lang.reflect.Method import java.net.URLClassLoader //TODO: `userSourcesClassLoader` must not be passed as a method argument, requires refactoring @@ -31,14 +30,6 @@ interface SpringApi { * because transactions are bound to threads */ fun afterTestMethod() - - /** - * Returns static method that should be defined like this: - * `fun getMockMvcResponse(controllerInstance: Any, mockMvc: MockMvc, requestBuilder: RequestBuilder): Map` - * - * @see relevantMockMvcResponseDataGetterNames - */ - val getMockMvcResponseDataMethod: Method } data class RepositoryDescription( diff --git a/utbot-spring-commons/src/main/kotlin/org/utbot/spring/SpringApiImpl.kt b/utbot-spring-commons/src/main/kotlin/org/utbot/spring/SpringApiImpl.kt index 15c735a6c0..687654cbae 100644 --- a/utbot-spring-commons/src/main/kotlin/org/utbot/spring/SpringApiImpl.kt +++ b/utbot-spring-commons/src/main/kotlin/org/utbot/spring/SpringApiImpl.kt @@ -17,7 +17,6 @@ import org.utbot.spring.api.provider.InstantiationSettings import org.utbot.spring.dummy.DummySpringIntegrationTestClass import org.utbot.spring.utils.DependencyUtils.isSpringDataOnClasspath import org.utbot.spring.utils.RepositoryUtils -import org.utbot.spring.utils.getMockMvcResponseData import java.lang.reflect.Method import java.net.URLClassLoader import kotlin.reflect.jvm.javaMethod @@ -158,9 +157,6 @@ class SpringApiImpl( isInsideTestMethod = false } - override val getMockMvcResponseDataMethod: Method - get() = ::getMockMvcResponseData.javaMethod!! - private fun describesRepository(bean: Any): Boolean = try { bean is CrudRepository<*, *> diff --git a/utbot-spring-commons/src/main/kotlin/org/utbot/spring/utils/MockMvcUtils.kt b/utbot-spring-commons/src/main/kotlin/org/utbot/spring/utils/MockMvcUtils.kt deleted file mode 100644 index 7265c1c0d9..0000000000 --- a/utbot-spring-commons/src/main/kotlin/org/utbot/spring/utils/MockMvcUtils.kt +++ /dev/null @@ -1,17 +0,0 @@ -package org.utbot.spring.utils - -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.RequestBuilder -import org.utbot.spring.api.relevantMockMvcResponseDataGetterNames - -fun getMockMvcResponseData( - // we ask for it, to force its initialization during value construction phase - @Suppress("UNUSED_PARAMETER") controllerInstance: Any, - mockMvc: MockMvc, - requestBuilder: RequestBuilder -): Map { - val response = mockMvc.perform(requestBuilder).andReturn().response - return relevantMockMvcResponseDataGetterNames.associateWith { getterName -> - response::class.java.getMethod(getterName).invoke(response) - } -} \ No newline at end of file From 5655f5ba961dd4c6f6ac09bbd49d298d1048ca4b Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 27 Jul 2023 12:44:54 +0300 Subject: [PATCH 06/19] Add `utCustomModelConstructorFinder` property to `UtModelConstructor` --- .../SimpleUtExecutionInstrumentation.kt | 6 +- .../JavaStdLibCustomModelConstructors.kt | 79 +++++++++++++++++ .../UtAssembleModelConstructors.kt | 88 ++----------------- .../constructors/UtCustomModelConstructor.kt | 19 ++++ .../constructors/UtModelConstructor.kt | 22 +++-- .../context/InstrumentationContext.kt | 6 +- .../context/SimpleInstrumentationContext.kt | 7 ++ .../phases/ModelConstructionPhase.kt | 10 ++- .../execution/phases/PhasesController.kt | 5 +- .../SpringUtExecutionInstrumentation.kt | 11 ++- .../process/InstrumentedProcessMain.kt | 9 +- .../constructors/BaseConstructorTest.kt | 10 ++- 12 files changed, 166 insertions(+), 106 deletions(-) create mode 100644 utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/JavaStdLibCustomModelConstructors.kt create mode 100644 utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtCustomModelConstructor.kt diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt index 00742b336d..4c3ba7775d 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt @@ -129,8 +129,10 @@ class SimpleUtExecutionInstrumentation( override fun getStaticField(fieldId: FieldId): Result = delegateInstrumentation.getStaticField(fieldId).map { value -> - UtModelConstructor.createOnlyUserClassesConstructor(pathsToUserClasses) - .construct(value, fieldId.type) + UtModelConstructor.createOnlyUserClassesConstructor( + pathsToUserClasses = pathsToUserClasses, + utCustomModelConstructorFinder = instrumentationContext::findUtCustomModelConstructor + ).construct(value, fieldId.type) } override fun transform( diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/JavaStdLibCustomModelConstructors.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/JavaStdLibCustomModelConstructors.kt new file mode 100644 index 0000000000..148bb3e059 --- /dev/null +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/JavaStdLibCustomModelConstructors.kt @@ -0,0 +1,79 @@ +package org.utbot.instrumentation.instrumentation.execution.constructors + +import org.utbot.framework.plugin.api.util.jClass +import org.utbot.framework.plugin.api.util.primitiveWrappers +import org.utbot.framework.plugin.api.util.voidWrapperClassId + +val javaStdLibCustomModelConstructors: Map, () -> UtCustomModelConstructor> = + mutableMapOf, () -> UtAssembleModelConstructorBase>( + /** + * Optionals + */ + java.util.OptionalInt::class.java to { OptionalIntConstructor() }, + java.util.OptionalLong::class.java to { OptionalLongConstructor() }, + java.util.OptionalDouble::class.java to { OptionalDoubleConstructor() }, + java.util.Optional::class.java to { OptionalConstructor() }, + + /** + * Lists + */ + java.util.LinkedList::class.java to { CollectionConstructor() }, + java.util.ArrayList::class.java to { CollectionConstructor() }, + java.util.AbstractList::class.java to { CollectionConstructor() }, + java.util.List::class.java to { CollectionConstructor() }, + java.util.concurrent.CopyOnWriteArrayList::class.java to { CollectionConstructor() }, + + /** + * Queues, deques + */ + java.util.PriorityQueue::class.java to { CollectionConstructor() }, + java.util.ArrayDeque::class.java to { CollectionConstructor() }, + java.util.concurrent.LinkedBlockingQueue::class.java to { CollectionConstructor() }, + java.util.concurrent.LinkedBlockingDeque::class.java to { CollectionConstructor() }, + java.util.concurrent.ConcurrentLinkedQueue::class.java to { CollectionConstructor() }, + java.util.concurrent.ConcurrentLinkedDeque::class.java to { CollectionConstructor() }, + java.util.Queue::class.java to { CollectionConstructor() }, + java.util.Deque::class.java to { CollectionConstructor() }, + + /** + * Sets + */ + java.util.HashSet::class.java to { CollectionConstructor() }, + java.util.TreeSet::class.java to { CollectionConstructor() }, + java.util.LinkedHashSet::class.java to { CollectionConstructor() }, + java.util.AbstractSet::class.java to { CollectionConstructor() }, + java.util.Set::class.java to { CollectionConstructor() }, + + /** + * Maps + */ + java.util.HashMap::class.java to { MapConstructor() }, + java.util.TreeMap::class.java to { MapConstructor() }, + java.util.LinkedHashMap::class.java to { MapConstructor() }, + java.util.AbstractMap::class.java to { MapConstructor() }, + java.util.concurrent.ConcurrentMap::class.java to { MapConstructor() }, + java.util.concurrent.ConcurrentHashMap::class.java to { MapConstructor() }, + java.util.IdentityHashMap::class.java to { MapConstructor() }, + java.util.WeakHashMap::class.java to { MapConstructor() }, + + /** + * Hashtables + */ + java.util.Hashtable::class.java to { MapConstructor() }, + + /** + * String wrapper + */ + java.lang.String::class.java.let { it to { PrimitiveWrapperConstructor() } }, + + /** + * TODO: JIRA:1405 -- Add assemble constructors for another standard classes as well. + */ + ).apply { + /** + * Primitive wrappers + */ + this += primitiveWrappers + .filter { it != voidWrapperClassId } + .associate { it.jClass to { PrimitiveWrapperConstructor() } } + } \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtAssembleModelConstructors.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtAssembleModelConstructors.kt index 39576b949b..cc6c4d87e2 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtAssembleModelConstructors.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtAssembleModelConstructors.kt @@ -7,86 +7,8 @@ import java.util.stream.LongStream import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtExecutableCallModel +import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtStatementModel -import org.utbot.framework.plugin.api.util.jClass -import org.utbot.framework.plugin.api.util.primitiveWrappers -import org.utbot.framework.plugin.api.util.voidWrapperClassId - -private val predefinedConstructors = mutableMapOf, () -> UtAssembleModelConstructorBase>( - /** - * Optionals - */ - java.util.OptionalInt::class.java to { OptionalIntConstructor() }, - java.util.OptionalLong::class.java to { OptionalLongConstructor() }, - java.util.OptionalDouble::class.java to { OptionalDoubleConstructor() }, - java.util.Optional::class.java to { OptionalConstructor() }, - - /** - * Lists - */ - java.util.LinkedList::class.java to { CollectionConstructor() }, - java.util.ArrayList::class.java to { CollectionConstructor() }, - java.util.AbstractList::class.java to { CollectionConstructor() }, - java.util.List::class.java to { CollectionConstructor() }, - java.util.concurrent.CopyOnWriteArrayList::class.java to { CollectionConstructor() }, - - /** - * Queues, deques - */ - java.util.PriorityQueue::class.java to { CollectionConstructor() }, - java.util.ArrayDeque::class.java to { CollectionConstructor() }, - java.util.concurrent.LinkedBlockingQueue::class.java to { CollectionConstructor() }, - java.util.concurrent.LinkedBlockingDeque::class.java to { CollectionConstructor() }, - java.util.concurrent.ConcurrentLinkedQueue::class.java to { CollectionConstructor() }, - java.util.concurrent.ConcurrentLinkedDeque::class.java to { CollectionConstructor() }, - java.util.Queue::class.java to { CollectionConstructor() }, - java.util.Deque::class.java to { CollectionConstructor() }, - - /** - * Sets - */ - java.util.HashSet::class.java to { CollectionConstructor() }, - java.util.TreeSet::class.java to { CollectionConstructor() }, - java.util.LinkedHashSet::class.java to { CollectionConstructor() }, - java.util.AbstractSet::class.java to { CollectionConstructor() }, - java.util.Set::class.java to { CollectionConstructor() }, - - /** - * Maps - */ - java.util.HashMap::class.java to { MapConstructor() }, - java.util.TreeMap::class.java to { MapConstructor() }, - java.util.LinkedHashMap::class.java to { MapConstructor() }, - java.util.AbstractMap::class.java to { MapConstructor() }, - java.util.concurrent.ConcurrentMap::class.java to { MapConstructor() }, - java.util.concurrent.ConcurrentHashMap::class.java to { MapConstructor() }, - java.util.IdentityHashMap::class.java to { MapConstructor() }, - java.util.WeakHashMap::class.java to { MapConstructor() }, - - /** - * Hashtables - */ - java.util.Hashtable::class.java to { MapConstructor() }, - - /** - * String wrapper - */ - java.lang.String::class.java.let { it to { PrimitiveWrapperConstructor() } }, - - /** - * TODO: JIRA:1405 -- Add assemble constructors for another standard classes as well. - */ -).apply { - /** - * Primitive wrappers - */ - this += primitiveWrappers - .filter { it != voidWrapperClassId } - .associate { it.jClass to { PrimitiveWrapperConstructor() } } -} - -internal fun findUtAssembleModelConstructor(classId: ClassId): UtAssembleModelConstructorBase? = - predefinedConstructors[classId.jClass]?.invoke() internal fun findStreamConstructor(stream: BaseStream<*, *>): UtAssembleModelConstructorBase = when (stream) { @@ -96,18 +18,18 @@ internal fun findStreamConstructor(stream: BaseStream<*, *>): UtAssembleModelCon else -> BaseStreamConstructor() } -internal abstract class UtAssembleModelConstructorBase { - fun constructAssembleModel( +internal abstract class UtAssembleModelConstructorBase : UtCustomModelConstructor { + override fun constructCustomModel( internalConstructor: UtModelConstructorInterface, value: Any, valueClassId: ClassId, id: Int?, - init: (UtAssembleModel) -> Unit + saveToCache: (UtModel) -> Unit ): UtAssembleModel { val baseName = valueClassId.simpleName.decapitalize() val instantiationCall = provideInstantiationCall(internalConstructor, value, valueClassId) return UtAssembleModel(id, valueClassId, nextModelName(baseName), instantiationCall) { - init(this) + saveToCache(this) provideModificationChain(internalConstructor, value) } } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtCustomModelConstructor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtCustomModelConstructor.kt new file mode 100644 index 0000000000..93e1db1b28 --- /dev/null +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtCustomModelConstructor.kt @@ -0,0 +1,19 @@ +package org.utbot.instrumentation.instrumentation.execution.constructors + +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtModel + +/** + * Responsible for constructing [UtModel]s of some specific type, that are more human-readable + * when rendered by the code generation compared to [UtCompositeModel]s. + */ +interface UtCustomModelConstructor { + fun constructCustomModel( + internalConstructor: UtModelConstructorInterface, + value: Any, + valueClassId: ClassId, + id: Int?, + saveToCache: (UtModel) -> Unit + ): UtModel +} \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelConstructor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelConstructor.kt index c9a9f75adf..4bc5bd164f 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelConstructor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelConstructor.kt @@ -32,6 +32,7 @@ interface UtModelConstructorInterface { */ class UtModelConstructor( private val objectToModelCache: IdentityHashMap, + private val utCustomModelConstructorFinder: (ClassId) -> UtCustomModelConstructor?, private val compositeModelStrategy: UtCompositeModelStrategy = AlwaysConstructStrategy, private val maxDepth: Long = DEFAULT_MAX_DEPTH ) : UtModelConstructorInterface { @@ -46,12 +47,15 @@ class UtModelConstructor( companion object { private const val DEFAULT_MAX_DEPTH = 7L - fun createOnlyUserClassesConstructor(pathsToUserClasses: Set): UtModelConstructor { + fun createOnlyUserClassesConstructor( + pathsToUserClasses: Set, + utCustomModelConstructorFinder: (ClassId) -> UtCustomModelConstructor? + ): UtModelConstructor { val cache = IdentityHashMap() val strategy = ConstructOnlyUserClassesOrCachedObjectsStrategy( pathsToUserClasses, cache ) - return UtModelConstructor(cache, strategy) + return UtModelConstructor(cache, utCustomModelConstructorFinder, strategy) } } @@ -269,7 +273,7 @@ class UtModelConstructor( val streamConstructor = findStreamConstructor(stream) try { - streamConstructor.constructAssembleModel(this, stream, valueToClassId(stream), handleId(stream)) { + streamConstructor.constructCustomModel(this, stream, valueToClassId(stream), handleId(stream)) { constructedObjects[stream] = it } } catch (e: Exception) { @@ -285,18 +289,18 @@ class UtModelConstructor( */ private fun constructFromAny(value: Any, remainingDepth: Long): UtModel = constructedObjects.getOrElse(value) { - tryConstructUtAssembleModel(value, remainingDepth) ?: constructCompositeModel(value, remainingDepth) + tryConstructCustomModel(value, remainingDepth) ?: constructCompositeModel(value, remainingDepth) } /** - * Constructs UtAssembleModel but does it only for predefined list of classes. + * Constructs custom UtModel but does it only for predefined list of classes. * - * Uses runtime class of an object. + * Uses runtime class of [value]. */ - private fun tryConstructUtAssembleModel(value: Any, remainingDepth: Long): UtModel? = - findUtAssembleModelConstructor(value::class.java.id)?.let { assembleConstructor -> + private fun tryConstructCustomModel(value: Any, remainingDepth: Long): UtModel? = + utCustomModelConstructorFinder(value::class.java.id)?.let { modelConstructor -> try { - assembleConstructor.constructAssembleModel( + modelConstructor.constructCustomModel( internalConstructor = this.withMaxDepth(remainingDepth - 1), value = value, valueClassId = valueToClassId(value), diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/InstrumentationContext.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/InstrumentationContext.kt index 6f207bdb40..b4f2906481 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/InstrumentationContext.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/InstrumentationContext.kt @@ -1,7 +1,9 @@ package org.utbot.instrumentation.instrumentation.execution.context +import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtConcreteValue import org.utbot.framework.plugin.api.UtModel +import org.utbot.instrumentation.instrumentation.execution.constructors.UtCustomModelConstructor import java.lang.reflect.Method import java.util.IdentityHashMap import org.utbot.instrumentation.instrumentation.mock.computeKeyForMethod @@ -9,7 +11,7 @@ import org.utbot.instrumentation.instrumentation.mock.computeKeyForMethod /** * Some information, which is fully computed after classes instrumentation. * - * This information will be used later in `invoke` function to construct values. + * This information will be used later in `invoke` function to construct values and models. */ interface InstrumentationContext { /** @@ -26,6 +28,8 @@ interface InstrumentationContext { */ fun constructContextDependentValue(model: UtModel): UtConcreteValue<*>? + fun findUtCustomModelConstructor(classId: ClassId): UtCustomModelConstructor? + object MockGetter { data class MockContainer(private val values: List<*>) { private var ptr: Int = 0 diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/SimpleInstrumentationContext.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/SimpleInstrumentationContext.kt index 83cc38dec8..ef6f24594c 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/SimpleInstrumentationContext.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/SimpleInstrumentationContext.kt @@ -1,7 +1,11 @@ package org.utbot.instrumentation.instrumentation.execution.context +import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtConcreteValue import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.util.jClass +import org.utbot.instrumentation.instrumentation.execution.constructors.UtCustomModelConstructor +import org.utbot.instrumentation.instrumentation.execution.constructors.javaStdLibCustomModelConstructors /** * Simple instrumentation context, that is used for pure JVM projects without @@ -14,4 +18,7 @@ class SimpleInstrumentationContext : InstrumentationContext { * There are no context dependent values for pure JVM projects */ override fun constructContextDependentValue(model: UtModel): UtConcreteValue<*>? = null + + override fun findUtCustomModelConstructor(classId: ClassId): UtCustomModelConstructor? = + javaStdLibCustomModelConstructors[classId.jClass]?.invoke() } \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ModelConstructionPhase.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ModelConstructionPhase.kt index 2c28010251..f68f723374 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ModelConstructionPhase.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ModelConstructionPhase.kt @@ -9,6 +9,7 @@ import org.utbot.instrumentation.instrumentation.et.ExplicitThrowInstruction import org.utbot.instrumentation.instrumentation.et.TraceHandler import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult import org.utbot.instrumentation.instrumentation.execution.constructors.UtCompositeModelStrategy +import org.utbot.instrumentation.instrumentation.execution.constructors.UtCustomModelConstructor import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelConstructor import java.security.AccessControlException import java.util.* @@ -17,7 +18,8 @@ import java.util.* * This phase of model construction from concrete values. */ class ModelConstructionPhase( - private val traceHandler: TraceHandler + private val traceHandler: TraceHandler, + private val utCustomModelConstructorFinder: (ClassId) -> UtCustomModelConstructor?, ) : ExecutionPhase { override fun wrapError(e: Throwable): ExecutionPhaseException { @@ -42,7 +44,11 @@ class ModelConstructionPhase( fun configureConstructor(block: ConstructorConfiguration.() -> Unit) { ConstructorConfiguration().run { block() - constructor = UtModelConstructor(cache, strategy) + constructor = UtModelConstructor( + objectToModelCache = cache, + utCustomModelConstructorFinder = utCustomModelConstructorFinder, + compositeModelStrategy = strategy, + ) } } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/PhasesController.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/PhasesController.kt index 7f3d100c56..a552bcbd83 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/PhasesController.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/PhasesController.kt @@ -31,7 +31,10 @@ class PhasesController( val statisticsCollectionPhase = StatisticsCollectionPhase(traceHandler) - val modelConstructionPhase = ModelConstructionPhase(traceHandler) + val modelConstructionPhase = ModelConstructionPhase( + traceHandler = traceHandler, + utCustomModelConstructorFinder = instrumentationContext::findUtCustomModelConstructor + ) val postprocessingPhase = PostprocessingPhase() diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt index 525c972d30..2a6808b694 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt @@ -10,10 +10,13 @@ import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.ConcreteContextLoadingResult import org.utbot.framework.plugin.api.SpringRepositoryId import org.utbot.framework.plugin.api.SpringSettings.* +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.jClass import org.utbot.instrumentation.instrumentation.ArgumentList import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation +import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelConstructor import org.utbot.instrumentation.instrumentation.execution.context.InstrumentationContext import org.utbot.instrumentation.instrumentation.execution.phases.ExecutionPhaseFailingOnAnyException import org.utbot.instrumentation.instrumentation.execution.phases.PhasesController @@ -94,7 +97,13 @@ class SpringUtExecutionInstrumentation( .also { logger.info { "Detected relevant beans for class ${clazz.name}: $it" } } } - fun getBean(beanName: String): Any = springApi.getBean(beanName) + fun getBeanModel(beanName: String, classpathToConstruct: Set): UtModel { + val bean = springApi.getBean(beanName) + return UtModelConstructor.createOnlyUserClassesConstructor( + pathsToUserClasses = classpathToConstruct, + utCustomModelConstructorFinder = instrumentationContext::findUtCustomModelConstructor + ).construct(bean, bean::class.java.id) + } fun getRepositoryDescriptions(classId: ClassId): Set { val relevantBeanNames = getRelevantBeans(classId.jClass) diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt index 58618ac593..590f4eb7c4 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt @@ -13,7 +13,6 @@ import org.utbot.instrumentation.agent.Agent import org.utbot.instrumentation.instrumentation.Instrumentation import org.utbot.instrumentation.instrumentation.coverage.CoverageInstrumentation import org.utbot.instrumentation.instrumentation.spring.SpringUtExecutionInstrumentation -import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelConstructor import org.utbot.instrumentation.process.generated.CollectCoverageResult import org.utbot.instrumentation.process.generated.GetSpringBeanResult import org.utbot.instrumentation.process.generated.GetSpringRepositoriesResult @@ -164,11 +163,9 @@ private fun InstrumentedProcessModel.setup(kryoHelper: KryoHelper, watchdog: Idl CollectCoverageResult(kryoHelper.writeObject(result)) } watchdog.measureTimeForActiveCall(getSpringBean, "Getting Spring bean") { params -> - val bean = (instrumentation as SpringUtExecutionInstrumentation).getBean(params.beanName) - val model = UtModelConstructor.createOnlyUserClassesConstructor(pathsToUserClasses).construct( - bean, ClassId(bean.javaClass.name) - ) - GetSpringBeanResult(kryoHelper.writeObject(model)) + val springUtExecutionInstrumentation = instrumentation as SpringUtExecutionInstrumentation + val beanModel = springUtExecutionInstrumentation.getBeanModel(params.beanName, pathsToUserClasses) + GetSpringBeanResult(kryoHelper.writeObject(beanModel)) } watchdog.measureTimeForActiveCall(getRelevantSpringRepositories, "Getting Spring repositories") { params -> val classId: ClassId = kryoHelper.readObject(params.classId) diff --git a/utbot-instrumentation/src/test/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/BaseConstructorTest.kt b/utbot-instrumentation/src/test/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/BaseConstructorTest.kt index a1dc7ffc2c..318a12eb7c 100644 --- a/utbot-instrumentation/src/test/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/BaseConstructorTest.kt +++ b/utbot-instrumentation/src/test/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/BaseConstructorTest.kt @@ -8,6 +8,8 @@ import java.util.IdentityHashMap import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.util.jClass abstract class BaseConstructorTest { private lateinit var cookie: AutoCloseable @@ -23,11 +25,17 @@ abstract class BaseConstructorTest { } protected fun computeReconstructed(value: T): T { - val model = UtModelConstructor(IdentityHashMap()).construct(value, value::class.java.id) + val model = UtModelConstructor( + objectToModelCache = IdentityHashMap(), + utCustomModelConstructorFinder = ::findUtCustomModelConstructor + ).construct(value, value::class.java.id) Assertions.assertTrue(model is UtAssembleModel) @Suppress("UNCHECKED_CAST") return ValueConstructor().construct(listOf(model)).single().value as T } + + protected open fun findUtCustomModelConstructor(classId: ClassId): UtCustomModelConstructor? = + javaStdLibCustomModelConstructors[classId.jClass]?.invoke() } \ No newline at end of file From 0b8d36438cb497be6de45f1c82dc4a708d59ecf7 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 27 Jul 2023 12:47:45 +0300 Subject: [PATCH 07/19] Add `UtSpringMockMvcResultActionsModel` and its `UtCustomModelConstructor` --- .../org/utbot/framework/plugin/api/Api.kt | 12 +++ .../plugin/api/util/SpringModelUtils.kt | 75 +++++++++++++++---- .../spring/SpringInstrumentationContext.kt | 7 ++ .../UtMockMvcResultActionsModelConstructor.kt | 41 ++++++++++ 4 files changed, 121 insertions(+), 14 deletions(-) create mode 100644 utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt 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 e76cea5178..7c1f8e8f0e 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 @@ -745,6 +745,18 @@ data class SpringRepositoryId( val entityClassId: ClassId, ) +class UtSpringMockMvcResultActionsModel( + val status: Int, + val errorMessage: String?, + val contentAsString: String, + val viewName: String?, + // model for mvcResult.modelAndView?.model + val model: UtModel? + // TODO add headers and other data +) : UtModel( + classId = SpringModelUtils.resultActionsClassId +) + /** * Model for a step to obtain [UtAssembleModel]. */ diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt index 9960d16ed8..48328d7985 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt @@ -107,10 +107,11 @@ object SpringModelUtils { private val mockMvcRequestBuildersClassId = ClassId("org.springframework.test.web.servlet.request.MockMvcRequestBuilders") private val requestBuilderClassId = ClassId("org.springframework.test.web.servlet.RequestBuilder") - private val resultActionsClassId = ClassId("org.springframework.test.web.servlet.ResultActions") - val mockMvcClassId = ClassId("org.springframework.test.web.servlet.MockMvc") + val resultActionsClassId = ClassId("org.springframework.test.web.servlet.ResultActions") + private val mockMvcClassId = ClassId("org.springframework.test.web.servlet.MockMvc") private val mvcResultClassId = ClassId("org.springframework.test.web.servlet.MvcResult") private val mockHttpServletRequestBuilderClassId = ClassId("org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder") + private val modelAndViewClassId = ClassId("org.springframework.web.servlet.ModelAndView") private val objectMapperClassId = ClassId("com.fasterxml.jackson.databind.ObjectMapper") @@ -121,6 +122,62 @@ object SpringModelUtils { returnType = resultActionsClassId ) + val resultActionsAndReturnMethodId = MethodId( + classId = resultActionsClassId, + name = "andReturn", + parameters = listOf(), + returnType = mvcResultClassId + ) + + val mvcResultGetResponseMethodId = MethodId( + classId = mvcResultClassId, + name = "getResponse", + parameters = listOf(), + returnType = mockHttpServletResponseClassId + ) + + val responseGetStatusMethodId = MethodId( + classId = mockHttpServletResponseClassId, + name = "getStatus", + parameters = listOf(), + returnType = intClassId + ) + + val responseGetErrorMessageMethodId = MethodId( + classId = mockHttpServletResponseClassId, + name = "getErrorMessage", + parameters = listOf(), + returnType = stringClassId + ) + + val responseGetContentAsStringMethodId = MethodId( + classId = mockHttpServletResponseClassId, + name = "getContentAsString", + parameters = listOf(), + returnType = stringClassId + ) + + val mvcResultGetModelAndViewMethodId = MethodId( + classId = mvcResultClassId, + name = "getModelAndView", + parameters = listOf(), + returnType = modelAndViewClassId + ) + + val modelAndViewGetModelMethodId = MethodId( + classId = modelAndViewClassId, + name = "getModel", + parameters = listOf(), + returnType = mapClassId + ) + + val modelAndViewGetViewNameMethodId = MethodId( + classId = modelAndViewClassId, + name = "getViewName", + parameters = listOf(), + returnType = stringClassId + ) + fun createMockMvcModel(idGenerator: () -> Int) = createBeanModel("mockMvc", idGenerator(), mockMvcClassId) @@ -143,12 +200,7 @@ object SpringModelUtils { modelName = "andReturn", instantiationCall = UtExecutableCallModel( instance = performModel, - executable = MethodId( - classId = mvcResultClassId, - name = "andReturn", - parameters = listOf(), - returnType = mvcResultClassId - ), + executable = resultActionsAndReturnMethodId, params = listOf() ) ) @@ -158,12 +210,7 @@ object SpringModelUtils { modelName = "getResponse", instantiationCall = UtExecutableCallModel( instance = andReturnModel, - executable = MethodId( - classId = mvcResultClassId, - name = "getResponse", - parameters = listOf(), - returnType = mockHttpServletResponseClassId - ), + executable = mvcResultGetResponseMethodId, params = listOf() ) ) diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt index 6850a0e67a..ebe91dfc44 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt @@ -1,11 +1,14 @@ package org.utbot.instrumentation.instrumentation.spring +import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.SpringSettings.* import org.utbot.framework.plugin.api.SpringConfiguration.* import org.utbot.framework.plugin.api.UtConcreteValue import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtSpringContextModel +import org.utbot.framework.plugin.api.util.SpringModelUtils.resultActionsClassId import org.utbot.framework.plugin.api.util.utContext +import org.utbot.instrumentation.instrumentation.execution.constructors.UtCustomModelConstructor import org.utbot.instrumentation.instrumentation.execution.context.InstrumentationContext import org.utbot.spring.api.SpringApi import org.utbot.spring.api.provider.SpringApiProviderFacade @@ -44,4 +47,8 @@ class SpringInstrumentationContext( is UtSpringContextModel -> UtConcreteValue(springApi.getOrLoadSpringApplicationContext()) else -> delegateInstrumentationContext.constructContextDependentValue(model) } + + override fun findUtCustomModelConstructor(classId: ClassId): UtCustomModelConstructor? = + if (classId == resultActionsClassId) UtMockMvcResultActionsModelConstructor() + else delegateInstrumentationContext.findUtCustomModelConstructor(classId) } \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt new file mode 100644 index 0000000000..5c521e741b --- /dev/null +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt @@ -0,0 +1,41 @@ +package org.utbot.instrumentation.instrumentation.spring + +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtSpringMockMvcResultActionsModel +import org.utbot.framework.plugin.api.util.SpringModelUtils.modelAndViewGetModelMethodId +import org.utbot.framework.plugin.api.util.SpringModelUtils.modelAndViewGetViewNameMethodId +import org.utbot.framework.plugin.api.util.SpringModelUtils.mvcResultGetModelAndViewMethodId +import org.utbot.framework.plugin.api.util.SpringModelUtils.mvcResultGetResponseMethodId +import org.utbot.framework.plugin.api.util.SpringModelUtils.responseGetContentAsStringMethodId +import org.utbot.framework.plugin.api.util.SpringModelUtils.responseGetErrorMessageMethodId +import org.utbot.framework.plugin.api.util.SpringModelUtils.responseGetStatusMethodId +import org.utbot.framework.plugin.api.util.SpringModelUtils.resultActionsAndReturnMethodId +import org.utbot.framework.plugin.api.util.mapClassId +import org.utbot.framework.plugin.api.util.method +import org.utbot.instrumentation.instrumentation.execution.constructors.UtCustomModelConstructor +import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelConstructorInterface + +class UtMockMvcResultActionsModelConstructor : UtCustomModelConstructor { + override fun constructCustomModel( + internalConstructor: UtModelConstructorInterface, + value: Any, + valueClassId: ClassId, + id: Int?, + saveToCache: (UtModel) -> Unit + ): UtModel { + val mvcResult = resultActionsAndReturnMethodId.method.invoke(value) + val response = mvcResultGetResponseMethodId.method.invoke(mvcResult) + val modelAndView = mvcResultGetModelAndViewMethodId.method.invoke(mvcResult) + + return UtSpringMockMvcResultActionsModel( + status = responseGetStatusMethodId.method.invoke(response) as Int, + errorMessage = responseGetErrorMessageMethodId.method.invoke(response) as String?, + contentAsString = responseGetContentAsStringMethodId.method.invoke(response) as String, + viewName = modelAndView?.let { modelAndViewGetViewNameMethodId.method.invoke(modelAndView) } as String?, + model = modelAndView?.let { modelAndViewGetModelMethodId.method.invoke(modelAndView) }?.let { + internalConstructor.construct((it as Map<*, *>).toMap(), mapClassId) + } + ).also(saveToCache) + } +} \ No newline at end of file From 1bf7ab8b1f1c229181ba135640806be2559b3261 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 27 Jul 2023 13:25:45 +0300 Subject: [PATCH 08/19] Add `UtCustomModel` (i.e. common parent of all framework specific models) --- .../org/utbot/framework/plugin/api/Api.kt | 19 ++++++++++++++++--- .../builders/SpringTestClassModelBuilder.kt | 6 +++--- .../fields/ExecutionStateAnalyzer.kt | 4 ++-- .../utbot/framework/util/UtModelVisitor.kt | 6 +++--- .../UtMockMvcResultActionsModelConstructor.kt | 1 + 5 files changed, 25 insertions(+), 11 deletions(-) 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 7c1f8e8f0e..32bc87d783 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 @@ -727,7 +727,17 @@ class UtLambdaModel( } } -object UtSpringContextModel : UtReferenceModel( +/** + * Common parent of all framework-specific models (e.g. Spring-specific models) + */ +abstract class UtCustomModel( + val origin: UtCompositeModel? = null, + id: Int?, + classId: ClassId, + modelName: String = id.toString() +) : UtReferenceModel(id, classId, modelName) + +object UtSpringContextModel : UtCustomModel( id = null, classId = SpringModelUtils.applicationContextClassId, modelName = "applicationContext" @@ -746,6 +756,7 @@ data class SpringRepositoryId( ) class UtSpringMockMvcResultActionsModel( + id: Int?, val status: Int, val errorMessage: String?, val contentAsString: String, @@ -753,8 +764,10 @@ class UtSpringMockMvcResultActionsModel( // model for mvcResult.modelAndView?.model val model: UtModel? // TODO add headers and other data -) : UtModel( - classId = SpringModelUtils.resultActionsClassId +) : UtCustomModel( + classId = SpringModelUtils.resultActionsClassId, + id = id, + modelName = "mockMvcResultActions@$id" ) /** diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt index 76da5b7899..49dc213fcf 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt @@ -10,13 +10,13 @@ import org.utbot.framework.plugin.api.UtArrayModel import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtClassRefModel import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtCustomModel import org.utbot.framework.plugin.api.UtDirectSetFieldModel import org.utbot.framework.plugin.api.UtEnumConstantModel import org.utbot.framework.plugin.api.UtLambdaModel import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtNullModel import org.utbot.framework.plugin.api.UtPrimitiveModel -import org.utbot.framework.plugin.api.UtSpringContextModel import org.utbot.framework.plugin.api.UtStatementCallModel import org.utbot.framework.plugin.api.UtVoidModel import org.utbot.framework.plugin.api.isMockModel @@ -112,8 +112,8 @@ class SpringTestClassModelBuilder(val context: CgContext): TestClassModelBuilder is UtPrimitiveModel, is UtClassRefModel, is UtVoidModel, - is UtEnumConstantModel, - is UtSpringContextModel -> {} + is UtEnumConstantModel -> {} + is UtCustomModel -> currentModel.origin?.let { collectRecursively(it.wrap(), allModels) } is UtLambdaModel -> { currentModel.capturedValues.forEach { collectRecursively(it.wrap(), allModels) } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt index 3b14ac3551..93eb24b4f9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt @@ -10,6 +10,7 @@ import org.utbot.framework.plugin.api.UtArrayModel import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtClassRefModel import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtCustomModel import org.utbot.framework.plugin.api.UtEnumConstantModel import org.utbot.framework.plugin.api.UtExecution import org.utbot.framework.plugin.api.UtLambdaModel @@ -17,7 +18,6 @@ import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtNullModel import org.utbot.framework.plugin.api.UtPrimitiveModel import org.utbot.framework.plugin.api.UtReferenceModel -import org.utbot.framework.plugin.api.UtSpringContextModel import org.utbot.framework.plugin.api.UtSymbolicExecution import org.utbot.framework.plugin.api.UtVoidModel import org.utbot.framework.util.UtModelVisitor @@ -238,7 +238,7 @@ private class FieldStateVisitor : UtModelVisitor() { recordFieldState(data, element) } - override fun visit(element: UtSpringContextModel, data: FieldData) { + override fun visit(element: UtCustomModel, data: FieldData) { recordFieldState(data, element) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/util/UtModelVisitor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/util/UtModelVisitor.kt index 48fa6418bc..e4621e64c9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/util/UtModelVisitor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/util/UtModelVisitor.kt @@ -4,13 +4,13 @@ import org.utbot.framework.plugin.api.UtArrayModel import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtClassRefModel import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtCustomModel import org.utbot.framework.plugin.api.UtEnumConstantModel import org.utbot.framework.plugin.api.UtLambdaModel import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtNullModel import org.utbot.framework.plugin.api.UtPrimitiveModel import org.utbot.framework.plugin.api.UtReferenceModel -import org.utbot.framework.plugin.api.UtSpringContextModel import org.utbot.framework.plugin.api.UtVoidModel import java.util.Collections import java.util.IdentityHashMap @@ -36,7 +36,7 @@ abstract class UtModelVisitor { is UtAssembleModel -> visit(element, data) is UtCompositeModel -> visit(element, data) is UtLambdaModel -> visit(element, data) - is UtSpringContextModel -> visit(element, data) + is UtCustomModel -> visit(element, data) } } @@ -46,7 +46,7 @@ abstract class UtModelVisitor { protected abstract fun visit(element: UtAssembleModel, data: D) protected abstract fun visit(element: UtCompositeModel, data: D) protected abstract fun visit(element: UtLambdaModel, data: D) - protected abstract fun visit(element: UtSpringContextModel, data: D) + protected abstract fun visit(element: UtCustomModel, data: D) /** * Returns true when we can traverse the given model. diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt index 5c521e741b..231c3f19e4 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt @@ -29,6 +29,7 @@ class UtMockMvcResultActionsModelConstructor : UtCustomModelConstructor { val modelAndView = mvcResultGetModelAndViewMethodId.method.invoke(mvcResult) return UtSpringMockMvcResultActionsModel( + id = id, status = responseGetStatusMethodId.method.invoke(response) as Int, errorMessage = responseGetErrorMessageMethodId.method.invoke(response) as String?, contentAsString = responseGetContentAsStringMethodId.method.invoke(response) as String, From 9fddb9771ed7ab8a21c4a97d5b9e8d2babdd41a4 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 27 Jul 2023 14:58:21 +0300 Subject: [PATCH 09/19] Properly handle `UtCustomModel` --- .../org/utbot/framework/plugin/api/Api.kt | 12 +- .../api/util/constructor/ValueConstructor.kt | 5 + .../CompositeModelMinimizationChecker.kt | 2 + .../assemble/AssembleModelGenerator.kt | 4 +- .../codegen/tree/CgClassFieldManager.kt | 3 +- .../codegen/tree/CgMethodConstructor.kt | 134 ++++++++++-------- .../codegen/tree/CgVariableConstructor.kt | 2 + .../fields/ExecutionStateAnalyzer.kt | 7 +- .../org/utbot/framework/util/SizeUtils.kt | 3 +- ...rumentationContextAwareValueConstructor.kt | 8 +- 10 files changed, 109 insertions(+), 71 deletions(-) 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 32bc87d783..49698c5524 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 @@ -574,6 +574,10 @@ data class UtArrayModel( override fun hashCode(): Int = id } +interface UtModelWithOrigin { + val origin: UtCompositeModel? +} + /** * Model for complex objects with assemble instructions. * @@ -588,8 +592,8 @@ data class UtAssembleModel private constructor( override val modelName: String, val instantiationCall: UtStatementCallModel, val modificationsChain: List, - val origin: UtCompositeModel? -) : UtReferenceModel(id, classId, modelName) { + override val origin: UtCompositeModel? +) : UtReferenceModel(id, classId, modelName), UtModelWithOrigin { /** * Creates a new [UtAssembleModel]. @@ -731,11 +735,11 @@ class UtLambdaModel( * Common parent of all framework-specific models (e.g. Spring-specific models) */ abstract class UtCustomModel( - val origin: UtCompositeModel? = null, + override val origin: UtCompositeModel? = null, id: Int?, classId: ClassId, modelName: String = id.toString() -) : UtReferenceModel(id, classId, modelName) +) : UtReferenceModel(id, classId, modelName), UtModelWithOrigin object UtSpringContextModel : UtCustomModel( id = null, diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/constructor/ValueConstructor.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/constructor/ValueConstructor.kt index 0da6b2d027..c2fa773df7 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/constructor/ValueConstructor.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/constructor/ValueConstructor.kt @@ -20,6 +20,7 @@ import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtClassRefModel import org.utbot.framework.plugin.api.UtCompositeModel import org.utbot.framework.plugin.api.UtConcreteValue +import org.utbot.framework.plugin.api.UtCustomModel import org.utbot.framework.plugin.api.UtDirectSetFieldModel import org.utbot.framework.plugin.api.UtDirectGetFieldModel import org.utbot.framework.plugin.api.UtEnumConstantModel @@ -193,6 +194,10 @@ class ValueConstructor { is UtAssembleModel -> UtConcreteValue(constructFromAssembleModel(model)) is UtLambdaModel -> UtConcreteValue(constructFromLambdaModel(model)) is UtVoidModel -> UtConcreteValue(Unit) + is UtCustomModel -> UtConcreteValue( + constructObject(model.origin ?: error("Can't construct value for custom model without origin [$model]")), + model.classId.jClass + ) // Python, JavaScript are supposed to be here as well else -> throw UnsupportedOperationException("UtModel $model cannot construct UtConcreteValue") } diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/models/CompositeModelMinimizationChecker.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/models/CompositeModelMinimizationChecker.kt index 3a3d205b4a..b8b7d11c2d 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/models/CompositeModelMinimizationChecker.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/models/CompositeModelMinimizationChecker.kt @@ -6,6 +6,7 @@ import org.utbot.framework.plugin.api.UtCompositeModel import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtReferenceModel import org.junit.Test +import org.utbot.framework.plugin.api.UtCustomModel import org.utbot.testcheckers.eq import org.utbot.testing.UtModelTestCaseChecker @@ -17,6 +18,7 @@ internal class CompositeModelMinimizationChecker : UtModelTestCaseChecker( private fun UtModel.getFieldsOrNull(): Map? = when(this) { is UtAssembleModel -> origin?.fields is UtCompositeModel -> fields + is UtCustomModel -> origin?.fields else -> null } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt index aff569ba26..fe1b95a29d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt @@ -21,6 +21,7 @@ import org.utbot.framework.plugin.api.UtArrayModel import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtClassRefModel import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtCustomModel import org.utbot.framework.plugin.api.UtDirectGetFieldModel import org.utbot.framework.plugin.api.UtDirectSetFieldModel import org.utbot.framework.plugin.api.UtEnumConstantModel @@ -199,7 +200,8 @@ class AssembleModelGenerator(private val basePackageName: String) { is UtPrimitiveModel, is UtClassRefModel, is UtVoidModel, - is UtEnumConstantModel -> utModel + is UtEnumConstantModel, + is UtCustomModel -> utModel is UtLambdaModel -> assembleLambdaModel(utModel) is UtArrayModel -> assembleArrayModel(utModel) is UtCompositeModel -> assembleCompositeModel(utModel) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgClassFieldManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgClassFieldManager.kt index 15ffbc387b..9102daaf5e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgClassFieldManager.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgClassFieldManager.kt @@ -11,6 +11,7 @@ import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtCompositeModel import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtModelWithOrigin import org.utbot.framework.plugin.api.isMockModel import org.utbot.framework.plugin.api.util.SpringModelUtils.autowiredClassId import org.utbot.framework.plugin.api.util.SpringModelUtils.isAutowiredFromContext @@ -47,7 +48,7 @@ class CgInjectingMocksFieldsManager(val context: CgContext) : CgClassFieldManage override fun constructVariableForField(model: UtModel, modelVariable: CgValue): CgValue { val modelFields = when (model) { is UtCompositeModel -> model.fields - is UtAssembleModel -> model.origin?.fields + is UtModelWithOrigin -> model.origin?.fields else -> null } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt index 69a47e82ad..ee259b5ccd 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt @@ -89,6 +89,7 @@ import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtClassRefModel import org.utbot.framework.plugin.api.UtCompositeModel import org.utbot.framework.plugin.api.UtConcreteExecutionFailure +import org.utbot.framework.plugin.api.UtCustomModel import org.utbot.framework.plugin.api.UtDirectSetFieldModel import org.utbot.framework.plugin.api.UtEnumConstantModel import org.utbot.framework.plugin.api.UtExecution @@ -98,6 +99,7 @@ import org.utbot.framework.plugin.api.UtExecutionSuccess import org.utbot.framework.plugin.api.UtExplicitlyThrownException import org.utbot.framework.plugin.api.UtLambdaModel import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtModelWithOrigin import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation import org.utbot.framework.plugin.api.UtNullModel import org.utbot.framework.plugin.api.UtOverflowFailure @@ -321,6 +323,8 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte +thisInstance[executable](*methodArguments.toTypedArray()) } else { this.resultModel = resultModel + + // TODO support custom way of rendering asserts when `resultModel` is `UtCustomModel` val expected = variableConstructor.getOrCreateVariable(resultModel, "expected") emptyLineIfNeeded() assertEquality(expected, actual) @@ -755,43 +759,21 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte ) } } - is UtCompositeModel -> { - // Basically, to compare two iterables or maps, we need to iterate over them and compare each entry. - // But it leads to a lot of trash code in each test method, and it is more clear to use - // outer deep equals here - if (expected.isIterableOrMap()) { - currentBlock += CgSingleLineComment( - "${expected.type.canonicalName} is iterable or Map, use outer deep equals to iterate over" - ) - currentBlock += getDeepEqualsAssertion(expected, actual).toStatement() - - return - } - - // We can use overridden equals if we have one, but not for mocks. - if (expected.hasNotParametrizedCustomEquals() && !expectedModel.isMock) { - // We rely on already existing equals - currentBlock += CgSingleLineComment("${expected.type.canonicalName} has overridden equals method") - currentBlock += assertions[assertEquals](expected, actual).toStatement() - - return - } - - for ((fieldId, fieldModel) in expectedModel.fields) { - // we should not process enclosing class - // (actually, we do not do it properly anyway) - if (fieldId.isInnerClassEnclosingClassReference) continue - - traverseFieldRecursively( - fieldId, - fieldModel, - expected, - actual, - depth, - visitedModels - ) - } - } + is UtCompositeModel -> assertDeepEqualsForComposite( + expected = expected, + actual = actual, + expectedModel = expectedModel, + depth = depth, + visitedModels = visitedModels + ) + is UtCustomModel -> assertDeepEqualsForComposite( + expected = expected, + actual = actual, + expectedModel = expectedModel.origin + ?: error("Can't generate equals assertion for custom expected model without origin [$expectedModel]"), + depth = depth, + visitedModels = visitedModels + ) is UtLambdaModel -> Unit // we do not check equality of lambdas is UtVoidModel -> { // Unit result is considered in generateResultAssertions method @@ -802,6 +784,50 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte } } + private fun TestFrameworkManager.assertDeepEqualsForComposite( + expected: CgVariable, + actual: CgVariable, + expectedModel: UtCompositeModel, + depth: Int, + visitedModels: MutableSet + ) { + // Basically, to compare two iterables or maps, we need to iterate over them and compare each entry. + // But it leads to a lot of trash code in each test method, and it is more clear to use + // outer deep equals here + if (expected.isIterableOrMap()) { + currentBlock += CgSingleLineComment( + "${expected.type.canonicalName} is iterable or Map, use outer deep equals to iterate over" + ) + currentBlock += getDeepEqualsAssertion(expected, actual).toStatement() + + return + } + + // We can use overridden equals if we have one, but not for mocks. + if (expected.hasNotParametrizedCustomEquals() && !expectedModel.isMock) { + // We rely on already existing equals + currentBlock += CgSingleLineComment("${expected.type.canonicalName} has overridden equals method") + currentBlock += assertions[assertEquals](expected, actual).toStatement() + + return + } + + for ((fieldId, fieldModel) in expectedModel.fields) { + // we should not process enclosing class + // (actually, we do not do it properly anyway) + if (fieldId.isInnerClassEnclosingClassReference) continue + + traverseFieldRecursively( + fieldId, + fieldModel, + expected, + actual, + depth, + visitedModels + ) + } + } + private fun TestFrameworkManager.addArraysLengthAssertion( expected: CgVariable, actual: CgVariable, @@ -1041,18 +1067,10 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte private fun collectExecutionsResultFields() { for (model in successfulExecutionsModels) { when (model) { - is UtCompositeModel -> { - for ((fieldId, fieldModel) in model.fields) { - collectExecutionsResultFieldsRecursively(fieldId, fieldModel, 0) - } - } + is UtCompositeModel -> collectExecutionsResultFieldsRecursively(model, 0) - is UtAssembleModel -> { - model.origin?.let { - for ((fieldId, fieldModel) in it.fields) { - collectExecutionsResultFieldsRecursively(fieldId, fieldModel, 0) - } - } + is UtModelWithOrigin -> model.origin?.let { + collectExecutionsResultFieldsRecursively(it, 0) } // Lambdas do not have fields. They have captured values, but we do not consider them here. @@ -1070,6 +1088,12 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte } } + private fun collectExecutionsResultFieldsRecursively(model: UtCompositeModel, depth: Int) { + for ((fieldId, fieldModel) in model.fields) { + collectExecutionsResultFieldsRecursively(fieldId, fieldModel, depth) + } + } + private fun collectExecutionsResultFieldsRecursively( fieldId: FieldId, fieldModel: UtModel, @@ -1083,18 +1107,10 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte fieldsOfExecutionResults.getOrPut(fieldKey) { mutableListOf() } += fieldModel when (fieldModel) { - is UtCompositeModel -> { - for ((id, model) in fieldModel.fields) { - collectExecutionsResultFieldsRecursively(id, model, depth + 1) - } - } + is UtCompositeModel -> collectExecutionsResultFieldsRecursively(fieldModel, depth + 1) - is UtAssembleModel -> { - fieldModel.origin?.let { - for ((id, model) in it.fields) { - collectExecutionsResultFieldsRecursively(id, model, depth + 1) - } - } + is UtModelWithOrigin -> fieldModel.origin?.let { + collectExecutionsResultFieldsRecursively(it, depth + 1) } // Lambdas do not have fields. They have captured values, but we do not consider them here. diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt index 9514d2f1e9..72a49d5573 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt @@ -30,6 +30,7 @@ import org.utbot.framework.plugin.api.UtArrayModel import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtClassRefModel import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtCustomModel import org.utbot.framework.plugin.api.UtDirectGetFieldModel import org.utbot.framework.plugin.api.UtDirectSetFieldModel import org.utbot.framework.plugin.api.UtEnumConstantModel @@ -111,6 +112,7 @@ open class CgVariableConstructor(val context: CgContext) : is UtLambdaModel -> constructLambda(model, baseName) is UtNullModel -> nullLiteral() is UtPrimitiveModel -> CgLiteral(model.classId, model.value) + is UtCustomModel -> constructValueByModel(model.origin ?: error("Can't construct value for custom model without origin [$model]"), name) is UtReferenceModel -> error("Unexpected UtReferenceModel: ${model::class}") is UtVoidModel -> error("Unexpected UtVoidModel: ${model::class}") else -> error("Unexpected UtModel: ${model::class}") diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt index 93eb24b4f9..e8fcb7f8ab 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt @@ -15,6 +15,7 @@ import org.utbot.framework.plugin.api.UtEnumConstantModel import org.utbot.framework.plugin.api.UtExecution import org.utbot.framework.plugin.api.UtLambdaModel import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtModelWithOrigin import org.utbot.framework.plugin.api.UtNullModel import org.utbot.framework.plugin.api.UtPrimitiveModel import org.utbot.framework.plugin.api.UtReferenceModel @@ -105,7 +106,7 @@ class ExecutionStateAnalyzer(val execution: UtExecution) { var modelBefore = before if (before::class != after::class) { - if (before is UtAssembleModel && after is UtCompositeModel && before.origin != null) { + if (before is UtModelWithOrigin && after is UtModelWithOrigin && before.origin != null) { modelBefore = before.origin ?: unreachableBranch("We have already checked the origin for a null value") } else { doNotRun { @@ -113,8 +114,8 @@ class ExecutionStateAnalyzer(val execution: UtExecution) { // modelAfter (constructed by concrete executor) will consist all these fields, // therefore, AssembleModelGenerator won't be able to transform the given composite model - val reason = if (before is UtAssembleModel && after is UtCompositeModel) { - "ModelBefore is an AssembleModel and ModelAfter " + + val reason = if (before is UtModelWithOrigin && after is UtCompositeModel) { + "ModelBefore is an UtModelWithOrigin and ModelAfter " + "is a CompositeModel, but modelBefore doesn't have an origin model." } else { "The model before and the model after have different types: " + diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/util/SizeUtils.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/util/SizeUtils.kt index 2e3d238121..ebd0673203 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/util/SizeUtils.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/util/SizeUtils.kt @@ -5,6 +5,7 @@ import org.utbot.framework.plugin.api.UtArrayModel import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtClassRefModel import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtCustomModel import org.utbot.framework.plugin.api.UtDirectSetFieldModel import org.utbot.framework.plugin.api.UtEnumConstantModel import org.utbot.framework.plugin.api.UtLambdaModel @@ -37,7 +38,7 @@ private fun UtModel.calculateSize(used: MutableSet = mutableSetOf()): I return when (this) { is UtNullModel, is UtPrimitiveModel, UtVoidModel -> 0 - is UtClassRefModel, is UtEnumConstantModel, is UtArrayModel -> 1 + is UtClassRefModel, is UtEnumConstantModel, is UtArrayModel, is UtCustomModel -> 1 is UtAssembleModel -> { 1 + instantiationCall.calculateSize(used) + modificationsChain.sumOf { it.calculateSize(used) } } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/InstrumentationContextAwareValueConstructor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/InstrumentationContextAwareValueConstructor.kt index a53df612c9..dc95f98c0a 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/InstrumentationContextAwareValueConstructor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/InstrumentationContextAwareValueConstructor.kt @@ -16,6 +16,7 @@ import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtClassRefModel import org.utbot.framework.plugin.api.UtCompositeModel import org.utbot.framework.plugin.api.UtConcreteValue +import org.utbot.framework.plugin.api.UtCustomModel import org.utbot.framework.plugin.api.UtDirectGetFieldModel import org.utbot.framework.plugin.api.UtDirectSetFieldModel import org.utbot.framework.plugin.api.UtEnumConstantModel @@ -110,8 +111,11 @@ class InstrumentationContextAwareValueConstructor( is UtVoidModel -> UtConcreteValue(Unit) else -> { instrumentationContext.constructContextDependentValue(model) ?: - // PythonModel, JsUtModel may be here - throw UnsupportedOperationException("UtModel $model cannot construct UtConcreteValue") + if (model is UtCustomModel) + construct(model.origin ?: error("Can't construct value for custom model without origin [$model]")) + else + // PythonModel, JsUtModel may be here + throw UnsupportedOperationException("UtModel $model cannot construct UtConcreteValue") } } From 3257dea93d343cccafd3885e90535d8db7305c75 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 27 Jul 2023 15:06:43 +0300 Subject: [PATCH 10/19] Add `origin` to `UtSpringMockMvcResultActionsModel` --- .../org/utbot/framework/plugin/api/Api.kt | 2 ++ .../constructors/UtModelConstructor.kt | 22 ++++++++++++++----- .../UtMockMvcResultActionsModelConstructor.kt | 1 + 3 files changed, 19 insertions(+), 6 deletions(-) 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 49698c5524..5080756335 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 @@ -761,6 +761,7 @@ data class SpringRepositoryId( class UtSpringMockMvcResultActionsModel( id: Int?, + origin: UtCompositeModel?, val status: Int, val errorMessage: String?, val contentAsString: String, @@ -769,6 +770,7 @@ class UtSpringMockMvcResultActionsModel( val model: UtModel? // TODO add headers and other data ) : UtCustomModel( + origin = origin, classId = SpringModelUtils.resultActionsClassId, id = id, modelName = "mockMvcResultActions@$id" diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelConstructor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelConstructor.kt index 4bc5bd164f..f2950aa27f 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelConstructor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelConstructor.kt @@ -17,6 +17,13 @@ interface UtModelConstructorInterface { * Constructs a UtModel from a concrete [value] with a specific [classId]. */ fun construct(value: Any?, classId: ClassId): UtModel + + /** + * Constructs UtCompositeModel. + * + * Uses runtime javaClass to collect ALL fields, except final static fields, and builds this model recursively. + */ + fun constructCompositeModel(value: Any): UtCompositeModel } /** @@ -314,12 +321,12 @@ class UtModelConstructor( } } - /** - * Constructs UtCompositeModel. - * - * Uses runtime javaClass to collect ALL fields, except final static fields, and builds this model recursively. - */ - private fun constructCompositeModel(value: Any, remainingDepth: Long): UtModel { + override fun constructCompositeModel(value: Any): UtCompositeModel = constructCompositeModel( + value, + remainingDepth = maxDepth + ) + + private fun constructCompositeModel(value: Any, remainingDepth: Long): UtCompositeModel { // value can be mock only if it was previously constructed from UtCompositeModel val isMock = objectToModelCache[value]?.isMockModel() ?: false @@ -350,6 +357,9 @@ class UtModelConstructor( private fun withMaxDepth(newMaxDepth: Long) = object : UtModelConstructorInterface { override fun construct(value: Any?, classId: ClassId): UtModel = construct(value, classId, newMaxDepth) + + override fun constructCompositeModel(value: Any): UtCompositeModel = + constructCompositeModel(value, newMaxDepth) } } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt index 231c3f19e4..6064e8e88f 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt @@ -30,6 +30,7 @@ class UtMockMvcResultActionsModelConstructor : UtCustomModelConstructor { return UtSpringMockMvcResultActionsModel( id = id, + origin = internalConstructor.constructCompositeModel(value), status = responseGetStatusMethodId.method.invoke(response) as Int, errorMessage = responseGetErrorMessageMethodId.method.invoke(response) as String?, contentAsString = responseGetContentAsStringMethodId.method.invoke(response) as String, From 58cc415b1fd2db89f88ae3f32e64ae9d3242eada Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 27 Jul 2023 19:39:18 +0300 Subject: [PATCH 11/19] Split `CgMethodTestSet.executableId` into `executableUnderTest` and `executablesToCall` --- .../org/utbot/framework/plugin/api/Api.kt | 2 + .../codegen/domain/models/CgMethodTestSet.kt | 13 +++++-- .../builders/SimpleTestClassModelBuilder.kt | 4 +- .../codegen/reports/TestsGenerationReport.kt | 6 +-- .../CgAbstractSpringTestClassConstructor.kt | 4 +- .../tree/CgAbstractTestClassConstructor.kt | 4 +- .../codegen/tree/CgMethodConstructor.kt | 39 ++++++++++++------- .../tree/CgSimpleTestClassConstructor.kt | 24 ++++++++---- .../tree/CgSpringVariableConstructor.kt | 9 +++++ .../codegen/tree/CgVariableConstructor.kt | 2 +- .../constructor/tree/JsCgMethodConstructor.kt | 8 ++-- .../tree/PythonCgMethodConstructor.kt | 13 +++++-- 12 files changed, 84 insertions(+), 44 deletions(-) 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 5080756335..979f6c01c0 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 @@ -142,6 +142,8 @@ abstract class UtExecution( testMethodName: String? = this.testMethodName, displayName: String? = this.displayName, ): UtExecution + + val executableToCall get() = stateBefore.executableToCall } /** diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/CgMethodTestSet.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/CgMethodTestSet.kt index 03345a4e84..e6248d6a6e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/CgMethodTestSet.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/CgMethodTestSet.kt @@ -12,11 +12,15 @@ import org.utbot.framework.plugin.api.util.objectClassId import org.utbot.framework.plugin.api.util.voidClassId import org.utbot.fuzzer.UtFuzzedExecution -data class CgMethodTestSet constructor( - val executableId: ExecutableId, +data class CgMethodTestSet( + val executableUnderTest: ExecutableId, val errors: Map = emptyMap(), val clustersInfo: List>, ) { + val executablesToCall get() = + executions.map { it.executableToCall ?: executableUnderTest } + .distinctBy { it.classId to it.signature } + var executions: List = emptyList() private set @@ -55,8 +59,11 @@ data class CgMethodTestSet constructor( * * Tries to find a unique result type in testSets or * gets executable return type. + * + * Returns `null` if there are multiple distinct [executablesToCall]. */ - fun resultType(): ClassId { + fun getCommonResultTypeOrNull(): ClassId? { + val executableId = executablesToCall.singleOrNull() ?: return null return when (executableId.returnType) { voidClassId -> executableId.returnType else -> { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SimpleTestClassModelBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SimpleTestClassModelBuilder.kt index 4c43d8a62f..111ed3764a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SimpleTestClassModelBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SimpleTestClassModelBuilder.kt @@ -12,10 +12,10 @@ open class SimpleTestClassModelBuilder(context: CgContext): TestClassModelBuilde testSets: List, ): SimpleTestClassModel { // For each class stores list of methods declared in this class (methods from nested classes are excluded) - val class2methodTestSets = testSets.groupBy { it.executableId.classId } + val class2methodTestSets = testSets.groupBy { it.executableUnderTest.classId } val classesWithMethodsUnderTest = testSets - .map { it.executableId.classId } + .map { it.executableUnderTest.classId } .distinct() // For each class stores list of its "direct" nested classes diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/reports/TestsGenerationReport.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/reports/TestsGenerationReport.kt index 5933955616..7ca14aa3cd 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/reports/TestsGenerationReport.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/reports/TestsGenerationReport.kt @@ -53,12 +53,12 @@ data class TestsGenerationReport( } fun addMethodErrors(testSet: CgMethodTestSet, errors: Map) { - this.executables += testSet.executableId - this.errors[testSet.executableId] = errors + this.executables += testSet.executableUnderTest + this.errors[testSet.executableUnderTest] = errors } fun addTestsByType(testSet: CgMethodTestSet, testMethods: List) { - with(testSet.executableId) { + with(testSet.executableUnderTest) { executables += this testMethods.forEach { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractSpringTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractSpringTestClassConstructor.kt index d3135d297a..fe412a9c5d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractSpringTestClassConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractSpringTestClassConstructor.kt @@ -40,7 +40,7 @@ abstract class CgAbstractSpringTestClassConstructor(context: CgContext) : constructAdditionalTestMethods()?.let { methodRegions += it } for ((testSetIndex, testSet) in testClassModel.methodTestSets.withIndex()) { - updateExecutableUnderTest(testSet.executableId) + updateExecutableUnderTest(testSet.executableUnderTest) withTestSetIdScope(testSetIndex) { val currentMethodUnderTestRegions = constructTestSet(testSet) ?: return@withTestSetIdScope val executableUnderTestCluster = CgMethodsCluster( @@ -75,7 +75,7 @@ abstract class CgAbstractSpringTestClassConstructor(context: CgContext) : val errors = testSet.allErrors if (errors.isNotEmpty()) { - regions += methodConstructor.errorMethod(testSet.executableId, errors) + regions += methodConstructor.errorMethod(testSet, errors) testsGenerationReport.addMethodErrors(testSet, errors) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractTestClassConstructor.kt index 16ef9aff27..34d4d47c0b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractTestClassConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractTestClassConstructor.kt @@ -89,7 +89,7 @@ abstract class CgAbstractTestClassConstructor(val context: C testSet: CgMethodTestSet, regions: MutableList> ) { - val (methodUnderTest, _, clustersInfo) = testSet + val (_, _, clustersInfo) = testSet for ((clusterSummary, executionIndices) in clustersInfo) { val currentTestCaseTestMethods = mutableListOf() @@ -102,7 +102,7 @@ abstract class CgAbstractTestClassConstructor(val context: C for (i in checkedRange) { withExecutionIdScope(i) { - currentTestCaseTestMethods += methodConstructor.createTestMethod(methodUnderTest, testSet.executions[i]) + currentTestCaseTestMethods += methodConstructor.createTestMethod(testSet, testSet.executions[i]) } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt index ee259b5ccd..07f463499e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt @@ -1422,14 +1422,14 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte } } - open fun createTestMethod(executableId: ExecutableId, execution: UtExecution): CgTestMethod = + open fun createTestMethod(testSet: CgMethodTestSet, execution: UtExecution): CgTestMethod = withTestMethodScope(execution) { - val testMethodName = nameGenerator.testMethodNameFor(executableId, execution.testMethodName) + val testMethodName = nameGenerator.testMethodNameFor(testSet.executableUnderTest, execution.testMethodName) if (execution.testMethodName == null) { execution.testMethodName = testMethodName } // TODO: remove this line when SAT-1273 is completed - execution.displayName = execution.displayName?.let { "${executableId.name}: $it" } + execution.displayName = execution.displayName?.let { "${testSet.executableUnderTest.name}: $it" } testMethod(testMethodName, execution.displayName) { //Enum constants are static, but there is no need to store and recover value for them val statics = currentExecution!!.stateBefore @@ -1451,7 +1451,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte } // build arguments for ((index, param) in execution.stateBefore.parameters.withIndex()) { - val name = paramNames[executableId]?.get(index) + val name = paramNames[execution.executableToCall ?: testSet.executableUnderTest]?.get(index) methodArguments += variableConstructor.getOrCreateVariable(param, name) } @@ -1524,7 +1524,11 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte private val expectedResultVarName = "expectedResult" private val expectedErrorVarName = "expectedError" - fun createParameterizedTestMethod(testSet: CgMethodTestSet, dataProviderMethodName: String): CgTestMethod { + /** + * Returns `null` if parameterized test method can't be created for [testSet] + * (for example, when there are multiple distinct [CgMethodTestSet.executablesToCall]). + */ + fun createParameterizedTestMethod(testSet: CgMethodTestSet, dataProviderMethodName: String): CgTestMethod? { //TODO: orientation on generic execution may be misleading, but what is the alternative? //may be a heuristic to select a model with minimal number of internal nulls should be used val genericExecution = chooseGenericExecution(testSet.executions) @@ -1540,7 +1544,8 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte return withTestMethodScope(genericExecution) { val testName = nameGenerator.parameterizedTestMethodName(dataProviderMethodName) withNameScope { - val testParameterDeclarations = createParameterDeclarations(testSet, genericExecution) + val testParameterDeclarations = + createParameterDeclarations(testSet, genericExecution) ?: return@withNameScope null methodType = PARAMETRIZED testMethod( @@ -1610,12 +1615,16 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte .firstOrNull { it.result is UtExecutionSuccess } ?: executions.first() } + /** + * Returns `null` if parameter declarations can't be created for [testSet] + * (for example, when there are multiple distinct [CgMethodTestSet.executablesToCall]). + */ private fun createParameterDeclarations( testSet: CgMethodTestSet, genericExecution: UtExecution, - ): List { - val executableUnderTest = testSet.executableId - val executableUnderTestParameters = testSet.executableId.executable.parameters + ): List? { + val executableToCall = testSet.executablesToCall.singleOrNull() ?: return null + val executableUnderTestParameters = executableToCall.executable.parameters return mutableListOf().apply { // this instance @@ -1634,7 +1643,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte // arguments for (index in genericExecution.stateBefore.parameters.indices) { - val argumentName = paramNames[executableUnderTest]?.get(index) + val argumentName = paramNames[executableToCall]?.get(index) val paramType = executableUnderTestParameters[index].parameterizedType val argumentType = when { @@ -1670,7 +1679,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte } } - val expectedResultClassId = wrapTypeIfRequired(testSet.resultType()) + val expectedResultClassId = wrapTypeIfRequired(testSet.getCommonResultTypeOrNull() ?: return null) if (expectedResultClassId != voidClassId) { val wrappedType = wrapIfPrimitive(expectedResultClassId) //We are required to wrap the type of expected result if it is primitive @@ -1748,7 +1757,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte } for ((paramIndex, paramModel) in execution.stateBefore.parameters.withIndex()) { - val argumentName = paramNames[testSet.executableId]?.get(paramIndex) + val argumentName = paramNames[execution.executableToCall ?: testSet.executableUnderTest]?.get(paramIndex) arguments += variableConstructor.getOrCreateVariable(paramModel, argumentName) } @@ -1951,8 +1960,8 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte } } - fun errorMethod(executable: ExecutableId, errors: Map): CgRegion { - val name = nameGenerator.errorMethodNameFor(executable) + fun errorMethod(testSet: CgMethodTestSet, errors: Map): CgRegion { + val name = nameGenerator.errorMethodNameFor(testSet.executableUnderTest) val body = block { comment("Couldn't generate some tests. List of errors:") comment() @@ -1980,7 +1989,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte } } val errorTestMethod = CgErrorTestMethod(name, body) - return CgSimpleRegion("Errors report for ${executable.name}", listOf(errorTestMethod)) + return CgSimpleRegion("Errors report for ${testSet.executableUnderTest.name}", listOf(errorTestMethod)) } private fun CgExecutableCall.wrapReflectiveCall() { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt index 10aa7739d0..f7207630e4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt @@ -50,7 +50,7 @@ open class CgSimpleTestClassConstructor(context: CgContext): CgAbstractTestClass } for ((testSetIndex, testSet) in notYetConstructedTestSets.withIndex()) { - updateExecutableUnderTest(testSet.executableId) + updateExecutableUnderTest(testSet.executableUnderTest) withTestSetIdScope(testSetIndex) { val currentMethodUnderTestRegions = constructTestSet(testSet) ?: return@withTestSetIdScope val executableUnderTestCluster = CgMethodsCluster( @@ -96,14 +96,16 @@ open class CgSimpleTestClassConstructor(context: CgContext): CgAbstractTestClass createParametrizedTestAndDataProvider( testSet, regions - ) + ).let { parametrizedTestCreatedSuccessfully -> + if (!parametrizedTestCreatedSuccessfully) createTest(testSet, regions) + } } }.onFailure { e -> processFailure(testSet, e) } } val errors = testSet.allErrors if (errors.isNotEmpty()) { - regions += methodConstructor.errorMethod(testSet.executableId, errors) + regions += methodConstructor.errorMethod(testSet, errors) testsGenerationReport.addMethodErrors(testSet, errors) } @@ -119,17 +121,21 @@ open class CgSimpleTestClassConstructor(context: CgContext): CgAbstractTestClass return testSets } + /** + * Returns `false` if parameterized test method can't be created for [testSet] + * (for example, when there are multiple distinct [CgMethodTestSet.executablesToCall]). + */ private fun createParametrizedTestAndDataProvider( testSet: CgMethodTestSet, regions: MutableList> - ) { + ) : Boolean { val (methodUnderTest, _, _) = testSet for (preparedTestSet in testSet.prepareTestSetsForParameterizedTestGeneration()) { - val dataProviderMethodName = nameGenerator.dataProviderMethodNameFor(preparedTestSet.executableId) + val dataProviderMethodName = nameGenerator.dataProviderMethodNameFor(preparedTestSet.executableUnderTest) val parameterizedTestMethod = - methodConstructor.createParameterizedTestMethod(preparedTestSet, dataProviderMethodName) + methodConstructor.createParameterizedTestMethod(preparedTestSet, dataProviderMethodName) ?: return false testFrameworkManager.addDataProvider( methodConstructor.createParameterizedTestDataProvider(preparedTestSet, dataProviderMethodName) @@ -150,6 +156,8 @@ open class CgSimpleTestClassConstructor(context: CgContext): CgAbstractTestClass "FUZZER: Tests for method ${methodUnderTest.humanReadableName} that cannot be presented as parameterized", collectFuzzerTestsForParameterizedMode(testSet), ) + + return true } /** @@ -164,7 +172,7 @@ open class CgSimpleTestClassConstructor(context: CgContext): CgAbstractTestClass .withIndex() .forEach { (index, execution) -> withExecutionIdScope(index) { - testMethods += methodConstructor.createTestMethod(testSet.executableId, execution) + testMethods += methodConstructor.createTestMethod(testSet, execution) } } @@ -184,7 +192,7 @@ open class CgSimpleTestClassConstructor(context: CgContext): CgAbstractTestClass .withIndex() .forEach { (index, execution) -> withExecutionIdScope(index) { - testMethods += methodConstructor.createTestMethod(testSet.executableId, execution) + testMethods += methodConstructor.createTestMethod(testSet, execution) } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt index f39de8d517..47fe59555f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt @@ -7,6 +7,7 @@ import org.utbot.framework.codegen.domain.models.CgValue import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtSpringContextModel +import org.utbot.framework.plugin.api.UtSpringMockMvcResultActionsModel import org.utbot.framework.plugin.api.util.stringClassId class CgSpringVariableConstructor(context: CgContext) : CgVariableConstructor(context) { @@ -35,4 +36,12 @@ class CgSpringVariableConstructor(context: CgContext) : CgVariableConstructor(co valueByUtModelWrapper[UtSpringContextModel.wrap()] = it } } + + override fun constructValueByModel(model: UtModel, name: String?): CgValue = when (model) { + is UtSpringMockMvcResultActionsModel -> CgLiteral( + model.classId, + "UtSpringMockMvcResult(${model.status}, ${model.errorMessage}, ${model.contentAsString}, ${model.viewName})" + ) + else -> super.constructValueByModel(model, name) + } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt index 72a49d5573..c78749d873 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt @@ -99,7 +99,7 @@ open class CgVariableConstructor(val context: CgContext) : constructValueByModel(model, name) } - private fun constructValueByModel(model: UtModel, name: String?): CgValue { + open fun constructValueByModel(model: UtModel, name: String?): CgValue { // name could be taken from existing names, or be specified manually, or be created from generator val baseName = name ?: nameGenerator.nameFrom(model.classId) diff --git a/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt b/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt index fbed241d3d..f107d984cf 100644 --- a/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt +++ b/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt @@ -22,10 +22,10 @@ class JsCgMethodConstructor(ctx: CgContext) : CgMethodConstructor(ctx) { testFrameworkManager.assertEquals(expected, actual) } - override fun createTestMethod(executableId: ExecutableId, execution: UtExecution): CgTestMethod = + override fun createTestMethod(testSet: CgMethodTestSet, execution: UtExecution): CgTestMethod = withTestMethodScope(execution) { - val testMethodName = nameGenerator.testMethodNameFor(executableId, execution.testMethodName) - execution.displayName = execution.displayName?.let { "${executableId.name}: $it" } + val testMethodName = nameGenerator.testMethodNameFor(testSet.executableUnderTest, execution.testMethodName) + execution.displayName = execution.displayName?.let { "${testSet.executableUnderTest.name}: $it" } testMethod(testMethodName, execution.displayName) { val statics = currentExecution!!.stateBefore.statics rememberInitialStaticFields(statics) @@ -37,7 +37,7 @@ class JsCgMethodConstructor(ctx: CgContext) : CgMethodConstructor(ctx) { } // build arguments for ((index, param) in execution.stateBefore.parameters.withIndex()) { - val name = paramNames[executableId]?.get(index) + val name = paramNames[execution.executableToCall ?: testSet.executableUnderTest]?.get(index) methodArguments += variableConstructor.getOrCreateVariable(param, name) } recordActualResult() diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt index 72756aa197..0dd9c5a816 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt @@ -6,6 +6,7 @@ import org.utbot.framework.codegen.domain.models.CgDocumentationComment import org.utbot.framework.codegen.domain.models.CgFieldAccess import org.utbot.framework.codegen.domain.models.CgGetLength import org.utbot.framework.codegen.domain.models.CgLiteral +import org.utbot.framework.codegen.domain.models.CgMethodTestSet import org.utbot.framework.codegen.domain.models.CgMultilineComment import org.utbot.framework.codegen.domain.models.CgParameterDeclaration import org.utbot.framework.codegen.domain.models.CgReferenceExpression @@ -38,17 +39,17 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex pythonDeepEquals(expected, actual) } - override fun createTestMethod(executableId: ExecutableId, execution: UtExecution): CgTestMethod = + override fun createTestMethod(testSet: CgMethodTestSet, execution: UtExecution): CgTestMethod = withTestMethodScope(execution) { val constructorState = (execution as PythonUtExecution).stateInit val diffIds = execution.diffIds (context.cgLanguageAssistant as PythonCgLanguageAssistant).clear() - val testMethodName = nameGenerator.testMethodNameFor(executableId, execution.testMethodName) + val testMethodName = nameGenerator.testMethodNameFor(testSet.executableUnderTest, execution.testMethodName) if (execution.testMethodName == null) { execution.testMethodName = testMethodName } // TODO: remove this line when SAT-1273 is completed - execution.displayName = execution.displayName?.let { "${executableId.name}: $it" } + execution.displayName = execution.displayName?.let { "${testSet.executableUnderTest.name}: $it" } pythonTestMethod(testMethodName, execution.displayName) { val statics = currentExecution!!.stateBefore.statics rememberInitialStaticFields(statics) @@ -122,7 +123,11 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex generateResultAssertions() if (methodType == CgTestMethodType.PASSED_EXCEPTION) { - generateFieldStateAssertions(stateAssertions, assertThisObject, executableId) + generateFieldStateAssertions( + stateAssertions, + assertThisObject, + execution.executableToCall ?: testSet.executableUnderTest + ) } } From 6107f0e1e2ae6485bb13c9d80e76903e59a7281f Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 27 Jul 2023 19:42:22 +0300 Subject: [PATCH 12/19] Enable `UtMockMvcResultActionsModelConstructor` for subtypes of `ResultActions` --- .../instrumentation/spring/SpringInstrumentationContext.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt index ebe91dfc44..fe24cdfc9a 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt @@ -7,6 +7,7 @@ import org.utbot.framework.plugin.api.UtConcreteValue import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtSpringContextModel import org.utbot.framework.plugin.api.util.SpringModelUtils.resultActionsClassId +import org.utbot.framework.plugin.api.util.isSubtypeOf import org.utbot.framework.plugin.api.util.utContext import org.utbot.instrumentation.instrumentation.execution.constructors.UtCustomModelConstructor import org.utbot.instrumentation.instrumentation.execution.context.InstrumentationContext @@ -49,6 +50,6 @@ class SpringInstrumentationContext( } override fun findUtCustomModelConstructor(classId: ClassId): UtCustomModelConstructor? = - if (classId == resultActionsClassId) UtMockMvcResultActionsModelConstructor() + if (classId.isSubtypeOf(resultActionsClassId)) UtMockMvcResultActionsModelConstructor() else delegateInstrumentationContext.findUtCustomModelConstructor(classId) } \ No newline at end of file From d5529eecd0c5e712c5bc9e8f049371f3e7bf3a05 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Fri, 28 Jul 2023 12:59:57 +0300 Subject: [PATCH 13/19] Add `CgCustomAssertConstructor` to `CgComponents` --- .../services/language/CgLanguageAssistant.kt | 8 +++++ .../framework/codegen/tree/CgComponents.kt | 6 ++++ .../codegen/tree/CgCustomAssertConstructor.kt | 19 ++++++++++++ .../codegen/tree/CgMethodConstructor.kt | 29 ++++++++++++++----- .../constructor/tree/JsCgMethodConstructor.kt | 3 +- .../tree/PythonCgMethodConstructor.kt | 16 ++++++---- 6 files changed, 65 insertions(+), 16 deletions(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgCustomAssertConstructor.kt diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/CgLanguageAssistant.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/CgLanguageAssistant.kt index 9efa0e1996..df92c641f3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/CgLanguageAssistant.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/CgLanguageAssistant.kt @@ -11,7 +11,9 @@ import org.utbot.framework.codegen.services.access.CgCallableAccessManager import org.utbot.framework.codegen.services.access.CgCallableAccessManagerImpl import org.utbot.framework.codegen.services.access.CgFieldStateManager import org.utbot.framework.codegen.services.access.CgFieldStateManagerImpl +import org.utbot.framework.codegen.tree.CgCustomAssertConstructor import org.utbot.framework.codegen.tree.CgMethodConstructor +import org.utbot.framework.codegen.tree.CgSimpleCustomAssertConstructor import org.utbot.framework.codegen.tree.CgStatementConstructor import org.utbot.framework.codegen.tree.CgStatementConstructorImpl import org.utbot.framework.codegen.tree.CgVariableConstructor @@ -47,6 +49,9 @@ interface CgLanguageAssistant { fun getVariableConstructorBy(context: CgContext): CgVariableConstructor fun getMethodConstructorBy(context: CgContext): CgMethodConstructor + + fun getCustomAssertConstructorBy(context: CgContext): CgCustomAssertConstructor + fun getCgFieldStateManager(context: CgContext): CgFieldStateManager fun getLanguageTestFrameworkManager(): LanguageTestFrameworkManager @@ -65,4 +70,7 @@ abstract class AbstractCgLanguageAssistant : CgLanguageAssistant { override fun getMethodConstructorBy(context: CgContext): CgMethodConstructor = CgMethodConstructor(context) override fun getCgFieldStateManager(context: CgContext): CgFieldStateManager = CgFieldStateManagerImpl(context) + + override fun getCustomAssertConstructorBy(context: CgContext): CgCustomAssertConstructor = + CgSimpleCustomAssertConstructor(context) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgComponents.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgComponents.kt index 8940a73bdd..5b9876f1ef 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgComponents.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgComponents.kt @@ -22,6 +22,7 @@ object CgComponents { mockFrameworkManagers.clear() variableConstructors.clear() methodConstructors.clear() + customAssertConstructors.clear() } private val nameGenerators: IdentityHashMap = IdentityHashMap() @@ -33,6 +34,7 @@ object CgComponents { private val statementConstructors: IdentityHashMap = IdentityHashMap() private val variableConstructors: IdentityHashMap = IdentityHashMap() private val methodConstructors: IdentityHashMap = IdentityHashMap() + private val customAssertConstructors: IdentityHashMap = IdentityHashMap() fun getNameGeneratorBy(context: CgContext): CgNameGenerator = nameGenerators.getOrPut(context) { context.cgLanguageAssistant.getNameGeneratorBy(context) @@ -62,4 +64,8 @@ object CgComponents { fun getMethodConstructorBy(context: CgContext): CgMethodConstructor = methodConstructors.getOrPut(context) { context.cgLanguageAssistant.getMethodConstructorBy(context) } + + fun getCustomAssertConstructorBy(context: CgContext): CgCustomAssertConstructor = customAssertConstructors.getOrPut(context) { + context.cgLanguageAssistant.getCustomAssertConstructorBy(context) + } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgCustomAssertConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgCustomAssertConstructor.kt new file mode 100644 index 0000000000..86dc612905 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgCustomAssertConstructor.kt @@ -0,0 +1,19 @@ +package org.utbot.framework.codegen.tree + +import org.utbot.framework.codegen.domain.context.CgContext +import org.utbot.framework.codegen.domain.context.CgContextOwner +import org.utbot.framework.codegen.domain.models.CgVariable +import org.utbot.framework.plugin.api.UtCustomModel + +interface CgCustomAssertConstructor { + val context: CgContext + fun tryConstructCustomAssert(expected: UtCustomModel, actual: CgVariable): Boolean +} + +class CgSimpleCustomAssertConstructor( + override val context: CgContext +) : CgCustomAssertConstructor, + CgContextOwner by context { + override fun tryConstructCustomAssert(expected: UtCustomModel, actual: CgVariable): Boolean = + false +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt index 07f463499e..54b7a9c9d7 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt @@ -57,6 +57,7 @@ import org.utbot.framework.codegen.services.access.CgCallableAccessManager import org.utbot.framework.codegen.services.access.CgFieldStateManagerImpl import org.utbot.framework.codegen.services.framework.TestFrameworkManager import org.utbot.framework.codegen.tree.CgComponents.getCallableAccessManagerBy +import org.utbot.framework.codegen.tree.CgComponents.getCustomAssertConstructorBy import org.utbot.framework.codegen.tree.CgComponents.getMockFrameworkManagerBy import org.utbot.framework.codegen.tree.CgComponents.getNameGeneratorBy import org.utbot.framework.codegen.tree.CgComponents.getStatementConstructorBy @@ -175,6 +176,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte protected val testFrameworkManager = getTestFrameworkManagerBy(context) protected val variableConstructor = getVariableConstructorBy(context) + private val customAssertConstructor = getCustomAssertConstructorBy(context) private val mockFrameworkManager = getMockFrameworkManagerBy(context) private val floatDelta: Float = 1e-6f @@ -323,11 +325,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte +thisInstance[executable](*methodArguments.toTypedArray()) } else { this.resultModel = resultModel - - // TODO support custom way of rendering asserts when `resultModel` is `UtCustomModel` - val expected = variableConstructor.getOrCreateVariable(resultModel, "expected") - emptyLineIfNeeded() - assertEquality(expected, actual) + assertEquality(resultModel, actual, emptyLineIfNeeded = true) } } .onFailure { exception -> processExecutionFailure(exception, executionResult) } @@ -573,9 +571,11 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte } if (afterModel !is UtReferenceModel) { - val expectedAfter = - variableConstructor.getOrCreateVariable(afterModel, "expected" + afterVariable.name.capitalize()) - assertEquality(expectedAfter, afterVariable) + assertEquality( + expected = afterModel, + actual = afterVariable, + expectedVariableName = "expected" + afterVariable.name.capitalize() + ) } else { if (beforeVariable != null) testFrameworkManager.assertBoolean(false, beforeVariable equalTo afterVariable) @@ -1195,6 +1195,19 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte return ClassIdArrayInfo(classId, nestedElementClassId, dimensions) } + fun assertEquality( + expected: UtModel, + actual: CgVariable, + expectedVariableName: String = "expected", + emptyLineIfNeeded: Boolean = false, + ) { + if (expected !is UtCustomModel || !customAssertConstructor.tryConstructCustomAssert(expected, actual)) { + val expectedVariable = variableConstructor.getOrCreateVariable(expected, expectedVariableName) + if (emptyLineIfNeeded) emptyLineIfNeeded() + assertEquality(expectedVariable, actual) + } + } + open fun assertEquality(expected: CgValue, actual: CgVariable) { when { expected.type.isArray -> { diff --git a/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt b/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt index f107d984cf..4334eb49e0 100644 --- a/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt +++ b/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt @@ -69,8 +69,7 @@ class JsCgMethodConstructor(ctx: CgContext) : CgMethodConstructor(ctx) { +thisInstance[method](*methodArguments.toTypedArray()) } else { resultModel = result - val expected = variableConstructor.getOrCreateVariable(result, "expected") - assertEquality(expected, actual) + assertEquality(result, actual) } } .onFailure { exception -> diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt index 0dd9c5a816..1325e9ed23 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt @@ -190,17 +190,21 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex emptyLineIfNeeded() } stateAssertions.forEach { (index, it) -> - val name = paramNames[executableId]?.get(index) + "_modified" - val modifiedArgument = variableConstructor.getOrCreateVariable(it.second, name) - assertEquality(modifiedArgument, it.first) + assertEquality( + expected = it.second, + actual = it.first, + expectedVariableName = paramNames[executableId]?.get(index) + "_modified" + ) } if (assertThisObject.isNotEmpty()) { emptyLineIfNeeded() } assertThisObject.forEach { - val name = it.first.name + "_modified" - val modifiedThisObject = variableConstructor.getOrCreateVariable(it.second, name) - assertEquality(modifiedThisObject, it.first) + assertEquality( + expected = it.second, + actual = it.first, + expectedVariableName = it.first.name + "_modified" + ) } } From 6790e71195dd9ab9377c27276eb48e882cab72ff Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Fri, 28 Jul 2023 13:11:13 +0300 Subject: [PATCH 14/19] Add custom asserts for `MockMvc.perform()` result --- .../plugin/api/util/SpringModelUtils.kt | 71 +++++++++++++++++++ .../codegen/domain/models/CgElement.kt | 2 +- .../codegen/generator/SpringCodeGenerator.kt | 6 ++ ...CgMockMvcResultActionsAssertConstructor.kt | 55 ++++++++++++++ 4 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMockMvcResultActionsAssertConstructor.kt diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt index 48328d7985..b5f5e71c96 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt @@ -24,6 +24,7 @@ object SpringModelUtils { val dirtiesContextClassModeClassId = ClassId("org.springframework.test.annotation.DirtiesContext\$ClassMode") val transactionalClassId = ClassId("org.springframework.transaction.annotation.Transactional") val autoConfigureTestDbClassId = ClassId("org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase") + val autoConfigureMockMvcClassId = ClassId("org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc") val runWithClassId = ClassId("org.junit.runner.RunWith") val springRunnerClassId = ClassId("org.springframework.test.context.junit4.SpringRunner") @@ -110,6 +111,13 @@ object SpringModelUtils { val resultActionsClassId = ClassId("org.springframework.test.web.servlet.ResultActions") private val mockMvcClassId = ClassId("org.springframework.test.web.servlet.MockMvc") private val mvcResultClassId = ClassId("org.springframework.test.web.servlet.MvcResult") + private val resultHandlerClassId = ClassId("org.springframework.test.web.servlet.ResultHandler") + val mockMvcResultHandlersClassId = ClassId("org.springframework.test.web.servlet.result.MockMvcResultHandlers") + private val resultMatcherClassId = ClassId("org.springframework.test.web.servlet.ResultMatcher") + val mockMvcResultMatchersClassId = ClassId("org.springframework.test.web.servlet.result.MockMvcResultMatchers") + private val statusResultMatchersClassId = ClassId("org.springframework.test.web.servlet.result.StatusResultMatchers") + private val contentResultMatchersClassId = ClassId("org.springframework.test.web.servlet.result.ContentResultMatchers") + private val viewResultMatchersClassId = ClassId("org.springframework.test.web.servlet.result.ViewResultMatchers") private val mockHttpServletRequestBuilderClassId = ClassId("org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder") private val modelAndViewClassId = ClassId("org.springframework.web.servlet.ModelAndView") @@ -178,6 +186,69 @@ object SpringModelUtils { returnType = stringClassId ) + val resultActionsAndDoMethodId = MethodId( + classId = resultActionsClassId, + name = "andDo", + parameters = listOf(resultHandlerClassId), + returnType = resultActionsClassId + ) + + val resultHandlersPrintMethodId = MethodId( + classId = mockMvcResultHandlersClassId, + name = "print", + parameters = listOf(), + returnType = resultHandlerClassId + ) + + val resultActionsAndExpectMethodId = MethodId( + classId = resultActionsClassId, + name = "andExpect", + parameters = listOf(resultMatcherClassId), + returnType = resultActionsClassId + ) + + val resultMatchersStatusMethodId = MethodId( + classId = mockMvcResultMatchersClassId, + name = "status", + parameters = listOf(), + returnType = statusResultMatchersClassId + ) + + val statusMatchersIsMethodId = MethodId( + classId = statusResultMatchersClassId, + name = "is", + parameters = listOf(intClassId), + returnType = resultMatcherClassId + ) + + val resultMatchersContentMethodId = MethodId( + classId = mockMvcResultMatchersClassId, + name = "content", + parameters = listOf(), + returnType = contentResultMatchersClassId + ) + + val contentMatchersStringMethodId = MethodId( + classId = contentResultMatchersClassId, + name = "string", + parameters = listOf(stringClassId), + returnType = resultMatcherClassId + ) + + val resultMatchersViewMethodId = MethodId( + classId = contentResultMatchersClassId, + name = "view", + parameters = listOf(), + returnType = viewResultMatchersClassId + ) + + val viewMatchersNameMethodId = MethodId( + classId = viewResultMatchersClassId, + name = "name", + parameters = listOf(stringClassId), + returnType = resultMatcherClassId + ) + fun createMockMvcModel(idGenerator: () -> Int) = createBeanModel("mockMvc", idGenerator(), mockMvcClassId) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/CgElement.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/CgElement.kt index a054d4fe1d..c143347adc 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/CgElement.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/CgElement.kt @@ -405,7 +405,7 @@ class CgSingleArgAnnotation( * NOTE: use `StatementConstructor.addAnnotation` * instead of explicit constructor call. */ -class CgMultipleArgsAnnotation( +data class CgMultipleArgsAnnotation( override val classId: ClassId, val arguments: MutableList, override val target: AnnotationTarget, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/SpringCodeGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/SpringCodeGenerator.kt index 937c841a26..67be83d0b5 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/SpringCodeGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/SpringCodeGenerator.kt @@ -4,11 +4,13 @@ import org.utbot.framework.codegen.domain.context.CgContext import org.utbot.framework.codegen.domain.models.CgMethodTestSet import org.utbot.framework.codegen.domain.models.builders.SpringTestClassModelBuilder import org.utbot.framework.codegen.services.language.CgLanguageAssistant +import org.utbot.framework.codegen.tree.CgCustomAssertConstructor import org.utbot.framework.codegen.tree.CgSpringIntegrationTestClassConstructor import org.utbot.framework.codegen.tree.CgSpringUnitTestClassConstructor import org.utbot.framework.codegen.tree.CgSpringVariableConstructor import org.utbot.framework.codegen.tree.CgVariableConstructor import org.utbot.framework.codegen.tree.ututils.UtilClassKind +import org.utbot.framework.codegen.tree.withCustomAssertForMockMvcResultActions import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ConcreteContextLoadingResult import org.utbot.framework.plugin.api.SpringSettings @@ -27,6 +29,10 @@ class SpringCodeGenerator( override fun getVariableConstructorBy(context: CgContext): CgVariableConstructor = // TODO decorate original `params.cgLanguageAssistant.getVariableConstructorBy(context)` CgSpringVariableConstructor(context) + + override fun getCustomAssertConstructorBy(context: CgContext): CgCustomAssertConstructor = + params.cgLanguageAssistant.getCustomAssertConstructorBy(context) + .withCustomAssertForMockMvcResultActions() } ) ) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMockMvcResultActionsAssertConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMockMvcResultActionsAssertConstructor.kt new file mode 100644 index 0000000000..d40e359164 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMockMvcResultActionsAssertConstructor.kt @@ -0,0 +1,55 @@ +package org.utbot.framework.codegen.tree + +import org.utbot.framework.codegen.domain.context.CgContext +import org.utbot.framework.codegen.domain.context.CgContextOwner +import org.utbot.framework.codegen.domain.models.AnnotationTarget +import org.utbot.framework.codegen.domain.models.CgVariable +import org.utbot.framework.codegen.services.access.CgCallableAccessManager +import org.utbot.framework.codegen.services.access.CgCallableAccessManagerImpl +import org.utbot.framework.codegen.tree.CgComponents.getStatementConstructorBy +import org.utbot.framework.plugin.api.UtCustomModel +import org.utbot.framework.plugin.api.UtSpringMockMvcResultActionsModel +import org.utbot.framework.plugin.api.util.SpringModelUtils.autoConfigureMockMvcClassId +import org.utbot.framework.plugin.api.util.SpringModelUtils.contentMatchersStringMethodId +import org.utbot.framework.plugin.api.util.SpringModelUtils.mockMvcResultHandlersClassId +import org.utbot.framework.plugin.api.util.SpringModelUtils.mockMvcResultMatchersClassId +import org.utbot.framework.plugin.api.util.SpringModelUtils.resultActionsAndDoMethodId +import org.utbot.framework.plugin.api.util.SpringModelUtils.resultActionsAndExpectMethodId +import org.utbot.framework.plugin.api.util.SpringModelUtils.resultHandlersPrintMethodId +import org.utbot.framework.plugin.api.util.SpringModelUtils.resultMatchersContentMethodId +import org.utbot.framework.plugin.api.util.SpringModelUtils.resultMatchersStatusMethodId +import org.utbot.framework.plugin.api.util.SpringModelUtils.resultMatchersViewMethodId +import org.utbot.framework.plugin.api.util.SpringModelUtils.statusMatchersIsMethodId +import org.utbot.framework.plugin.api.util.SpringModelUtils.viewMatchersNameMethodId + +fun CgCustomAssertConstructor.withCustomAssertForMockMvcResultActions() = + CgMockMvcResultActionsAssertConstructor(context, this) + +class CgMockMvcResultActionsAssertConstructor( + context: CgContext, + private val delegateAssertConstructor: CgCustomAssertConstructor +) : CgCustomAssertConstructor by delegateAssertConstructor, + CgContextOwner by context, + CgStatementConstructor by getStatementConstructorBy(context), + CgCallableAccessManager by CgCallableAccessManagerImpl(context) { + override fun tryConstructCustomAssert(expected: UtCustomModel, actual: CgVariable): Boolean { + if (expected is UtSpringMockMvcResultActionsModel) { + addAnnotation(autoConfigureMockMvcClassId, AnnotationTarget.Class) + var expr = actual[resultActionsAndDoMethodId](mockMvcResultHandlersClassId[resultHandlersPrintMethodId]()) + expr = expr[resultActionsAndExpectMethodId]( + mockMvcResultMatchersClassId[resultMatchersStatusMethodId]()[statusMatchersIsMethodId](expected.status) + ) + expected.viewName?.let { viewName -> + expr = expr[resultActionsAndExpectMethodId]( + mockMvcResultMatchersClassId[resultMatchersViewMethodId]()[viewMatchersNameMethodId](viewName) + ) + } + expr = expr[resultActionsAndExpectMethodId]( + mockMvcResultMatchersClassId[resultMatchersContentMethodId]()[contentMatchersStringMethodId](expected.contentAsString) + ) + +expr + return true + } else + return delegateAssertConstructor.tryConstructCustomAssert(expected, actual) + } +} \ No newline at end of file From 6cc441ef2bb016e4594ad83f87d78cfc5458733b Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Fri, 28 Jul 2023 14:45:04 +0300 Subject: [PATCH 15/19] Remove redundant code --- .../plugin/api/util/SpringModelUtils.kt | 35 ------------------- .../tree/CgSpringVariableConstructor.kt | 9 ----- 2 files changed, 44 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt index b5f5e71c96..dc826f0e60 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt @@ -252,41 +252,6 @@ object SpringModelUtils { fun createMockMvcModel(idGenerator: () -> Int) = createBeanModel("mockMvc", idGenerator(), mockMvcClassId) - fun createGetMockMvcResponseModel(requestBuilderModel: UtModel, idGenerator: () -> Int): UtModel { - val mockMvcModel = createMockMvcModel(idGenerator) - val performModel = UtAssembleModel( - id = idGenerator(), - classId = resultActionsClassId, - modelName = "perform", - instantiationCall = UtExecutableCallModel( - instance = mockMvcModel, - executable = mockMvcPerformMethodId, - params = listOf(requestBuilderModel) - ) - ) - // TODO add `andDo(print())` - val andReturnModel = UtAssembleModel( - id = idGenerator(), - classId = mvcResultClassId, - modelName = "andReturn", - instantiationCall = UtExecutableCallModel( - instance = performModel, - executable = resultActionsAndReturnMethodId, - params = listOf() - ) - ) - return UtAssembleModel( - id = idGenerator(), - classId = mockHttpServletResponseClassId, - modelName = "getResponse", - instantiationCall = UtExecutableCallModel( - instance = andReturnModel, - executable = mvcResultGetResponseMethodId, - params = listOf() - ) - ) - } - fun createRequestBuilderModelOrNull(methodId: MethodId, arguments: List, idGenerator: () -> Int): UtModel? { check(methodId.parameters.size == arguments.size) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt index 47fe59555f..f39de8d517 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt @@ -7,7 +7,6 @@ import org.utbot.framework.codegen.domain.models.CgValue import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtSpringContextModel -import org.utbot.framework.plugin.api.UtSpringMockMvcResultActionsModel import org.utbot.framework.plugin.api.util.stringClassId class CgSpringVariableConstructor(context: CgContext) : CgVariableConstructor(context) { @@ -36,12 +35,4 @@ class CgSpringVariableConstructor(context: CgContext) : CgVariableConstructor(co valueByUtModelWrapper[UtSpringContextModel.wrap()] = it } } - - override fun constructValueByModel(model: UtModel, name: String?): CgValue = when (model) { - is UtSpringMockMvcResultActionsModel -> CgLiteral( - model.classId, - "UtSpringMockMvcResult(${model.status}, ${model.errorMessage}, ${model.contentAsString}, ${model.viewName})" - ) - else -> super.constructValueByModel(model, name) - } } \ No newline at end of file From 40f3228ebf78efa725af98b0415ee34c8b75e9e9 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Fri, 28 Jul 2023 14:51:30 +0300 Subject: [PATCH 16/19] Handle `modelTagName` for `UtCustomModel` --- .../domain/models/builders/SpringTestClassModelBuilder.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt index 49dc213fcf..8294a09e94 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt @@ -113,7 +113,9 @@ class SpringTestClassModelBuilder(val context: CgContext): TestClassModelBuilder is UtClassRefModel, is UtVoidModel, is UtEnumConstantModel -> {} - is UtCustomModel -> currentModel.origin?.let { collectRecursively(it.wrap(), allModels) } + is UtCustomModel -> currentModel.origin?.let { + collectRecursively(it.wrap(currentModelWrapper.modelTagName), allModels) + } is UtLambdaModel -> { currentModel.capturedValues.forEach { collectRecursively(it.wrap(), allModels) } } From bfd582790938886fc74135b3b15d172e65e33547 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Fri, 28 Jul 2023 15:14:19 +0300 Subject: [PATCH 17/19] Fix compilation after rebase --- .../codegen/domain/context/CgContext.kt | 16 ++- .../builders/SpringTestClassModelBuilder.kt | 119 +++++++++--------- 2 files changed, 65 insertions(+), 70 deletions(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt index 3791d3428a..de776c7146 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt @@ -457,7 +457,13 @@ interface CgContextOwner { val getLambdaMethod: MethodId get() = utilMethodProvider.getLambdaMethodMethodId - fun UtModel.wrap(modelTagName: String? = null): UtModelWrapper + fun UtModel.wrap(modelTagName: String? = null): UtModelWrapper = + UtModelWrapper( + testSetId = currentTestSetId, + executionId = currentExecutionId, + model = this, + modelTagName = modelTagName + ) } /** @@ -586,14 +592,6 @@ class CgContext( } } - override fun UtModel.wrap(modelTagName: String?): UtModelWrapper = - UtModelWrapper( - testSetId = currentTestSetId, - executionId = currentExecutionId, - model = this, - modelTagName = modelTagName - ) - private fun createClassIdForNestedClass(testClassModel: SimpleTestClassModel): ClassId { val simpleName = "${testClassModel.classUnderTest.simpleName}Test" return BuiltinClassId( diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt index 8294a09e94..8b24281948 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt @@ -2,6 +2,7 @@ package org.utbot.framework.codegen.domain.models.builders import org.utbot.framework.codegen.domain.UtModelWrapper import org.utbot.framework.codegen.domain.context.CgContext +import org.utbot.framework.codegen.domain.context.CgContextOwner import org.utbot.framework.codegen.domain.models.CgMethodTestSet import org.utbot.framework.codegen.domain.models.SpringSpecificInformation import org.utbot.framework.codegen.domain.models.SpringTestClassModel @@ -24,7 +25,9 @@ import org.utbot.framework.plugin.api.util.SpringModelUtils.isAutowiredFromConte typealias TypedModelWrappers = Map> -class SpringTestClassModelBuilder(val context: CgContext): TestClassModelBuilder() { +class SpringTestClassModelBuilder(val context: CgContext): + TestClassModelBuilder(), + CgContextOwner by context { override fun createTestClassModel(classUnderTest: ClassId, testSets: List): SpringTestClassModel { val baseModel = SimpleTestClassModelBuilder(context).createTestClassModel(classUnderTest, testSets) @@ -43,23 +46,21 @@ class SpringTestClassModelBuilder(val context: CgContext): TestClassModelBuilder val thisInstancesDependentModels = mutableSetOf() val stateBeforeDependentModels = mutableSetOf() - with(context) { - for ((testSetIndex, testSet) in testSets.withIndex()) { - withTestSetIdScope(testSetIndex) { - for ((executionIndex, execution) in testSet.executions.withIndex()) { - withExecutionIdScope(executionIndex) { - setOf(execution.stateBefore.thisInstance, execution.stateAfter.thisInstance) - .filterNotNull() - .forEach { model -> - thisInstanceModels += model.wrap() - thisInstancesDependentModels += collectByModel(model) - - } - - (execution.stateBefore.parameters + execution.stateBefore.thisInstance) - .filterNotNull() - .forEach { model -> stateBeforeDependentModels += collectByModel(model) } - } + for ((testSetIndex, testSet) in testSets.withIndex()) { + withTestSetIdScope(testSetIndex) { + for ((executionIndex, execution) in testSet.executions.withIndex()) { + withExecutionIdScope(executionIndex) { + setOf(execution.stateBefore.thisInstance, execution.stateAfter.thisInstance) + .filterNotNull() + .forEach { model -> + thisInstanceModels += model.wrap() + thisInstancesDependentModels += collectByModel(model) + + } + + (execution.stateBefore.parameters + execution.stateBefore.thisInstance) + .filterNotNull() + .forEach { model -> stateBeforeDependentModels += collectByModel(model) } } } } @@ -84,9 +85,7 @@ class SpringTestClassModelBuilder(val context: CgContext): TestClassModelBuilder private fun collectByModel(model: UtModel): Set { val dependentModels = mutableSetOf() - with(context){ - collectRecursively(model.wrap(), dependentModels) - } + collectRecursively(model.wrap(), dependentModels) return dependentModels } @@ -106,51 +105,49 @@ class SpringTestClassModelBuilder(val context: CgContext): TestClassModelBuilder return } - with(context) { - when (val currentModel = currentModelWrapper.model) { - is UtNullModel, - is UtPrimitiveModel, - is UtClassRefModel, - is UtVoidModel, - is UtEnumConstantModel -> {} - is UtCustomModel -> currentModel.origin?.let { - collectRecursively(it.wrap(currentModelWrapper.modelTagName), allModels) - } - is UtLambdaModel -> { - currentModel.capturedValues.forEach { collectRecursively(it.wrap(), allModels) } - } - is UtArrayModel -> { - currentModel.stores.values.forEach { collectRecursively(it.wrap(), allModels) } - if (currentModel.stores.count() < currentModel.length) { - collectRecursively(currentModel.constModel.wrap(), allModels) - } + when (val currentModel = currentModelWrapper.model) { + is UtNullModel, + is UtPrimitiveModel, + is UtClassRefModel, + is UtVoidModel, + is UtEnumConstantModel -> {} + is UtCustomModel -> currentModel.origin?.let { + collectRecursively(it.wrap(currentModelWrapper.modelTagName), allModels) + } + is UtLambdaModel -> { + currentModel.capturedValues.forEach { collectRecursively(it.wrap(), allModels) } + } + is UtArrayModel -> { + currentModel.stores.values.forEach { collectRecursively(it.wrap(), allModels) } + if (currentModel.stores.count() < currentModel.length) { + collectRecursively(currentModel.constModel.wrap(), allModels) } - is UtCompositeModel -> { - // Here we traverse fields only. - // Traversing mocks as well will result in wrong models playing - // a role of class fields with @Mock annotation. - currentModel.fields.forEach { (fieldId, model) -> - // We use `modelTagName` in order to distinguish mock models - val modeTagName = if(model.isMockModel()) fieldId.name else null - collectRecursively(model.wrap(modeTagName), allModels) - } + } + is UtCompositeModel -> { + // Here we traverse fields only. + // Traversing mocks as well will result in wrong models playing + // a role of class fields with @Mock annotation. + currentModel.fields.forEach { (fieldId, model) -> + // We use `modelTagName` in order to distinguish mock models + val modeTagName = if(model.isMockModel()) fieldId.name else null + collectRecursively(model.wrap(modeTagName), allModels) } - is UtAssembleModel -> { - currentModel.origin?.let { collectRecursively(it.wrap(), allModels) } - - currentModel.instantiationCall.instance?.let { collectRecursively(it.wrap(), allModels) } - currentModel.instantiationCall.params.forEach { collectRecursively(it.wrap(), allModels) } - - currentModel.modificationsChain.forEach { stmt -> - stmt.instance?.let { collectRecursively(it.wrap(), allModels) } - when (stmt) { - is UtStatementCallModel -> stmt.params.forEach { collectRecursively(it.wrap(), allModels) } - is UtDirectSetFieldModel -> collectRecursively(stmt.fieldModel.wrap(), allModels) - } + } + is UtAssembleModel -> { + currentModel.origin?.let { collectRecursively(it.wrap(), allModels) } + + currentModel.instantiationCall.instance?.let { collectRecursively(it.wrap(), allModels) } + currentModel.instantiationCall.params.forEach { collectRecursively(it.wrap(), allModels) } + + currentModel.modificationsChain.forEach { stmt -> + stmt.instance?.let { collectRecursively(it.wrap(), allModels) } + when (stmt) { + is UtStatementCallModel -> stmt.params.forEach { collectRecursively(it.wrap(), allModels) } + is UtDirectSetFieldModel -> collectRecursively(stmt.fieldModel.wrap(), allModels) } } - //Python, JavaScript, Go models are not required in Spring } + //Python, JavaScript, Go models are not required in Spring } } } \ No newline at end of file From 06a2ebd951b9961ca95314175b06a284f18e9227 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Fri, 28 Jul 2023 18:19:03 +0300 Subject: [PATCH 18/19] Fix JS compilation --- utbot-js/src/main/kotlin/api/JsUtModelConstructor.kt | 4 ++-- .../codegen/model/constructor/tree/JsCgMethodConstructor.kt | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/utbot-js/src/main/kotlin/api/JsUtModelConstructor.kt b/utbot-js/src/main/kotlin/api/JsUtModelConstructor.kt index 09c5b74af3..aaf51d5a61 100644 --- a/utbot-js/src/main/kotlin/api/JsUtModelConstructor.kt +++ b/utbot-js/src/main/kotlin/api/JsUtModelConstructor.kt @@ -21,11 +21,11 @@ import org.utbot.framework.plugin.api.UtExecutableCallModel import org.utbot.framework.plugin.api.UtModel import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelConstructorInterface -class JsUtModelConstructor : UtModelConstructorInterface { +class JsUtModelConstructor { // TODO SEVERE: Requires substantial expansion to other types @Suppress("NAME_SHADOWING") - override fun construct(value: Any?, classId: ClassId): UtModel { + fun construct(value: Any?, classId: ClassId): UtModel { val classId = classId as JsClassId if (classId == jsErrorClassId) return UtModel(jsErrorClassId) return when (value) { diff --git a/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt b/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt index 4334eb49e0..685c62e612 100644 --- a/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt +++ b/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt @@ -2,6 +2,7 @@ package framework.codegen.model.constructor.tree import framework.api.js.JsClassId import org.utbot.framework.codegen.domain.context.CgContext +import org.utbot.framework.codegen.domain.models.CgMethodTestSet import org.utbot.framework.codegen.domain.models.CgTestMethod import org.utbot.framework.codegen.domain.models.CgTestMethodType import org.utbot.framework.codegen.domain.models.CgValue From e5dd7c195a2d60a4b9c54343a31fa04031e71c2d Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Mon, 31 Jul 2023 16:43:15 +0300 Subject: [PATCH 19/19] Address comments in #2447 --- .../org/utbot/framework/plugin/api/Api.kt | 25 +++++++++++---- .../plugin/api/util/SpringModelUtils.kt | 2 +- .../codegen/tree/CgClassFieldManager.kt | 4 +-- .../codegen/tree/CgMethodConstructor.kt | 11 ++++--- ...CgMockMvcResultActionsAssertConstructor.kt | 12 +++---- ...CgSpringIntegrationTestClassConstructor.kt | 9 ++++-- .../codegen/tree/CgVariableConstructor.kt | 14 ++++++-- .../fields/ExecutionStateAnalyzer.kt | 6 ++-- .../SimpleUtExecutionInstrumentation.kt | 2 +- .../JavaStdLibCustomModelConstructors.kt | 2 +- .../UtAssembleModelConstructors.kt | 4 +-- .../constructors/UtCustomModelConstructor.kt | 19 ----------- .../constructors/UtModelConstructor.kt | 32 +++++++------------ .../UtModelWithCompositeOriginConstructor.kt | 30 +++++++++++++++++ .../context/InstrumentationContext.kt | 8 +++-- .../context/SimpleInstrumentationContext.kt | 8 ++--- .../phases/ModelConstructionPhase.kt | 6 ++-- .../execution/phases/PhasesController.kt | 2 +- .../spring/SpringInstrumentationContext.kt | 6 ++-- .../SpringUtExecutionInstrumentation.kt | 5 ++- .../UtMockMvcResultActionsModelConstructor.kt | 10 +++--- .../constructors/BaseConstructorTest.kt | 6 ++-- .../main/kotlin/api/JsUtModelConstructor.kt | 4 +-- .../constructor/tree/JsCgMethodConstructor.kt | 6 ++-- .../tree/PythonCgMethodConstructor.kt | 6 +--- 25 files changed, 134 insertions(+), 105 deletions(-) delete mode 100644 utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtCustomModelConstructor.kt create mode 100644 utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelWithCompositeOriginConstructor.kt 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 979f6c01c0..c5b3474254 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 @@ -576,9 +576,20 @@ data class UtArrayModel( override fun hashCode(): Int = id } -interface UtModelWithOrigin { - val origin: UtCompositeModel? -} +/** + * Wrapper of [origin] model, that can be handled in a different + * way in some situations (e.g. during value construction). + */ +sealed class UtModelWithCompositeOrigin( + id: Int?, + classId: ClassId, + modelName: String = id.toString(), + open val origin: UtCompositeModel?, +) : UtReferenceModel( + id = id, + classId = classId, + modelName = modelName +) /** * Model for complex objects with assemble instructions. @@ -595,7 +606,7 @@ data class UtAssembleModel private constructor( val instantiationCall: UtStatementCallModel, val modificationsChain: List, override val origin: UtCompositeModel? -) : UtReferenceModel(id, classId, modelName), UtModelWithOrigin { +) : UtModelWithCompositeOrigin(id, classId, modelName, origin) { /** * Creates a new [UtAssembleModel]. @@ -737,11 +748,11 @@ class UtLambdaModel( * Common parent of all framework-specific models (e.g. Spring-specific models) */ abstract class UtCustomModel( - override val origin: UtCompositeModel? = null, id: Int?, classId: ClassId, - modelName: String = id.toString() -) : UtReferenceModel(id, classId, modelName), UtModelWithOrigin + modelName: String = id.toString(), + override val origin: UtCompositeModel? = null, +) : UtModelWithCompositeOrigin(id, classId, modelName, origin) object UtSpringContextModel : UtCustomModel( id = null, diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt index dc826f0e60..4bb8326466 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt @@ -109,7 +109,7 @@ object SpringModelUtils { private val mockMvcRequestBuildersClassId = ClassId("org.springframework.test.web.servlet.request.MockMvcRequestBuilders") private val requestBuilderClassId = ClassId("org.springframework.test.web.servlet.RequestBuilder") val resultActionsClassId = ClassId("org.springframework.test.web.servlet.ResultActions") - private val mockMvcClassId = ClassId("org.springframework.test.web.servlet.MockMvc") + val mockMvcClassId = ClassId("org.springframework.test.web.servlet.MockMvc") private val mvcResultClassId = ClassId("org.springframework.test.web.servlet.MvcResult") private val resultHandlerClassId = ClassId("org.springframework.test.web.servlet.ResultHandler") val mockMvcResultHandlersClassId = ClassId("org.springframework.test.web.servlet.result.MockMvcResultHandlers") diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgClassFieldManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgClassFieldManager.kt index 9102daaf5e..45b028a545 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgClassFieldManager.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgClassFieldManager.kt @@ -11,7 +11,7 @@ import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtCompositeModel import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.UtModelWithOrigin +import org.utbot.framework.plugin.api.UtModelWithCompositeOrigin import org.utbot.framework.plugin.api.isMockModel import org.utbot.framework.plugin.api.util.SpringModelUtils.autowiredClassId import org.utbot.framework.plugin.api.util.SpringModelUtils.isAutowiredFromContext @@ -48,7 +48,7 @@ class CgInjectingMocksFieldsManager(val context: CgContext) : CgClassFieldManage override fun constructVariableForField(model: UtModel, modelVariable: CgValue): CgValue { val modelFields = when (model) { is UtCompositeModel -> model.fields - is UtModelWithOrigin -> model.origin?.fields + is UtModelWithCompositeOrigin -> model.origin?.fields else -> null } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt index 54b7a9c9d7..2fddec0c3c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMethodConstructor.kt @@ -100,7 +100,7 @@ import org.utbot.framework.plugin.api.UtExecutionSuccess import org.utbot.framework.plugin.api.UtExplicitlyThrownException import org.utbot.framework.plugin.api.UtLambdaModel import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.UtModelWithOrigin +import org.utbot.framework.plugin.api.UtModelWithCompositeOrigin import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation import org.utbot.framework.plugin.api.UtNullModel import org.utbot.framework.plugin.api.UtOverflowFailure @@ -1069,7 +1069,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte when (model) { is UtCompositeModel -> collectExecutionsResultFieldsRecursively(model, 0) - is UtModelWithOrigin -> model.origin?.let { + is UtModelWithCompositeOrigin -> model.origin?.let { collectExecutionsResultFieldsRecursively(it, 0) } @@ -1109,7 +1109,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte when (fieldModel) { is UtCompositeModel -> collectExecutionsResultFieldsRecursively(fieldModel, depth + 1) - is UtModelWithOrigin -> fieldModel.origin?.let { + is UtModelWithCompositeOrigin -> fieldModel.origin?.let { collectExecutionsResultFieldsRecursively(it, depth + 1) } @@ -1201,7 +1201,10 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte expectedVariableName: String = "expected", emptyLineIfNeeded: Boolean = false, ) { - if (expected !is UtCustomModel || !customAssertConstructor.tryConstructCustomAssert(expected, actual)) { + val successfullyConstructedCustomAssert = expected is UtCustomModel && + customAssertConstructor.tryConstructCustomAssert(expected, actual) + + if (!successfullyConstructedCustomAssert) { val expectedVariable = variableConstructor.getOrCreateVariable(expected, expectedVariableName) if (emptyLineIfNeeded) emptyLineIfNeeded() assertEquality(expectedVariable, actual) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMockMvcResultActionsAssertConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMockMvcResultActionsAssertConstructor.kt index d40e359164..963eabdf3b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMockMvcResultActionsAssertConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgMockMvcResultActionsAssertConstructor.kt @@ -2,14 +2,12 @@ package org.utbot.framework.codegen.tree import org.utbot.framework.codegen.domain.context.CgContext import org.utbot.framework.codegen.domain.context.CgContextOwner -import org.utbot.framework.codegen.domain.models.AnnotationTarget import org.utbot.framework.codegen.domain.models.CgVariable import org.utbot.framework.codegen.services.access.CgCallableAccessManager import org.utbot.framework.codegen.services.access.CgCallableAccessManagerImpl import org.utbot.framework.codegen.tree.CgComponents.getStatementConstructorBy import org.utbot.framework.plugin.api.UtCustomModel import org.utbot.framework.plugin.api.UtSpringMockMvcResultActionsModel -import org.utbot.framework.plugin.api.util.SpringModelUtils.autoConfigureMockMvcClassId import org.utbot.framework.plugin.api.util.SpringModelUtils.contentMatchersStringMethodId import org.utbot.framework.plugin.api.util.SpringModelUtils.mockMvcResultHandlersClassId import org.utbot.framework.plugin.api.util.SpringModelUtils.mockMvcResultMatchersClassId @@ -34,20 +32,18 @@ class CgMockMvcResultActionsAssertConstructor( CgCallableAccessManager by CgCallableAccessManagerImpl(context) { override fun tryConstructCustomAssert(expected: UtCustomModel, actual: CgVariable): Boolean { if (expected is UtSpringMockMvcResultActionsModel) { - addAnnotation(autoConfigureMockMvcClassId, AnnotationTarget.Class) - var expr = actual[resultActionsAndDoMethodId](mockMvcResultHandlersClassId[resultHandlersPrintMethodId]()) - expr = expr[resultActionsAndExpectMethodId]( + +actual[resultActionsAndDoMethodId](mockMvcResultHandlersClassId[resultHandlersPrintMethodId]()) + +actual[resultActionsAndExpectMethodId]( mockMvcResultMatchersClassId[resultMatchersStatusMethodId]()[statusMatchersIsMethodId](expected.status) ) expected.viewName?.let { viewName -> - expr = expr[resultActionsAndExpectMethodId]( + +actual[resultActionsAndExpectMethodId]( mockMvcResultMatchersClassId[resultMatchersViewMethodId]()[viewMatchersNameMethodId](viewName) ) } - expr = expr[resultActionsAndExpectMethodId]( + +actual[resultActionsAndExpectMethodId]( mockMvcResultMatchersClassId[resultMatchersContentMethodId]()[contentMatchersStringMethodId](expected.contentAsString) ) - +expr return true } else return delegateAssertConstructor.tryConstructCustomAssert(expected, actual) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringIntegrationTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringIntegrationTestClassConstructor.kt index 743f6114ed..ff217b2a8a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringIntegrationTestClassConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringIntegrationTestClassConstructor.kt @@ -17,6 +17,7 @@ import org.utbot.framework.plugin.api.ConcreteContextLoadingResult import org.utbot.framework.plugin.api.SpringSettings.* import org.utbot.framework.plugin.api.SpringConfiguration.* import org.utbot.framework.plugin.api.util.IndentUtil.TAB +import org.utbot.framework.plugin.api.util.SpringModelUtils import org.utbot.framework.plugin.api.util.SpringModelUtils.activeProfilesClassId import org.utbot.framework.plugin.api.util.SpringModelUtils.autoConfigureTestDbClassId import org.utbot.framework.plugin.api.util.SpringModelUtils.bootstrapWithClassId @@ -25,6 +26,7 @@ import org.utbot.framework.plugin.api.util.SpringModelUtils.crudRepositoryClassI import org.utbot.framework.plugin.api.util.SpringModelUtils.dirtiesContextClassId import org.utbot.framework.plugin.api.util.SpringModelUtils.dirtiesContextClassModeClassId import org.utbot.framework.plugin.api.util.SpringModelUtils.extendWithClassId +import org.utbot.framework.plugin.api.util.SpringModelUtils.mockMvcClassId import org.utbot.framework.plugin.api.util.SpringModelUtils.runWithClassId import org.utbot.framework.plugin.api.util.SpringModelUtils.springBootTestClassId import org.utbot.framework.plugin.api.util.SpringModelUtils.springBootTestContextBootstrapperClassId @@ -47,7 +49,7 @@ class CgSpringIntegrationTestClassConstructor( } override fun constructTestClass(testClassModel: SpringTestClassModel): CgClass { - addNecessarySpringSpecificAnnotations() + addNecessarySpringSpecificAnnotations(testClassModel) return super.constructTestClass(testClassModel) } @@ -102,7 +104,7 @@ class CgSpringIntegrationTestClassConstructor( .map { it.escapeControlChars() } ) - private fun addNecessarySpringSpecificAnnotations() { + private fun addNecessarySpringSpecificAnnotations(testClassModel: SpringTestClassModel) { val isSpringBootTestAccessible = utContext.classLoader.tryLoadClass(springBootTestClassId.name) != null if (isSpringBootTestAccessible) { addAnnotation(springBootTestClassId, Class) @@ -193,5 +195,8 @@ class CgSpringIntegrationTestClassConstructor( // generated tests will fail with `ClassNotFoundException: org.springframework.dao.DataAccessException`. if (utContext.classLoader.tryLoadClass(crudRepositoryClassId.name) != null) addAnnotation(autoConfigureTestDbClassId, Class) + + if (mockMvcClassId in testClassModel.springSpecificInformation.autowiredFromContextModels) + addAnnotation(SpringModelUtils.autoConfigureMockMvcClassId, Class) } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt index c78749d873..e25f5ab6b5 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt @@ -1,5 +1,6 @@ package org.utbot.framework.codegen.tree +import mu.KotlinLogging import org.utbot.common.isStatic import org.utbot.framework.codegen.domain.builtin.forName import org.utbot.framework.codegen.domain.builtin.setArrayElement @@ -68,6 +69,10 @@ open class CgVariableConstructor(val context: CgContext) : CgCallableAccessManager by getCallableAccessManagerBy(context), CgStatementConstructor by getStatementConstructorBy(context) { + companion object { + private val logger = KotlinLogging.logger {} + } + private val nameGenerator = getNameGeneratorBy(context) val mockFrameworkManager = getMockFrameworkManagerBy(context) @@ -99,7 +104,7 @@ open class CgVariableConstructor(val context: CgContext) : constructValueByModel(model, name) } - open fun constructValueByModel(model: UtModel, name: String?): CgValue { + private fun constructValueByModel(model: UtModel, name: String?): CgValue { // name could be taken from existing names, or be specified manually, or be created from generator val baseName = name ?: nameGenerator.nameFrom(model.classId) @@ -112,7 +117,12 @@ open class CgVariableConstructor(val context: CgContext) : is UtLambdaModel -> constructLambda(model, baseName) is UtNullModel -> nullLiteral() is UtPrimitiveModel -> CgLiteral(model.classId, model.value) - is UtCustomModel -> constructValueByModel(model.origin ?: error("Can't construct value for custom model without origin [$model]"), name) + is UtCustomModel -> { + logger.error { "Unexpected behaviour: value for UtCustomModel [$model] is constructed by base CgVariableConstructor" } + constructValueByModel( + model.origin ?: error("Can't construct value for UtCustomModel without origin [$model]"), name + ) + } is UtReferenceModel -> error("Unexpected UtReferenceModel: ${model::class}") is UtVoidModel -> error("Unexpected UtVoidModel: ${model::class}") else -> error("Unexpected UtModel: ${model::class}") diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt index e8fcb7f8ab..46466eb945 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt @@ -15,7 +15,7 @@ import org.utbot.framework.plugin.api.UtEnumConstantModel import org.utbot.framework.plugin.api.UtExecution import org.utbot.framework.plugin.api.UtLambdaModel import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.UtModelWithOrigin +import org.utbot.framework.plugin.api.UtModelWithCompositeOrigin import org.utbot.framework.plugin.api.UtNullModel import org.utbot.framework.plugin.api.UtPrimitiveModel import org.utbot.framework.plugin.api.UtReferenceModel @@ -106,7 +106,7 @@ class ExecutionStateAnalyzer(val execution: UtExecution) { var modelBefore = before if (before::class != after::class) { - if (before is UtModelWithOrigin && after is UtModelWithOrigin && before.origin != null) { + if (before is UtModelWithCompositeOrigin && after is UtModelWithCompositeOrigin && before.origin != null) { modelBefore = before.origin ?: unreachableBranch("We have already checked the origin for a null value") } else { doNotRun { @@ -114,7 +114,7 @@ class ExecutionStateAnalyzer(val execution: UtExecution) { // modelAfter (constructed by concrete executor) will consist all these fields, // therefore, AssembleModelGenerator won't be able to transform the given composite model - val reason = if (before is UtModelWithOrigin && after is UtCompositeModel) { + val reason = if (before is UtModelWithCompositeOrigin && after is UtCompositeModel) { "ModelBefore is an UtModelWithOrigin and ModelAfter " + "is a CompositeModel, but modelBefore doesn't have an origin model." } else { diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt index 4c3ba7775d..adaec0ab1a 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt @@ -131,7 +131,7 @@ class SimpleUtExecutionInstrumentation( delegateInstrumentation.getStaticField(fieldId).map { value -> UtModelConstructor.createOnlyUserClassesConstructor( pathsToUserClasses = pathsToUserClasses, - utCustomModelConstructorFinder = instrumentationContext::findUtCustomModelConstructor + utModelWithCompositeOriginConstructorFinder = instrumentationContext::findUtModelWithCompositeOriginConstructor ).construct(value, fieldId.type) } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/JavaStdLibCustomModelConstructors.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/JavaStdLibCustomModelConstructors.kt index 148bb3e059..72419f9c49 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/JavaStdLibCustomModelConstructors.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/JavaStdLibCustomModelConstructors.kt @@ -4,7 +4,7 @@ import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.primitiveWrappers import org.utbot.framework.plugin.api.util.voidWrapperClassId -val javaStdLibCustomModelConstructors: Map, () -> UtCustomModelConstructor> = +val javaStdLibModelWithCompositeOriginConstructors: Map, () -> UtModelWithCompositeOriginConstructor> = mutableMapOf, () -> UtAssembleModelConstructorBase>( /** * Optionals diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtAssembleModelConstructors.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtAssembleModelConstructors.kt index cc6c4d87e2..f6acd29bba 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtAssembleModelConstructors.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtAssembleModelConstructors.kt @@ -18,8 +18,8 @@ internal fun findStreamConstructor(stream: BaseStream<*, *>): UtAssembleModelCon else -> BaseStreamConstructor() } -internal abstract class UtAssembleModelConstructorBase : UtCustomModelConstructor { - override fun constructCustomModel( +internal abstract class UtAssembleModelConstructorBase : UtModelWithCompositeOriginConstructor { + override fun constructModelWithCompositeOrigin( internalConstructor: UtModelConstructorInterface, value: Any, valueClassId: ClassId, diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtCustomModelConstructor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtCustomModelConstructor.kt deleted file mode 100644 index 93e1db1b28..0000000000 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtCustomModelConstructor.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.utbot.instrumentation.instrumentation.execution.constructors - -import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.UtCompositeModel -import org.utbot.framework.plugin.api.UtModel - -/** - * Responsible for constructing [UtModel]s of some specific type, that are more human-readable - * when rendered by the code generation compared to [UtCompositeModel]s. - */ -interface UtCustomModelConstructor { - fun constructCustomModel( - internalConstructor: UtModelConstructorInterface, - value: Any, - valueClassId: ClassId, - id: Int?, - saveToCache: (UtModel) -> Unit - ): UtModel -} \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelConstructor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelConstructor.kt index f2950aa27f..dd5de775c6 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelConstructor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelConstructor.kt @@ -17,13 +17,6 @@ interface UtModelConstructorInterface { * Constructs a UtModel from a concrete [value] with a specific [classId]. */ fun construct(value: Any?, classId: ClassId): UtModel - - /** - * Constructs UtCompositeModel. - * - * Uses runtime javaClass to collect ALL fields, except final static fields, and builds this model recursively. - */ - fun constructCompositeModel(value: Any): UtCompositeModel } /** @@ -39,7 +32,7 @@ interface UtModelConstructorInterface { */ class UtModelConstructor( private val objectToModelCache: IdentityHashMap, - private val utCustomModelConstructorFinder: (ClassId) -> UtCustomModelConstructor?, + private val utModelWithCompositeOriginConstructorFinder: (ClassId) -> UtModelWithCompositeOriginConstructor?, private val compositeModelStrategy: UtCompositeModelStrategy = AlwaysConstructStrategy, private val maxDepth: Long = DEFAULT_MAX_DEPTH ) : UtModelConstructorInterface { @@ -56,13 +49,13 @@ class UtModelConstructor( fun createOnlyUserClassesConstructor( pathsToUserClasses: Set, - utCustomModelConstructorFinder: (ClassId) -> UtCustomModelConstructor? + utModelWithCompositeOriginConstructorFinder: (ClassId) -> UtModelWithCompositeOriginConstructor? ): UtModelConstructor { val cache = IdentityHashMap() val strategy = ConstructOnlyUserClassesOrCachedObjectsStrategy( pathsToUserClasses, cache ) - return UtModelConstructor(cache, utCustomModelConstructorFinder, strategy) + return UtModelConstructor(cache, utModelWithCompositeOriginConstructorFinder, strategy) } } @@ -280,7 +273,7 @@ class UtModelConstructor( val streamConstructor = findStreamConstructor(stream) try { - streamConstructor.constructCustomModel(this, stream, valueToClassId(stream), handleId(stream)) { + streamConstructor.constructModelWithCompositeOrigin(this, stream, valueToClassId(stream), handleId(stream)) { constructedObjects[stream] = it } } catch (e: Exception) { @@ -305,9 +298,9 @@ class UtModelConstructor( * Uses runtime class of [value]. */ private fun tryConstructCustomModel(value: Any, remainingDepth: Long): UtModel? = - utCustomModelConstructorFinder(value::class.java.id)?.let { modelConstructor -> + utModelWithCompositeOriginConstructorFinder(value::class.java.id)?.let { modelConstructor -> try { - modelConstructor.constructCustomModel( + modelConstructor.constructModelWithCompositeOrigin( internalConstructor = this.withMaxDepth(remainingDepth - 1), value = value, valueClassId = valueToClassId(value), @@ -321,11 +314,11 @@ class UtModelConstructor( } } - override fun constructCompositeModel(value: Any): UtCompositeModel = constructCompositeModel( - value, - remainingDepth = maxDepth - ) - + /** + * Constructs UtCompositeModel. + * + * Uses runtime javaClass to collect ALL fields, except final static fields, and builds this model recursively. + */ private fun constructCompositeModel(value: Any, remainingDepth: Long): UtCompositeModel { // value can be mock only if it was previously constructed from UtCompositeModel val isMock = objectToModelCache[value]?.isMockModel() ?: false @@ -357,9 +350,6 @@ class UtModelConstructor( private fun withMaxDepth(newMaxDepth: Long) = object : UtModelConstructorInterface { override fun construct(value: Any?, classId: ClassId): UtModel = construct(value, classId, newMaxDepth) - - override fun constructCompositeModel(value: Any): UtCompositeModel = - constructCompositeModel(value, newMaxDepth) } } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelWithCompositeOriginConstructor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelWithCompositeOriginConstructor.kt new file mode 100644 index 0000000000..034f7a2306 --- /dev/null +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/UtModelWithCompositeOriginConstructor.kt @@ -0,0 +1,30 @@ +package org.utbot.instrumentation.instrumentation.execution.constructors + +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtModelWithCompositeOrigin + +/** + * Responsible for constructing [UtModelWithCompositeOrigin]s of some specific type, that are more human-readable + * when rendered by the code generation compared to [UtCompositeModel]s. + */ +interface UtModelWithCompositeOriginConstructor { + + /** + * @param internalConstructor constructor to use for constructing child models + * (e.g. when [value] is a list, [internalConstructor] is used for constructing list elements) + * @param value object to construct model for + * @param valueClassId [ClassId] to use for constructed model + * @param saveToCache function that should be called on the returned model right after constructing it, + * but before adding any modifications, so [internalConstructor] doesn't have to reconstruct it for every modification + * and recursive values (e.g. list containing itself) are constructed correctly + */ + fun constructModelWithCompositeOrigin( + internalConstructor: UtModelConstructorInterface, + value: Any, + valueClassId: ClassId, + id: Int?, + saveToCache: (UtModel) -> Unit + ): UtModelWithCompositeOrigin +} \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/InstrumentationContext.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/InstrumentationContext.kt index b4f2906481..e2f818d64b 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/InstrumentationContext.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/InstrumentationContext.kt @@ -3,7 +3,7 @@ package org.utbot.instrumentation.instrumentation.execution.context import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtConcreteValue import org.utbot.framework.plugin.api.UtModel -import org.utbot.instrumentation.instrumentation.execution.constructors.UtCustomModelConstructor +import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelWithCompositeOriginConstructor import java.lang.reflect.Method import java.util.IdentityHashMap import org.utbot.instrumentation.instrumentation.mock.computeKeyForMethod @@ -28,7 +28,11 @@ interface InstrumentationContext { */ fun constructContextDependentValue(model: UtModel): UtConcreteValue<*>? - fun findUtCustomModelConstructor(classId: ClassId): UtCustomModelConstructor? + /** + * Finds [UtModelWithCompositeOriginConstructor] that should be used to + * construct models for instances of specified [class][classId]. + */ + fun findUtModelWithCompositeOriginConstructor(classId: ClassId): UtModelWithCompositeOriginConstructor? object MockGetter { data class MockContainer(private val values: List<*>) { diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/SimpleInstrumentationContext.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/SimpleInstrumentationContext.kt index ef6f24594c..51e08ae816 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/SimpleInstrumentationContext.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/context/SimpleInstrumentationContext.kt @@ -4,8 +4,8 @@ import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtConcreteValue import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.util.jClass -import org.utbot.instrumentation.instrumentation.execution.constructors.UtCustomModelConstructor -import org.utbot.instrumentation.instrumentation.execution.constructors.javaStdLibCustomModelConstructors +import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelWithCompositeOriginConstructor +import org.utbot.instrumentation.instrumentation.execution.constructors.javaStdLibModelWithCompositeOriginConstructors /** * Simple instrumentation context, that is used for pure JVM projects without @@ -19,6 +19,6 @@ class SimpleInstrumentationContext : InstrumentationContext { */ override fun constructContextDependentValue(model: UtModel): UtConcreteValue<*>? = null - override fun findUtCustomModelConstructor(classId: ClassId): UtCustomModelConstructor? = - javaStdLibCustomModelConstructors[classId.jClass]?.invoke() + override fun findUtModelWithCompositeOriginConstructor(classId: ClassId): UtModelWithCompositeOriginConstructor? = + javaStdLibModelWithCompositeOriginConstructors[classId.jClass]?.invoke() } \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ModelConstructionPhase.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ModelConstructionPhase.kt index f68f723374..20f6dac2e3 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ModelConstructionPhase.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ModelConstructionPhase.kt @@ -9,7 +9,7 @@ import org.utbot.instrumentation.instrumentation.et.ExplicitThrowInstruction import org.utbot.instrumentation.instrumentation.et.TraceHandler import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult import org.utbot.instrumentation.instrumentation.execution.constructors.UtCompositeModelStrategy -import org.utbot.instrumentation.instrumentation.execution.constructors.UtCustomModelConstructor +import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelWithCompositeOriginConstructor import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelConstructor import java.security.AccessControlException import java.util.* @@ -19,7 +19,7 @@ import java.util.* */ class ModelConstructionPhase( private val traceHandler: TraceHandler, - private val utCustomModelConstructorFinder: (ClassId) -> UtCustomModelConstructor?, + private val utModelWithCompositeOriginConstructorFinder: (ClassId) -> UtModelWithCompositeOriginConstructor?, ) : ExecutionPhase { override fun wrapError(e: Throwable): ExecutionPhaseException { @@ -46,7 +46,7 @@ class ModelConstructionPhase( block() constructor = UtModelConstructor( objectToModelCache = cache, - utCustomModelConstructorFinder = utCustomModelConstructorFinder, + utModelWithCompositeOriginConstructorFinder = utModelWithCompositeOriginConstructorFinder, compositeModelStrategy = strategy, ) } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/PhasesController.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/PhasesController.kt index a552bcbd83..53d4e6fc0f 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/PhasesController.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/PhasesController.kt @@ -33,7 +33,7 @@ class PhasesController( val modelConstructionPhase = ModelConstructionPhase( traceHandler = traceHandler, - utCustomModelConstructorFinder = instrumentationContext::findUtCustomModelConstructor + utModelWithCompositeOriginConstructorFinder = instrumentationContext::findUtModelWithCompositeOriginConstructor ) val postprocessingPhase = PostprocessingPhase() diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt index fe24cdfc9a..79c9209772 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt @@ -9,7 +9,7 @@ import org.utbot.framework.plugin.api.UtSpringContextModel import org.utbot.framework.plugin.api.util.SpringModelUtils.resultActionsClassId import org.utbot.framework.plugin.api.util.isSubtypeOf import org.utbot.framework.plugin.api.util.utContext -import org.utbot.instrumentation.instrumentation.execution.constructors.UtCustomModelConstructor +import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelWithCompositeOriginConstructor import org.utbot.instrumentation.instrumentation.execution.context.InstrumentationContext import org.utbot.spring.api.SpringApi import org.utbot.spring.api.provider.SpringApiProviderFacade @@ -49,7 +49,7 @@ class SpringInstrumentationContext( else -> delegateInstrumentationContext.constructContextDependentValue(model) } - override fun findUtCustomModelConstructor(classId: ClassId): UtCustomModelConstructor? = + override fun findUtModelWithCompositeOriginConstructor(classId: ClassId): UtModelWithCompositeOriginConstructor? = if (classId.isSubtypeOf(resultActionsClassId)) UtMockMvcResultActionsModelConstructor() - else delegateInstrumentationContext.findUtCustomModelConstructor(classId) + else delegateInstrumentationContext.findUtModelWithCompositeOriginConstructor(classId) } \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt index 2a6808b694..bdc9feedae 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt @@ -101,7 +101,7 @@ class SpringUtExecutionInstrumentation( val bean = springApi.getBean(beanName) return UtModelConstructor.createOnlyUserClassesConstructor( pathsToUserClasses = classpathToConstruct, - utCustomModelConstructorFinder = instrumentationContext::findUtCustomModelConstructor + utModelWithCompositeOriginConstructorFinder = instrumentationContext::findUtModelWithCompositeOriginConstructor ).construct(bean, bean::class.java.id) } @@ -152,6 +152,9 @@ class SpringUtExecutionInstrumentation( targetDirectoryName = "spring-commons" ).path + // TODO may be we can use some alternative sandbox that has more permissions + // (at the very least we need `ReflectPermission("suppressAccessChecks")` + // to let Jackson work with private fields when `@RequestBody` is used) override val forceDisableSandbox: Boolean get() = true diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt index 6064e8e88f..05f307a373 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/UtMockMvcResultActionsModelConstructor.kt @@ -13,24 +13,24 @@ import org.utbot.framework.plugin.api.util.SpringModelUtils.responseGetStatusMet import org.utbot.framework.plugin.api.util.SpringModelUtils.resultActionsAndReturnMethodId import org.utbot.framework.plugin.api.util.mapClassId import org.utbot.framework.plugin.api.util.method -import org.utbot.instrumentation.instrumentation.execution.constructors.UtCustomModelConstructor +import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelWithCompositeOriginConstructor import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelConstructorInterface -class UtMockMvcResultActionsModelConstructor : UtCustomModelConstructor { - override fun constructCustomModel( +class UtMockMvcResultActionsModelConstructor : UtModelWithCompositeOriginConstructor { + override fun constructModelWithCompositeOrigin( internalConstructor: UtModelConstructorInterface, value: Any, valueClassId: ClassId, id: Int?, saveToCache: (UtModel) -> Unit - ): UtModel { + ): UtSpringMockMvcResultActionsModel { val mvcResult = resultActionsAndReturnMethodId.method.invoke(value) val response = mvcResultGetResponseMethodId.method.invoke(mvcResult) val modelAndView = mvcResultGetModelAndViewMethodId.method.invoke(mvcResult) return UtSpringMockMvcResultActionsModel( id = id, - origin = internalConstructor.constructCompositeModel(value), + origin = null, // replace with actual origin if needed status = responseGetStatusMethodId.method.invoke(response) as Int, errorMessage = responseGetErrorMessageMethodId.method.invoke(response) as String?, contentAsString = responseGetContentAsStringMethodId.method.invoke(response) as String, diff --git a/utbot-instrumentation/src/test/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/BaseConstructorTest.kt b/utbot-instrumentation/src/test/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/BaseConstructorTest.kt index 318a12eb7c..afc147be72 100644 --- a/utbot-instrumentation/src/test/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/BaseConstructorTest.kt +++ b/utbot-instrumentation/src/test/kotlin/org/utbot/instrumentation/instrumentation/execution/constructors/BaseConstructorTest.kt @@ -27,7 +27,7 @@ abstract class BaseConstructorTest { protected fun computeReconstructed(value: T): T { val model = UtModelConstructor( objectToModelCache = IdentityHashMap(), - utCustomModelConstructorFinder = ::findUtCustomModelConstructor + utModelWithCompositeOriginConstructorFinder = ::findUtCustomModelConstructor ).construct(value, value::class.java.id) Assertions.assertTrue(model is UtAssembleModel) @@ -36,6 +36,6 @@ abstract class BaseConstructorTest { return ValueConstructor().construct(listOf(model)).single().value as T } - protected open fun findUtCustomModelConstructor(classId: ClassId): UtCustomModelConstructor? = - javaStdLibCustomModelConstructors[classId.jClass]?.invoke() + protected open fun findUtCustomModelConstructor(classId: ClassId): UtModelWithCompositeOriginConstructor? = + javaStdLibModelWithCompositeOriginConstructors[classId.jClass]?.invoke() } \ No newline at end of file diff --git a/utbot-js/src/main/kotlin/api/JsUtModelConstructor.kt b/utbot-js/src/main/kotlin/api/JsUtModelConstructor.kt index aaf51d5a61..09c5b74af3 100644 --- a/utbot-js/src/main/kotlin/api/JsUtModelConstructor.kt +++ b/utbot-js/src/main/kotlin/api/JsUtModelConstructor.kt @@ -21,11 +21,11 @@ import org.utbot.framework.plugin.api.UtExecutableCallModel import org.utbot.framework.plugin.api.UtModel import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelConstructorInterface -class JsUtModelConstructor { +class JsUtModelConstructor : UtModelConstructorInterface { // TODO SEVERE: Requires substantial expansion to other types @Suppress("NAME_SHADOWING") - fun construct(value: Any?, classId: ClassId): UtModel { + override fun construct(value: Any?, classId: ClassId): UtModel { val classId = classId as JsClassId if (classId == jsErrorClassId) return UtModel(jsErrorClassId) return when (value) { diff --git a/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt b/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt index 685c62e612..38102712da 100644 --- a/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt +++ b/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgMethodConstructor.kt @@ -38,7 +38,7 @@ class JsCgMethodConstructor(ctx: CgContext) : CgMethodConstructor(ctx) { } // build arguments for ((index, param) in execution.stateBefore.parameters.withIndex()) { - val name = paramNames[execution.executableToCall ?: testSet.executableUnderTest]?.get(index) + val name = paramNames[testSet.executableUnderTest]?.get(index) methodArguments += variableConstructor.getOrCreateVariable(param, name) } recordActualResult() @@ -61,7 +61,7 @@ class JsCgMethodConstructor(ctx: CgContext) : CgMethodConstructor(ctx) { override fun generateResultAssertions() { emptyLineIfNeeded() val currentExecution = currentExecution!! - val method = currentExecutableToCall as MethodId + val method = currentExecutableUnderTest as MethodId // build assertions currentExecution.result .onSuccess { result -> @@ -80,7 +80,7 @@ class JsCgMethodConstructor(ctx: CgContext) : CgMethodConstructor(ctx) { private fun processExecutionFailure(execution: UtExecution, exception: Throwable) { val methodInvocationBlock = { - with(currentExecutableToCall) { + with(currentExecutableUnderTest) { when (this) { is MethodId -> thisInstance[this](*methodArguments.toTypedArray()).intercepted() is ConstructorId -> this(*methodArguments.toTypedArray()).intercepted() diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt index 1325e9ed23..659d282553 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgMethodConstructor.kt @@ -123,11 +123,7 @@ class PythonCgMethodConstructor(context: CgContext) : CgMethodConstructor(contex generateResultAssertions() if (methodType == CgTestMethodType.PASSED_EXCEPTION) { - generateFieldStateAssertions( - stateAssertions, - assertThisObject, - execution.executableToCall ?: testSet.executableUnderTest - ) + generateFieldStateAssertions(stateAssertions, assertThisObject, testSet.executableUnderTest) } }