Skip to content

Commit 49b7bad

Browse files
committed
Java API for the engine should be extended to support Spring-aware generation #2613 (#2614)
1 parent be47d83 commit 49b7bad

File tree

5 files changed

+216
-20
lines changed

5 files changed

+216
-20
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/src/main/kotlin/org/utbot/external/api/UtBotJavaApi.kt

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import org.utbot.framework.codegen.domain.TestFramework
1212
import org.utbot.framework.codegen.generator.CodeGenerator
1313
import org.utbot.framework.codegen.generator.CodeGeneratorParams
1414
import org.utbot.framework.codegen.services.language.CgLanguageAssistant
15-
import org.utbot.framework.context.simple.SimpleApplicationContext
15+
import org.utbot.framework.context.ApplicationContext
16+
import org.utbot.framework.context.simple.DefaultSimpleApplicationContext
1617
import org.utbot.framework.context.utils.transformValueProvider
1718
import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionData
1819
import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult
@@ -36,12 +37,9 @@ import org.utbot.framework.plugin.api.util.stringClassId
3637
import org.utbot.framework.plugin.api.util.withUtContext
3738
import org.utbot.framework.plugin.api.util.wrapperByPrimitive
3839
import org.utbot.framework.plugin.services.JdkInfoDefaultProvider
39-
import org.utbot.fuzzer.FuzzedType
4040
import org.utbot.fuzzer.FuzzedValue
41-
import org.utbot.fuzzing.FuzzedDescription
4241
import org.utbot.fuzzing.JavaValueProvider
4342
import org.utbot.fuzzing.Seed
44-
import org.utbot.fuzzing.ValueProvider
4543
import org.utbot.instrumentation.ConcreteExecutor
4644
import org.utbot.instrumentation.execute
4745
import org.utbot.instrumentation.instrumentation.execution.SimpleUtExecutionInstrumentation
@@ -120,16 +118,22 @@ object UtBotJavaApi {
120118
classpath: String,
121119
dependencyClassPath: String,
122120
mockStrategyApi: MockStrategyApi = MockStrategyApi.OTHER_PACKAGES,
123-
generationTimeoutInMillis: Long = UtSettings.utBotGenerationTimeoutInMillis
121+
generationTimeoutInMillis: Long = UtSettings.utBotGenerationTimeoutInMillis,
122+
applicationContext: ApplicationContext = DefaultSimpleApplicationContext
124123
): MutableList<UtMethodTestSet> {
125124

126125
val utContext = UtContext(classUnderTest.classLoader)
127126
val testSets: MutableList<UtMethodTestSet> = mutableListOf()
128127

129128
testSets.addAll(withUtContext(utContext) {
130129
val buildPath = FileUtil.isolateClassFiles(classUnderTest).toPath()
131-
TestCaseGenerator(listOf(buildPath), classpath, dependencyClassPath, jdkInfo = JdkInfoDefaultProvider().info)
132-
.generate(
130+
TestCaseGenerator(
131+
listOf(buildPath),
132+
classpath,
133+
dependencyClassPath,
134+
jdkInfo = JdkInfoDefaultProvider().info,
135+
applicationContext = applicationContext
136+
).generate(
133137
methodsForAutomaticGeneration.map {
134138
it.methodToBeTestedFromUserInput.executableId
135139
},
@@ -156,7 +160,8 @@ object UtBotJavaApi {
156160
dependencyClassPath: String,
157161
mockStrategyApi: MockStrategyApi = MockStrategyApi.OTHER_PACKAGES,
158162
generationTimeoutInMillis: Long = UtSettings.utBotGenerationTimeoutInMillis,
159-
primitiveValuesSupplier: CustomFuzzerValueSupplier = CustomFuzzerValueSupplier { null }
163+
primitiveValuesSupplier: CustomFuzzerValueSupplier = CustomFuzzerValueSupplier { null },
164+
applicationContext: ApplicationContext = DefaultSimpleApplicationContext
160165
): MutableList<UtMethodTestSet> {
161166
fun createPrimitiveModels(supplier: CustomFuzzerValueSupplier, classId: ClassId): Sequence<UtPrimitiveModel> =
162167
supplier
@@ -189,7 +194,7 @@ object UtBotJavaApi {
189194
classpath,
190195
dependencyClassPath,
191196
jdkInfo = JdkInfoDefaultProvider().info,
192-
applicationContext = SimpleApplicationContext().transformValueProvider { defaultModelProvider ->
197+
applicationContext = applicationContext.transformValueProvider { defaultModelProvider ->
193198
customModelProvider.withFallback(defaultModelProvider)
194199
}
195200
)

utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleApplicationContext.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import org.utbot.framework.context.TypeReplacer
1212
/**
1313
* A context to use when no specific data is required.
1414
*/
15-
class SimpleApplicationContext(
15+
open class SimpleApplicationContext(
1616
override val mockerContext: MockerContext = SimpleMockerContext(
1717
mockFrameworkInstalled = true,
1818
staticsMockingIsConfigured = true
@@ -27,4 +27,6 @@ class SimpleApplicationContext(
2727

2828
override fun createCodeGenerator(params: CodeGeneratorParams): AbstractCodeGenerator =
2929
CodeGenerator(params)
30-
}
30+
}
31+
32+
object DefaultSimpleApplicationContext : SimpleApplicationContext()

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

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ import org.utbot.framework.UtSettings.disableCoroutinesDebug
2626
import org.utbot.framework.UtSettings.utBotGenerationTimeoutInMillis
2727
import org.utbot.framework.UtSettings.warmupConcreteExecution
2828
import org.utbot.framework.context.ApplicationContext
29-
import org.utbot.framework.context.simple.SimpleApplicationContext
30-
import org.utbot.framework.context.simple.SimpleMockerContext
29+
import org.utbot.framework.context.simple.DefaultSimpleApplicationContext
3130
import org.utbot.framework.plugin.api.utils.checkFrameworkDependencies
3231
import org.utbot.framework.minimization.minimizeTestCase
3332
import org.utbot.framework.plugin.api.util.id
@@ -66,12 +65,7 @@ open class TestCaseGenerator(
6665
val engineActions: MutableList<(UtBotSymbolicEngine) -> Unit> = mutableListOf(),
6766
val isCanceled: () -> Boolean = { false },
6867
val forceSootReload: Boolean = true,
69-
val applicationContext: ApplicationContext = SimpleApplicationContext(
70-
SimpleMockerContext(
71-
mockFrameworkInstalled = true,
72-
staticsMockingIsConfigured = true
73-
)
74-
),
68+
val applicationContext: ApplicationContext = DefaultSimpleApplicationContext,
7569
) {
7670
private val logger: KLogger = KotlinLogging.logger {}
7771
private val timeoutLogger: KLogger = KotlinLogging.logger(logger.name + ".timeout")
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package org.utibot.examples.spring;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
import org.junit.jupiter.api.AfterEach;
5+
import org.junit.jupiter.api.Assertions;
6+
import org.junit.jupiter.api.BeforeEach;
7+
import org.junit.jupiter.api.Test;
8+
import org.utbot.common.PathUtil;
9+
import org.utbot.examples.spring.autowiring.oneBeanForOneType.Order;
10+
import org.utbot.examples.spring.autowiring.oneBeanForOneType.ServiceWithInjectedField;
11+
import org.utbot.external.api.TestMethodInfo;
12+
import org.utbot.external.api.UtBotJavaApi;
13+
import org.utbot.external.api.UtModelFactory;
14+
import org.utbot.framework.codegen.domain.ForceStaticMocking;
15+
import org.utbot.framework.codegen.domain.Junit4;
16+
import org.utbot.framework.codegen.domain.MockitoStaticMocking;
17+
import org.utbot.framework.codegen.domain.ProjectType;
18+
import org.utbot.framework.context.ApplicationContext;
19+
import org.utbot.framework.context.simple.SimpleApplicationContext;
20+
import org.utbot.framework.context.spring.SpringApplicationContextImpl;
21+
import org.utbot.framework.plugin.api.*;
22+
import org.utbot.framework.util.Snippet;
23+
24+
import java.io.File;
25+
import java.lang.reflect.Method;
26+
import java.net.URISyntaxException;
27+
import java.net.URL;
28+
import java.util.Arrays;
29+
import java.util.Collections;
30+
import java.util.HashMap;
31+
import java.util.List;
32+
import java.util.stream.Collectors;
33+
34+
import static org.utbot.external.api.UtModelFactoryKt.classIdForType;
35+
import static org.utbot.framework.plugin.api.MockFramework.MOCKITO;
36+
import static org.utbot.framework.util.TestUtilsKt.compileClassFile;
37+
38+
39+
class PredefinedGeneratorParameters {
40+
41+
static String destinationClassName = "GeneratedTest";
42+
43+
static Method getMethodByName(Class<?> clazz, String name, Class<?>... parameters) {
44+
try {
45+
return clazz.getDeclaredMethod(name, parameters);
46+
} catch (NoSuchMethodException ignored) {
47+
Assertions.fail();
48+
}
49+
throw new RuntimeException();
50+
}
51+
}
52+
53+
public class SpringUtBotJavaApiTest {
54+
private UtModelFactory modelFactory;
55+
56+
@BeforeEach
57+
public void setUp() {
58+
modelFactory = new UtModelFactory();
59+
}
60+
61+
@AfterEach
62+
public void tearDown() {
63+
try {
64+
modelFactory = null;
65+
} catch (Exception e) {
66+
Assertions.fail();
67+
}
68+
}
69+
70+
@NotNull
71+
private static String normalizePath(URL url) throws URISyntaxException {
72+
return new File(url.toURI()).getPath();
73+
}
74+
75+
@NotNull
76+
private static String getClassPath(Class<?> clazz) {
77+
try {
78+
return normalizePath(clazz.getProtectionDomain().getCodeSource().getLocation());
79+
} catch (URISyntaxException e) {
80+
throw new RuntimeException(e);
81+
}
82+
}
83+
84+
@NotNull
85+
private static String getDependencyClassPath() {
86+
87+
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
88+
URL[] urls = PathUtil.getUrlsFromClassLoader(contextClassLoader);
89+
90+
91+
return Arrays.stream(urls).map(url ->
92+
{
93+
try {
94+
return new File(url.toURI()).toString();
95+
} catch (URISyntaxException e) {
96+
Assertions.fail(e);
97+
}
98+
throw new RuntimeException();
99+
}).collect(Collectors.joining(File.pathSeparator));
100+
}
101+
102+
@Test
103+
public void testMultiMethodClass() {
104+
105+
UtBotJavaApi.setStopConcreteExecutorOnExit(false);
106+
107+
String classpath = getClassPath(ServiceWithInjectedField.class);
108+
String dependencyClassPath = getDependencyClassPath();
109+
110+
// Order clas
111+
HashMap<String, UtModel> fields = new HashMap<>();
112+
fields.put("id", new UtPrimitiveModel(0L));
113+
fields.put("buyer", new UtPrimitiveModel(""));
114+
fields.put("price", new UtPrimitiveModel(0d));
115+
fields.put("qty", new UtPrimitiveModel(0));
116+
117+
UtCompositeModel orderClassModel = modelFactory.produceCompositeModel(
118+
classIdForType(Order.class),
119+
fields
120+
);
121+
122+
UtCompositeModel classUnderTestModel = modelFactory.produceCompositeModel(
123+
classIdForType(ServiceWithInjectedField.class)
124+
);
125+
126+
EnvironmentModels createOrderMethodState = new EnvironmentModels(
127+
classUnderTestModel,
128+
Collections.emptyList(),
129+
Collections.emptyMap()
130+
);
131+
132+
EnvironmentModels getOrdersMethodState = new EnvironmentModels(
133+
classUnderTestModel,
134+
Collections.singletonList(orderClassModel),
135+
Collections.emptyMap()
136+
);
137+
138+
139+
Method getOrdersMethodUnderTest = PredefinedGeneratorParameters.getMethodByName(
140+
ServiceWithInjectedField.class,
141+
"getOrders"
142+
);
143+
144+
Method createOrderMethodUnderTest = PredefinedGeneratorParameters.getMethodByName(
145+
ServiceWithInjectedField.class,
146+
"createOrder",
147+
Order.class
148+
);
149+
150+
TestMethodInfo firstTestMethodInfo = new TestMethodInfo(
151+
createOrderMethodUnderTest,
152+
createOrderMethodState);
153+
154+
TestMethodInfo secondTestMethodInfo = new TestMethodInfo(
155+
getOrdersMethodUnderTest,
156+
getOrdersMethodState);
157+
158+
ApplicationContext springApplicationContext = new SpringApplicationContextImpl(
159+
new SimpleApplicationContext(),
160+
Collections.emptyList(),
161+
SpringTestType.UNIT_TEST,
162+
SpringSettings.AbsentSpringSettings
163+
);
164+
165+
List<UtMethodTestSet> testSets = UtBotJavaApi.generateTestSets(
166+
Arrays.asList(firstTestMethodInfo, secondTestMethodInfo, secondTestMethodInfo),
167+
ServiceWithInjectedField.class,
168+
classpath,
169+
dependencyClassPath,
170+
MockStrategyApi.OTHER_PACKAGES,
171+
60000L,
172+
springApplicationContext
173+
);
174+
175+
String generationResult = UtBotJavaApi.generate(
176+
Collections.emptyList(),
177+
testSets,
178+
PredefinedGeneratorParameters.destinationClassName,
179+
classpath,
180+
dependencyClassPath,
181+
ServiceWithInjectedField.class,
182+
ProjectType.PureJvm,
183+
Junit4.INSTANCE,
184+
MOCKITO,
185+
CodegenLanguage.JAVA,
186+
MockitoStaticMocking.INSTANCE,
187+
false,
188+
ForceStaticMocking.DO_NOT_FORCE,
189+
ServiceWithInjectedField.class.getPackage().getName()
190+
);
191+
192+
Snippet snippet = new Snippet(CodegenLanguage.JAVA, generationResult);
193+
compileClassFile(PredefinedGeneratorParameters.destinationClassName, snippet);
194+
}
195+
}

0 commit comments

Comments
 (0)