Skip to content

Commit a819d13

Browse files
committed
Extend Java API for Spring-aware generation #2614
1 parent 4e5ba7f commit a819d13

File tree

10 files changed

+725
-686
lines changed

10 files changed

+725
-686
lines changed

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1439,7 +1439,7 @@ sealed class SpringConfiguration(val fullDisplayName: String) {
14391439
}
14401440

14411441
sealed interface SpringSettings {
1442-
object AbsentSpringSettings : SpringSettings {
1442+
companion object AbsentSpringSettings : SpringSettings {
14431443
// NOTE that overriding equals is required just because without it
14441444
// we will lose equality for objects after deserialization
14451445
override fun equals(other: Any?): Boolean = other is AbsentSpringSettings

utbot-framework-test/src/test/java/org/utbot/examples/manual/UtBotJavaApiTest.java

Lines changed: 371 additions & 630 deletions
Large diffs are not rendered by default.

utbot-framework/src/main/kotlin/org/utbot/external/api/UtBotJavaApi.kt

Lines changed: 69 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,14 @@ package org.utbot.external.api
33
import org.utbot.common.FileUtil
44
import org.utbot.common.nameOfPackage
55
import org.utbot.framework.UtSettings
6-
import org.utbot.framework.codegen.domain.ForceStaticMocking
7-
import org.utbot.framework.codegen.domain.Junit5
8-
import org.utbot.framework.codegen.domain.NoStaticMocking
9-
import org.utbot.framework.codegen.domain.ProjectType
10-
import org.utbot.framework.codegen.domain.StaticsMocking
11-
import org.utbot.framework.codegen.domain.TestFramework
12-
import org.utbot.framework.codegen.generator.CodeGenerator
6+
import org.utbot.framework.codegen.domain.*
137
import org.utbot.framework.codegen.generator.CodeGeneratorParams
148
import org.utbot.framework.codegen.services.language.CgLanguageAssistant
15-
import org.utbot.framework.context.simple.SimpleApplicationContext
9+
import org.utbot.framework.context.ApplicationContext
1610
import org.utbot.framework.context.utils.transformValueProvider
1711
import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionData
1812
import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult
1913
import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation
20-
import org.utbot.framework.plugin.api.ClassId
21-
import org.utbot.framework.plugin.api.CodegenLanguage
22-
import org.utbot.framework.plugin.api.MockFramework
23-
import org.utbot.framework.plugin.api.MockStrategyApi
24-
import org.utbot.framework.plugin.api.TestCaseGenerator
25-
import org.utbot.framework.plugin.api.UtMethodTestSet
26-
import org.utbot.framework.plugin.api.UtPrimitiveModel
27-
import org.utbot.framework.plugin.api.UtSymbolicExecution
2814
import org.utbot.framework.plugin.api.util.UtContext
2915
import org.utbot.framework.plugin.api.util.executableId
3016
import org.utbot.framework.plugin.api.util.id
@@ -36,26 +22,54 @@ import org.utbot.framework.plugin.api.util.stringClassId
3622
import org.utbot.framework.plugin.api.util.withUtContext
3723
import org.utbot.framework.plugin.api.util.wrapperByPrimitive
3824
import org.utbot.framework.plugin.services.JdkInfoDefaultProvider
39-
import org.utbot.fuzzer.FuzzedType
4025
import org.utbot.fuzzer.FuzzedValue
41-
import org.utbot.fuzzing.FuzzedDescription
4226
import org.utbot.fuzzing.JavaValueProvider
4327
import org.utbot.fuzzing.Seed
44-
import org.utbot.fuzzing.ValueProvider
4528
import org.utbot.instrumentation.ConcreteExecutor
4629
import org.utbot.instrumentation.execute
47-
import org.utbot.instrumentation.instrumentation.execution.SimpleUtExecutionInstrumentation
48-
import java.io.File
4930
import kotlin.reflect.jvm.kotlinFunction
31+
import org.utbot.framework.codegen.domain.StaticsMocking
32+
import org.utbot.framework.plugin.api.*
33+
import java.lang.reflect.Method
5034

5135
object UtBotJavaApi {
5236

37+
/**
38+
* For running tests it could be reasonable to reuse the same concrete executor
39+
*/
5340
@JvmStatic
5441
var stopConcreteExecutorOnExit: Boolean = true
5542

43+
44+
/**
45+
* Generates test code
46+
* @param methodsForGeneration specify methods that are supposed to be executed concretely.
47+
* In order to execute method you are supposed to provide some
48+
* values to pass in it this is why we use [TestMethodInfo] here.
49+
* @param generatedTestCases specify [UtMethodTestSet]s that are used for test code
50+
* generation. By comparison with the first parameter,
51+
* {@code UtMethodTestSet} contains more information about
52+
* test, including result of the executions. Note, that
53+
* you can get the object with any sort of analysis,
54+
* for instance, symbolic or fuzz execution.
55+
* @param destinationClassName the name of containing class for the generated tests
56+
* @param classpath classpath that are used to build the class under test
57+
* @param dependencyClassPath class path including dependencies required for the code generation
58+
* @param classUnderTest for this class test should be generated
59+
* @param projectType JVM, Spring, Python, or other type of project
60+
* @param testFramework test framework that is going to be used for running generated tests
61+
* @param mockFramework framework that will be used in the generated tests
62+
* @param codegenLanguage the target language of the test generation. It can be different from the source language.
63+
* @param staticsMocking the approach to the statics mocking
64+
* @param generateWarningsForStaticMocking enable generation of warning about forced static mocking in comments
65+
* of generated tests.
66+
* @param forceStaticMocking enables static mocking
67+
* @param testClassPackageName package name for the generated class with the tests
68+
* @param applicationContext specify application context here
69+
*/
5670
@JvmStatic
5771
@JvmOverloads
58-
fun generate(
72+
fun generateTestCode(
5973
methodsForGeneration: List<TestMethodInfo>,
6074
generatedTestCases: List<UtMethodTestSet> = mutableListOf(),
6175
destinationClassName: String,
@@ -69,16 +83,18 @@ object UtBotJavaApi {
6983
staticsMocking: StaticsMocking = NoStaticMocking,
7084
generateWarningsForStaticMocking: Boolean = false,
7185
forceStaticMocking: ForceStaticMocking = ForceStaticMocking.DO_NOT_FORCE,
72-
testClassPackageName: String = classUnderTest.nameOfPackage
86+
testClassPackageName: String = classUnderTest.nameOfPackage,
87+
applicationContext: ApplicationContext
7388
): String {
7489

75-
val utContext = UtContext(classUnderTest.classLoader)
76-
7790
val testSets: MutableList<UtMethodTestSet> = generatedTestCases.toMutableList()
7891

7992
val concreteExecutor = ConcreteExecutor(
80-
SimpleUtExecutionInstrumentation.Factory(pathsToUserClasses = classpath.split(File.pathSeparator).toSet()),
81-
classpath,
93+
applicationContext.createConcreteExecutionContext(
94+
fullClasspath = dependencyClassPath,
95+
classpathWithoutDependencies = classpath
96+
).instrumentationFactory,
97+
classpath
8298
)
8399

84100
testSets.addAll(generateUnitTests(concreteExecutor, methodsForGeneration, classUnderTest))
@@ -87,8 +103,8 @@ object UtBotJavaApi {
87103
concreteExecutor.close()
88104
}
89105

90-
return withUtContext(utContext) {
91-
val codeGenerator = CodeGenerator(
106+
return withUtContext(UtContext(classUnderTest.classLoader)) {
107+
applicationContext.createCodeGenerator(
92108
CodeGeneratorParams(
93109
classUnderTest = classUnderTest.id,
94110
projectType = projectType,
@@ -99,11 +115,9 @@ object UtBotJavaApi {
99115
staticsMocking = staticsMocking,
100116
forceStaticMocking = forceStaticMocking,
101117
generateWarningsForStaticMocking = generateWarningsForStaticMocking,
102-
testClassPackageName = testClassPackageName
118+
testClassPackageName = testClassPackageName,
103119
)
104-
)
105-
106-
codeGenerator.generateAsString(testSets, destinationClassName)
120+
).generateAsString(testSets, destinationClassName)
107121
}
108122
}
109123

@@ -114,29 +128,33 @@ object UtBotJavaApi {
114128
*/
115129
@JvmStatic
116130
@JvmOverloads
117-
fun generateTestSets(
118-
methodsForAutomaticGeneration: List<TestMethodInfo>,
131+
fun generateTestSetsForMethods(
132+
methodsToAnalyze: List<Method>,
119133
classUnderTest: Class<*>,
120134
classpath: String,
121135
dependencyClassPath: String,
122136
mockStrategyApi: MockStrategyApi = MockStrategyApi.OTHER_PACKAGES,
123-
generationTimeoutInMillis: Long = UtSettings.utBotGenerationTimeoutInMillis
137+
generationTimeoutInMillis: Long = UtSettings.utBotGenerationTimeoutInMillis,
138+
applicationContext: ApplicationContext
124139
): MutableList<UtMethodTestSet> {
125140

126141
val utContext = UtContext(classUnderTest.classLoader)
127142
val testSets: MutableList<UtMethodTestSet> = mutableListOf()
128143

129144
testSets.addAll(withUtContext(utContext) {
130145
val buildPath = FileUtil.isolateClassFiles(classUnderTest).toPath()
131-
TestCaseGenerator(listOf(buildPath), classpath, dependencyClassPath, jdkInfo = JdkInfoDefaultProvider().info)
132-
.generate(
133-
methodsForAutomaticGeneration.map {
134-
it.methodToBeTestedFromUserInput.executableId
135-
},
136-
mockStrategyApi,
137-
chosenClassesToMockAlways = emptySet(),
138-
generationTimeoutInMillis
139-
)
146+
TestCaseGenerator(
147+
listOf(buildPath),
148+
classpath,
149+
dependencyClassPath,
150+
jdkInfo = JdkInfoDefaultProvider().info,
151+
applicationContext = applicationContext
152+
).generate(
153+
methodsToAnalyze.map { it.executableId },
154+
mockStrategyApi,
155+
chosenClassesToMockAlways = emptySet(),
156+
generationTimeoutInMillis
157+
)
140158
})
141159

142160
return testSets
@@ -145,18 +163,19 @@ object UtBotJavaApi {
145163
/**
146164
* Generates test cases using only fuzzing workflow.
147165
*
148-
* @see [generateTestSets]
166+
* @see [generateTestSetsForMethods]
149167
*/
150168
@JvmStatic
151169
@JvmOverloads
152170
fun fuzzingTestSets(
153-
methodsForAutomaticGeneration: List<TestMethodInfo>,
171+
methodsToAnalyze: List<Method>,
154172
classUnderTest: Class<*>,
155173
classpath: String,
156174
dependencyClassPath: String,
157175
mockStrategyApi: MockStrategyApi = MockStrategyApi.OTHER_PACKAGES,
158176
generationTimeoutInMillis: Long = UtSettings.utBotGenerationTimeoutInMillis,
159-
primitiveValuesSupplier: CustomFuzzerValueSupplier = CustomFuzzerValueSupplier { null }
177+
primitiveValuesSupplier: CustomFuzzerValueSupplier = CustomFuzzerValueSupplier { null },
178+
applicationContext: ApplicationContext
160179
): MutableList<UtMethodTestSet> {
161180
fun createPrimitiveModels(supplier: CustomFuzzerValueSupplier, classId: ClassId): Sequence<UtPrimitiveModel> =
162181
supplier
@@ -189,14 +208,12 @@ object UtBotJavaApi {
189208
classpath,
190209
dependencyClassPath,
191210
jdkInfo = JdkInfoDefaultProvider().info,
192-
applicationContext = SimpleApplicationContext().transformValueProvider { defaultModelProvider ->
211+
applicationContext = applicationContext.transformValueProvider { defaultModelProvider ->
193212
customModelProvider.withFallback(defaultModelProvider)
194213
}
195214
)
196215
.generate(
197-
methodsForAutomaticGeneration.map {
198-
it.methodToBeTestedFromUserInput.executableId
199-
},
216+
methodsToAnalyze.map{ it.executableId },
200217
mockStrategyApi,
201218
chosenClassesToMockAlways = emptySet(),
202219
generationTimeoutInMillis,
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package org.utbot.examples.spring.app;
2+
3+
public interface MyService {
4+
String getName();
5+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.utbot.examples.spring.app;
2+
3+
import org.springframework.stereotype.Service;
4+
5+
@Service
6+
public class MyServiceImpl implements MyService {
7+
@Override
8+
public String getName() {
9+
return "impl";
10+
}
11+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.utbot.examples.spring.app;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.stereotype.Service;
5+
6+
@Service
7+
public class MyServiceUser {
8+
private final MyService myService;
9+
10+
@Autowired
11+
public MyServiceUser(MyService myService) {
12+
this.myService = myService;
13+
}
14+
15+
public String useMyService() {
16+
return myService.getName();
17+
}
18+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.utbot.examples.spring.app;
2+
3+
import org.springframework.boot.autoconfigure.SpringBootApplication;
4+
5+
@SpringBootApplication
6+
public class SpringExampleApp {
7+
}

0 commit comments

Comments
 (0)