Skip to content

Commit 5ac552b

Browse files
authored
Generate separated parametrized tests for test sets with different result type #510 (#652)
1 parent 048d91b commit 5ac552b

File tree

7 files changed

+114
-45
lines changed

7 files changed

+114
-45
lines changed

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

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import org.utbot.framework.plugin.api.util.isPrimitive
2828
import org.utbot.framework.plugin.api.util.jClass
2929
import org.utbot.framework.plugin.api.util.longClassId
3030
import org.utbot.framework.plugin.api.util.method
31+
import org.utbot.framework.plugin.api.util.objectClassId
3132
import org.utbot.framework.plugin.api.util.primitiveTypeJvmNameOrNull
3233
import org.utbot.framework.plugin.api.util.safeJField
3334
import org.utbot.framework.plugin.api.util.shortClassId
@@ -98,22 +99,6 @@ data class UtMethodTestSet(
9899
val clustersInfo: List<Pair<UtClusterInfo?, IntRange>> = listOf(null to executions.indices)
99100
)
100101

101-
data class CgMethodTestSet private constructor(
102-
val executableId: ExecutableId,
103-
val executions: List<UtExecution> = emptyList(),
104-
val jimpleBody: JimpleBody? = null,
105-
val errors: Map<String, Int> = emptyMap(),
106-
val clustersInfo: List<Pair<UtClusterInfo?, IntRange>> = listOf(null to executions.indices)
107-
) {
108-
constructor(from: UtMethodTestSet) : this(
109-
from.method.callable.executableId,
110-
from.executions,
111-
from.jimpleBody,
112-
from.errors,
113-
from.clustersInfo
114-
)
115-
}
116-
117102
data class Step(
118103
val stmt: Stmt,
119104
val depth: Int,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import org.utbot.framework.codegen.ParametrizedTestSource
66
import org.utbot.framework.codegen.RuntimeExceptionTestsBehaviour
77
import org.utbot.framework.codegen.StaticsMocking
88
import org.utbot.framework.codegen.TestFramework
9+
import org.utbot.framework.codegen.model.constructor.CgMethodTestSet
910
import org.utbot.framework.codegen.model.constructor.context.CgContext
1011
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor
1112
import org.utbot.framework.codegen.model.constructor.tree.TestsGenerationReport
1213
import org.utbot.framework.codegen.model.tree.CgTestClassFile
1314
import org.utbot.framework.codegen.model.visitor.CgAbstractRenderer
14-
import org.utbot.framework.plugin.api.CgMethodTestSet
1515
import org.utbot.framework.plugin.api.ClassId
1616
import org.utbot.framework.plugin.api.CodegenLanguage
1717
import org.utbot.framework.plugin.api.ExecutableId
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package org.utbot.framework.codegen.model.constructor
2+
3+
import org.utbot.framework.plugin.api.ClassId
4+
import org.utbot.framework.plugin.api.ExecutableId
5+
import org.utbot.framework.plugin.api.UtClusterInfo
6+
import org.utbot.framework.plugin.api.UtExecution
7+
import org.utbot.framework.plugin.api.UtExecutionSuccess
8+
import org.utbot.framework.plugin.api.UtMethodTestSet
9+
import org.utbot.framework.plugin.api.util.executableId
10+
import org.utbot.framework.plugin.api.util.objectClassId
11+
import soot.jimple.JimpleBody
12+
13+
data class CgMethodTestSet private constructor(
14+
val executableId: ExecutableId,
15+
val jimpleBody: JimpleBody? = null,
16+
val errors: Map<String, Int> = emptyMap(),
17+
val clustersInfo: List<Pair<UtClusterInfo?, IntRange>>,
18+
) {
19+
var executions: List<UtExecution> = emptyList()
20+
private set
21+
22+
constructor(from: UtMethodTestSet) : this(
23+
from.method.callable.executableId,
24+
from.jimpleBody,
25+
from.errors,
26+
from.clustersInfo
27+
) {
28+
executions = from.executions
29+
}
30+
31+
/**
32+
* Splits [CgMethodTestSet] into separate test sets having
33+
* unique result model [ClassId] in each subset.
34+
*/
35+
fun splitExecutionsByResult() : List<CgMethodTestSet> {
36+
val successfulExecutions = executions.filter { it.result is UtExecutionSuccess }
37+
val executionsByResult: Map<ClassId, List<UtExecution>> =
38+
if (successfulExecutions.isNotEmpty()) {
39+
successfulExecutions.groupBy { (it.result as UtExecutionSuccess).model.classId }
40+
} else {
41+
mapOf(objectClassId to executions)
42+
}
43+
44+
return executionsByResult.map{ (_, executions) -> substituteExecutions(executions) }
45+
}
46+
47+
/**
48+
* Finds a [ClassId] of all result models in executions.
49+
*
50+
* Tries to find an unique result type in testSets or
51+
* gets executable return type.
52+
*/
53+
fun resultType(): ClassId {
54+
val successfulExecutions = executions.filter { it.result is UtExecutionSuccess }
55+
return if (successfulExecutions.isNotEmpty()) {
56+
successfulExecutions
57+
.map { (it.result as UtExecutionSuccess).model.classId }
58+
.distinct()
59+
.singleOrNull()
60+
?: executableId.returnType
61+
} else {
62+
executableId.returnType
63+
}
64+
}
65+
66+
private fun substituteExecutions(newExecutions: List<UtExecution>): CgMethodTestSet =
67+
copy().apply { executions = newExecutions }
68+
}

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ import kotlinx.collections.immutable.PersistentSet
5151
import kotlinx.collections.immutable.persistentListOf
5252
import kotlinx.collections.immutable.persistentMapOf
5353
import kotlinx.collections.immutable.persistentSetOf
54+
import org.utbot.framework.codegen.model.constructor.CgMethodTestSet
5455
import org.utbot.framework.codegen.model.constructor.builtin.streamsDeepEqualsMethodId
5556
import org.utbot.framework.codegen.model.tree.CgParameterKind
56-
import org.utbot.framework.plugin.api.CgMethodTestSet
5757
import org.utbot.framework.plugin.api.util.id
5858
import org.utbot.framework.plugin.api.util.isCheckedException
5959
import org.utbot.framework.plugin.api.util.isSubtypeOf

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import org.utbot.framework.codegen.Junit5
88
import org.utbot.framework.codegen.ParametrizedTestSource
99
import org.utbot.framework.codegen.RuntimeExceptionTestsBehaviour.PASS
1010
import org.utbot.framework.codegen.TestNg
11+
import org.utbot.framework.codegen.model.constructor.CgMethodTestSet
1112
import org.utbot.framework.codegen.model.constructor.builtin.closeMethodIdOrNull
1213
import org.utbot.framework.codegen.model.constructor.builtin.forName
1314
import org.utbot.framework.codegen.model.constructor.builtin.getClass
@@ -84,7 +85,6 @@ import org.utbot.framework.fields.ExecutionStateAnalyzer
8485
import org.utbot.framework.fields.FieldPath
8586
import org.utbot.framework.plugin.api.BuiltinClassId
8687
import org.utbot.framework.plugin.api.BuiltinMethodId
87-
import org.utbot.framework.plugin.api.CgMethodTestSet
8888
import org.utbot.framework.plugin.api.ClassId
8989
import org.utbot.framework.plugin.api.CodegenLanguage
9090
import org.utbot.framework.plugin.api.ConcreteExecutionFailureException
@@ -1265,9 +1265,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
12651265
currentMethodParameters[CgParameterKind.Argument(index)] = argument.parameter
12661266
}
12671267

1268-
val method = currentExecutable!!
1269-
val expectedResultClassId = wrapTypeIfRequired(method.returnType)
1270-
1268+
val expectedResultClassId = wrapTypeIfRequired(testSet.resultType())
12711269
if (expectedResultClassId != voidClassId) {
12721270
val wrappedType = wrapIfPrimitive(expectedResultClassId)
12731271
//We are required to wrap the type of expected result if it is primitive

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgTestClassConstructor.kt

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.utbot.framework.codegen.model.constructor.tree
33
import org.utbot.common.appendHtmlLine
44
import org.utbot.engine.displayName
55
import org.utbot.framework.codegen.ParametrizedTestSource
6+
import org.utbot.framework.codegen.model.constructor.CgMethodTestSet
67
import org.utbot.framework.codegen.model.constructor.context.CgContext
78
import org.utbot.framework.codegen.model.constructor.context.CgContextOwner
89
import org.utbot.framework.codegen.model.constructor.util.CgComponents
@@ -23,7 +24,6 @@ import org.utbot.framework.codegen.model.tree.buildTestClass
2324
import org.utbot.framework.codegen.model.tree.buildTestClassBody
2425
import org.utbot.framework.codegen.model.tree.buildTestClassFile
2526
import org.utbot.framework.codegen.model.visitor.importUtilMethodDependencies
26-
import org.utbot.framework.plugin.api.CgMethodTestSet
2727
import org.utbot.framework.plugin.api.ExecutableId
2828
import org.utbot.framework.plugin.api.MethodId
2929
import org.utbot.framework.plugin.api.UtMethodTestSet
@@ -83,7 +83,7 @@ internal class CgTestClassConstructor(val context: CgContext) :
8383
return null
8484
}
8585

86-
val (methodUnderTest, executions, _, _, clustersInfo) = testSet
86+
val (methodUnderTest, _, _, clustersInfo) = testSet
8787
val regions = mutableListOf<CgRegion<CgMethod>>()
8888
val requiredFields = mutableListOf<CgParameterDeclaration>()
8989

@@ -94,7 +94,7 @@ internal class CgTestClassConstructor(val context: CgContext) :
9494
emptyLineIfNeeded()
9595
for (i in executionIndices) {
9696
runCatching {
97-
currentTestCaseTestMethods += methodConstructor.createTestMethod(methodUnderTest, executions[i])
97+
currentTestCaseTestMethods += methodConstructor.createTestMethod(methodUnderTest, testSet.executions[i])
9898
}.onFailure { e -> processFailure(testSet, e) }
9999
}
100100
val clusterHeader = clusterSummary?.header
@@ -107,22 +107,9 @@ internal class CgTestClassConstructor(val context: CgContext) :
107107
}
108108
}
109109
ParametrizedTestSource.PARAMETRIZE -> {
110-
runCatching {
111-
val dataProviderMethodName = nameGenerator.dataProviderMethodNameFor(testSet.executableId)
112-
113-
val parameterizedTestMethod =
114-
methodConstructor.createParameterizedTestMethod(testSet, dataProviderMethodName)
115-
116-
requiredFields += parameterizedTestMethod.requiredFields
117-
118-
cgDataProviderMethods +=
119-
methodConstructor.createParameterizedTestDataProvider(testSet, dataProviderMethodName)
120-
121-
regions += CgSimpleRegion(
122-
"Parameterized test for method ${methodUnderTest.displayName}",
123-
listOf(parameterizedTestMethod),
124-
)
125-
}.onFailure { error -> processFailure(testSet, error) }
110+
for (currentTestSet in testSet.splitExecutionsByResult()) {
111+
createParametrizedTestAndDataProvider(currentTestSet, requiredFields, regions, methodUnderTest)
112+
}
126113
}
127114
}
128115

@@ -141,6 +128,30 @@ internal class CgTestClassConstructor(val context: CgContext) :
141128
.merge(failure.description, 1, Int::plus)
142129
}
143130

131+
private fun createParametrizedTestAndDataProvider(
132+
testSet: CgMethodTestSet,
133+
requiredFields: MutableList<CgParameterDeclaration>,
134+
regions: MutableList<CgRegion<CgMethod>>,
135+
methodUnderTest: ExecutableId,
136+
) {
137+
runCatching {
138+
val dataProviderMethodName = nameGenerator.dataProviderMethodNameFor(testSet.executableId)
139+
140+
val parameterizedTestMethod =
141+
methodConstructor.createParameterizedTestMethod(testSet, dataProviderMethodName)
142+
143+
requiredFields += parameterizedTestMethod.requiredFields
144+
145+
cgDataProviderMethods +=
146+
methodConstructor.createParameterizedTestDataProvider(testSet, dataProviderMethodName)
147+
148+
regions += CgSimpleRegion(
149+
"Parameterized test for method ${methodUnderTest.displayName}",
150+
listOf(parameterizedTestMethod),
151+
)
152+
}.onFailure { error -> processFailure(testSet, error) }
153+
}
154+
144155
// TODO: collect imports of util methods
145156
private fun createUtilMethods(): List<CgUtilMethod> {
146157
val utilMethods = mutableListOf<CgUtilMethod>()

utbot-framework/src/test/kotlin/org/utbot/framework/codegen/TestCodeGeneratorPipeline.kt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,17 @@ class TestCodeGeneratorPipeline(private val testFrameworkConfiguration: TestFram
130130
"Errors regions has been generated: $errorText"
131131
}
132132

133-
require(generatedMethodsCount == expectedNumberOfGeneratedMethods) {
134-
"Something went wrong during the code generation for ${classUnderTest.simpleName}. " +
135-
"Expected to generate $expectedNumberOfGeneratedMethods test methods, " +
136-
"but got only $generatedMethodsCount"
133+
// for now, we skip a comparing of generated and expected test methods
134+
// in parametrized test generation mode
135+
// because there are problems with determining expected number of methods,
136+
// due to a feature that generates several separated parametrized tests
137+
// when we have several executions with different result type
138+
if (parametrizedTestSource != ParametrizedTestSource.PARAMETRIZE) {
139+
require(generatedMethodsCount == expectedNumberOfGeneratedMethods) {
140+
"Something went wrong during the code generation for ${classUnderTest.simpleName}. " +
141+
"Expected to generate $expectedNumberOfGeneratedMethods test methods, " +
142+
"but got only $generatedMethodsCount"
143+
}
137144
}
138145
}.onFailure {
139146
val classes = listOf(classPipeline).retrieveClasses()

0 commit comments

Comments
 (0)