Skip to content

Commit 5659243

Browse files
committed
Refactor TestCaseGenerator and CodeGenerator
1 parent 9b95f55 commit 5659243

File tree

16 files changed

+262
-314
lines changed

16 files changed

+262
-314
lines changed

utbot-cli/src/main/kotlin/org/utbot/cli/BunchTestGeneratorCommand.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,15 @@ class BunchTestGeneratorCommand : GenerateTestsAbstractCommand(
9797
val targetMethods = classUnderTest.targetMethods()
9898
if (targetMethods.isEmpty()) return
9999

100-
initializeEngine(workingDirectory)
100+
val testCaseGenerator = initializeGenerator(workingDirectory)
101101

102102
// utContext is used in `generate`, `generateTest`, `generateReport`
103103
withUtContext(UtContext(classLoader)) {
104104

105105
val testClassName = "${classUnderTest.simpleName}Test"
106106

107107
val testSets = generateTestSets(
108+
testCaseGenerator,
108109
targetMethods,
109110
searchDirectory = workingDirectory,
110111
chosenClassesToMockAlways = (Mocker.defaultSuperClassesToMockAlwaysNames + classesToMockAlways)

utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,13 @@ abstract class GenerateTestsAbstractCommand(name: String, help: String) :
154154
classLoader.loadClass(classFqn).kotlin
155155

156156
protected fun generateTestSets(
157+
testCaseGenerator: TestCaseGenerator,
157158
targetMethods: List<UtMethod<*>>,
158159
sourceCodeFile: Path? = null,
159160
searchDirectory: Path,
160161
chosenClassesToMockAlways: Set<ClassId>
161162
): List<UtMethodTestSet> =
162-
TestCaseGenerator.generate(
163+
testCaseGenerator.generate(
163164
targetMethods,
164165
mockStrategy,
165166
chosenClassesToMockAlways,
@@ -190,30 +191,26 @@ abstract class GenerateTestsAbstractCommand(name: String, help: String) :
190191
classUnderTest
191192
).generateAsString(testSets, testClassname)
192193

193-
protected fun initializeEngine(workingDirectory: Path) {
194+
protected fun initializeGenerator(workingDirectory: Path): TestCaseGenerator {
194195
val classPathNormalized =
195196
classLoader.urLs.joinToString(separator = File.pathSeparator) { it.toPath().absolutePath }
196-
197-
// TODO: SAT-1566
198-
// Set UtSettings parameters.
197+
// TODO: SAT-1566 Set UtSettings parameters.
199198
UtSettings.treatOverflowAsError = treatOverflowAsError == TreatOverflowAsError.AS_ERROR
200199

201-
TestCaseGenerator.init(workingDirectory, classPathNormalized, System.getProperty("java.class.path"))
200+
return TestCaseGenerator(workingDirectory, classPathNormalized, System.getProperty("java.class.path"))
202201
}
203202

204203
private fun initializeCodeGenerator(testFramework: String, classUnderTest: KClass<*>): CodeGenerator {
205204
val generateWarningsForStaticMocking =
206205
forceStaticMocking == ForceStaticMocking.FORCE && staticsMocking is NoStaticMocking
207-
return CodeGenerator().apply {
208-
init(
209-
testFramework = testFrameworkByName(testFramework),
210-
classUnderTest = classUnderTest.java,
211-
codegenLanguage = codegenLanguage,
212-
staticsMocking = staticsMocking,
213-
forceStaticMocking = forceStaticMocking,
214-
generateWarningsForStaticMocking = generateWarningsForStaticMocking,
215-
)
216-
}
206+
return CodeGenerator(
207+
testFramework = testFrameworkByName(testFramework),
208+
classUnderTest = classUnderTest.java,
209+
codegenLanguage = codegenLanguage,
210+
staticsMocking = staticsMocking,
211+
forceStaticMocking = forceStaticMocking,
212+
generateWarningsForStaticMocking = generateWarningsForStaticMocking,
213+
)
217214
}
218215

219216
protected fun KClass<*>.targetMethods() =

utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsCommand.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class GenerateTestsCommand :
9292

9393
val classUnderTest: KClass<*> = loadClassBySpecifiedFqn(targetClassFqn)
9494
val targetMethods = classUnderTest.targetMethods()
95-
initializeEngine(workingDirectory)
95+
val testCaseGenerator = initializeGenerator(workingDirectory)
9696

9797
if (targetMethods.isEmpty()) {
9898
throw Exception("Nothing to process. No methods were provided")
@@ -103,6 +103,7 @@ class GenerateTestsCommand :
103103
val testClassName = output?.toPath()?.toFile()?.nameWithoutExtension
104104
?: "${classUnderTest.simpleName}Test"
105105
val testSets = generateTestSets(
106+
testCaseGenerator,
106107
targetMethods,
107108
Paths.get(sourceCodeFile),
108109
searchDirectory = workingDirectory,

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

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,7 @@ object UtBotJavaApi {
8282
}
8383

8484
return withUtContext(utContext) {
85-
val testGenerator = CodeGenerator().apply {
86-
init(
85+
val codeGenerator = CodeGenerator(
8786
classUnderTest = classUnderTest,
8887
testFramework = testFramework,
8988
mockFramework = mockFramework,
@@ -93,12 +92,8 @@ object UtBotJavaApi {
9392
generateWarningsForStaticMocking = generateWarningsForStaticMocking,
9493
testClassPackageName = testClassPackageName
9594
)
96-
}
9795

98-
testGenerator.generateAsString(
99-
testSets,
100-
destinationClassName
101-
)
96+
codeGenerator.generateAsString(testSets, destinationClassName)
10297
}
10398
}
10499

@@ -122,12 +117,8 @@ object UtBotJavaApi {
122117
val testSets: MutableList<UtMethodTestSet> = mutableListOf()
123118

124119
testSets.addAll(withUtContext(utContext) {
125-
TestCaseGenerator
126-
.apply {
127-
init(
128-
FileUtil.isolateClassFiles(classUnderTest.kotlin).toPath(), classpath, dependencyClassPath
129-
)
130-
}
120+
val buildPath = FileUtil.isolateClassFiles(classUnderTest.kotlin).toPath()
121+
TestCaseGenerator(buildPath, classpath, dependencyClassPath)
131122
.generate(
132123
methodsForAutomaticGeneration.map {
133124
toUtMethod(
@@ -189,12 +180,9 @@ object UtBotJavaApi {
189180
}
190181

191182
return withUtContext(UtContext(classUnderTest.classLoader)) {
192-
TestCaseGenerator
193-
.apply {
194-
init(
195-
FileUtil.isolateClassFiles(classUnderTest.kotlin).toPath(), classpath, dependencyClassPath
196-
)
197-
}.generate(
183+
val buildPath = FileUtil.isolateClassFiles(classUnderTest.kotlin).toPath()
184+
TestCaseGenerator(buildPath, classpath, dependencyClassPath)
185+
.generate(
198186
methodsForAutomaticGeneration.map {
199187
toUtMethod(
200188
it.methodToBeTestedFromUserInput,

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/CodeGenerator.kt

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,42 +18,36 @@ import org.utbot.framework.plugin.api.UtMethod
1818
import org.utbot.framework.plugin.api.UtMethodTestSet
1919
import org.utbot.framework.plugin.api.util.id
2020

21-
class CodeGenerator {
22-
private lateinit var context: CgContext
23-
24-
fun init(
25-
classUnderTest: Class<*>,
26-
params: MutableMap<UtMethod<*>, List<String>> = mutableMapOf(),
27-
testFramework: TestFramework = TestFramework.defaultItem,
28-
mockFramework: MockFramework? = MockFramework.defaultItem,
29-
staticsMocking: StaticsMocking = StaticsMocking.defaultItem,
30-
forceStaticMocking: ForceStaticMocking = ForceStaticMocking.defaultItem,
31-
generateWarningsForStaticMocking: Boolean = true,
32-
codegenLanguage: CodegenLanguage = CodegenLanguage.defaultItem,
33-
parameterizedTestSource: ParametrizedTestSource = ParametrizedTestSource.defaultItem,
34-
runtimeExceptionTestsBehaviour: RuntimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.defaultItem,
35-
hangingTestsTimeout: HangingTestsTimeout = HangingTestsTimeout(),
36-
enableTestsTimeout: Boolean = true,
37-
testClassPackageName: String = classUnderTest.packageName,
38-
) {
39-
context = CgContext(
40-
classUnderTest = classUnderTest.id,
41-
// TODO: remove existingNames parameter completely
42-
existingMethodNames = mutableSetOf(),
43-
paramNames = params,
44-
testFramework = testFramework,
45-
mockFramework = mockFramework ?: MockFramework.MOCKITO,
46-
codegenLanguage = codegenLanguage,
47-
parameterizedTestSource = parameterizedTestSource,
48-
staticsMocking = staticsMocking,
49-
forceStaticMocking = forceStaticMocking,
50-
generateWarningsForStaticMocking = generateWarningsForStaticMocking,
51-
runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour,
52-
hangingTestsTimeout = hangingTestsTimeout,
53-
enableTestsTimeout = enableTestsTimeout,
54-
testClassPackageName = testClassPackageName
55-
)
56-
}
21+
class CodeGenerator(
22+
private val classUnderTest: Class<*>,
23+
params: MutableMap<UtMethod<*>, List<String>> = mutableMapOf(),
24+
testFramework: TestFramework = TestFramework.defaultItem,
25+
mockFramework: MockFramework? = MockFramework.defaultItem,
26+
staticsMocking: StaticsMocking = StaticsMocking.defaultItem,
27+
forceStaticMocking: ForceStaticMocking = ForceStaticMocking.defaultItem,
28+
generateWarningsForStaticMocking: Boolean = true,
29+
codegenLanguage: CodegenLanguage = CodegenLanguage.defaultItem,
30+
parameterizedTestSource: ParametrizedTestSource = ParametrizedTestSource.defaultItem,
31+
runtimeExceptionTestsBehaviour: RuntimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.defaultItem,
32+
hangingTestsTimeout: HangingTestsTimeout = HangingTestsTimeout(),
33+
enableTestsTimeout: Boolean = true,
34+
testClassPackageName: String = classUnderTest.packageName,
35+
) {
36+
private var context: CgContext = CgContext(
37+
classUnderTest = classUnderTest.id,
38+
paramNames = params,
39+
testFramework = testFramework,
40+
mockFramework = mockFramework ?: MockFramework.MOCKITO,
41+
codegenLanguage = codegenLanguage,
42+
parameterizedTestSource = parameterizedTestSource,
43+
staticsMocking = staticsMocking,
44+
forceStaticMocking = forceStaticMocking,
45+
generateWarningsForStaticMocking = generateWarningsForStaticMocking,
46+
runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour,
47+
hangingTestsTimeout = hangingTestsTimeout,
48+
enableTestsTimeout = enableTestsTimeout,
49+
testClassPackageName = testClassPackageName
50+
)
5751

5852
//TODO: we support custom test class name only in utbot-online, probably support them in plugin as well
5953
fun generateAsString(testSets: Collection<UtMethodTestSet>, testClassCustomName: String? = null): String =

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

Lines changed: 51 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
package org.utbot.framework.plugin.api
22

3-
import org.utbot.common.FileUtil
3+
import kotlinx.coroutines.CoroutineScope
4+
import kotlinx.coroutines.GlobalScope
5+
import kotlinx.coroutines.cancel
6+
import kotlinx.coroutines.flow.Flow
7+
import kotlinx.coroutines.flow.collect
8+
import kotlinx.coroutines.flow.flattenConcat
9+
import kotlinx.coroutines.flow.flowOf
10+
import kotlinx.coroutines.isActive
11+
import kotlinx.coroutines.launch
12+
import kotlinx.coroutines.runBlocking
13+
import kotlinx.coroutines.yield
14+
import mu.KLogger
15+
import mu.KotlinLogging
416
import org.utbot.common.bracket
517
import org.utbot.common.runBlockingWithCancellationPredicate
618
import org.utbot.common.runIgnoringCancellationException
719
import org.utbot.common.trace
820
import org.utbot.engine.EngineController
921
import org.utbot.engine.Mocker
22+
import org.utbot.engine.UtBotSymbolicEngine
1023
import org.utbot.framework.TestSelectionStrategyType
1124
import org.utbot.framework.UtSettings
1225
import org.utbot.framework.UtSettings.checkSolverTimeoutMillis
@@ -34,85 +47,52 @@ import java.util.*
3447
import kotlin.coroutines.cancellation.CancellationException
3548
import kotlin.math.min
3649
import kotlin.reflect.KCallable
37-
import kotlinx.coroutines.CoroutineScope
38-
import kotlinx.coroutines.GlobalScope
39-
import kotlinx.coroutines.cancel
40-
import kotlinx.coroutines.flow.Flow
41-
import kotlinx.coroutines.flow.collect
42-
import kotlinx.coroutines.flow.flattenConcat
43-
import kotlinx.coroutines.flow.flowOf
44-
import kotlinx.coroutines.isActive
45-
import kotlinx.coroutines.launch
46-
import kotlinx.coroutines.runBlocking
47-
import kotlinx.coroutines.yield
48-
import mu.KotlinLogging
49-
import org.utbot.engine.UtBotSymbolicEngine
50-
51-
object TestCaseGenerator {
5250

53-
private val logger = KotlinLogging.logger {}
54-
private val timeoutLogger = KotlinLogging.logger(logger.name + ".timeout")
51+
open class TestCaseGenerator(
52+
private val buildDir: Path,
53+
private val classpath: String?,
54+
private val dependencyPaths: String,
55+
val engineActions: MutableList<(UtBotSymbolicEngine) -> Unit> = mutableListOf(),
56+
val isCanceled: () -> Boolean = { false },
57+
) {
58+
private lateinit var logger: KLogger
59+
private lateinit var timeoutLogger: KLogger
5560

56-
lateinit var engineActions: MutableList<(UtBotSymbolicEngine) -> Unit>
57-
lateinit var isCanceled: () -> Boolean
61+
init {
62+
if (!isCanceled()) {
63+
checkFrameworkDependencies(dependencyPaths)
5864

59-
//properties to save time on soot initialization
60-
private var previousBuildDir: Path? = null
61-
private var previousClasspath: String? = null
62-
private var previousTimestamp: Long? = null
63-
private var dependencyPaths: String = ""
65+
logger = KotlinLogging.logger {}
66+
timeoutLogger = KotlinLogging.logger(logger.name + ".timeout")
6467

65-
fun init(
66-
buildDir: Path,
67-
classpath: String?,
68-
dependencyPaths: String,
69-
engineActions: MutableList<(UtBotSymbolicEngine) -> Unit> = mutableListOf(),
70-
isCanceled: () -> Boolean = { false },
71-
) {
72-
this.isCanceled = isCanceled
73-
this.engineActions = engineActions
74-
if (isCanceled()) return
68+
logger.trace("Initializing ${this.javaClass.name} with buildDir = $buildDir, classpath = $classpath")
7569

76-
checkFrameworkDependencies(dependencyPaths)
7770

78-
logger.trace("Initializing ${this.javaClass.name} with buildDir = $buildDir, classpath = $classpath")
79-
80-
//optimization: maxLastModifiedRecursivelyMillis can take time
81-
val timestamp = if (UtSettings.classfilesCanChange) maxLastModifiedRecursivelyMillis(buildDir, classpath) else 0
82-
if (buildDir == previousBuildDir && classpath == previousClasspath && timestamp == previousTimestamp) {
83-
logger.info { "Ignoring soot initialization because parameters are the same as on previous initialization" }
84-
return
85-
}
86-
87-
if (disableCoroutinesDebug) {
88-
System.setProperty(kotlinx.coroutines.DEBUG_PROPERTY_NAME, kotlinx.coroutines.DEBUG_PROPERTY_VALUE_OFF)
89-
}
71+
if (disableCoroutinesDebug) {
72+
System.setProperty(kotlinx.coroutines.DEBUG_PROPERTY_NAME, kotlinx.coroutines.DEBUG_PROPERTY_VALUE_OFF)
73+
}
9074

91-
timeoutLogger.trace().bracket("Soot initialization") {
92-
runSoot(buildDir, classpath)
93-
}
75+
timeoutLogger.trace().bracket("Soot initialization") {
76+
runSoot(buildDir, classpath)
77+
}
9478

95-
previousBuildDir = buildDir
96-
previousClasspath = classpath
97-
previousTimestamp = timestamp
98-
this.dependencyPaths = dependencyPaths
99-
100-
//warmup
101-
if (warmupConcreteExecution) {
102-
ConcreteExecutor(
103-
UtExecutionInstrumentation,
104-
classpathForEngine,
105-
dependencyPaths
106-
).apply {
107-
classLoader = utContext.classLoader
108-
withUtContext(UtContext(Warmup::class.java.classLoader)) {
109-
runBlocking {
110-
constructExecutionsForWarmup().forEach { (method, data) ->
111-
executeAsync(method, emptyArray(), data)
79+
//warmup
80+
if (warmupConcreteExecution) {
81+
ConcreteExecutor(
82+
UtExecutionInstrumentation,
83+
classpathForEngine(),
84+
dependencyPaths
85+
).apply {
86+
classLoader = utContext.classLoader
87+
withUtContext(UtContext(Warmup::class.java.classLoader)) {
88+
runBlocking {
89+
constructExecutionsForWarmup().forEach { (method, data) ->
90+
executeAsync(method, emptyArray(), data)
91+
}
11292
}
11393
}
94+
warmup()
11495
}
115-
warmup()
11696
}
11797
}
11898
}
@@ -254,17 +234,7 @@ object TestCaseGenerator {
254234
)
255235
}
256236

257-
private val classpathForEngine: String
258-
get() = previousBuildDir!!.toString() + (previousClasspath?.let { File.pathSeparator + it } ?: "")
259-
260-
private fun maxLastModifiedRecursivelyMillis(buildDir: Path, classpath: String?): Long {
261-
val paths = mutableListOf<File>()
262-
paths += buildDir.toFile()
263-
if (classpath != null) {
264-
paths += classpath.split(File.pathSeparatorChar).map { File(it) }
265-
}
266-
return FileUtil.maxLastModifiedRecursivelyMillis(paths)
267-
}
237+
private fun classpathForEngine() = buildDir.toString() + (classpath?.let { File.pathSeparator + it } ?: "")
268238

269239
private fun createSymbolicEngine(
270240
controller: EngineController,
@@ -278,7 +248,7 @@ object TestCaseGenerator {
278248
return UtBotSymbolicEngine(
279249
controller,
280250
method,
281-
classpathForEngine,
251+
classpathForEngine(),
282252
dependencyPaths = dependencyPaths,
283253
mockStrategy = mockStrategyApi.toModel(),
284254
chosenClassesToMockAlways = chosenClassesToMockAlways,

0 commit comments

Comments
 (0)