Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: UnitTestBot/UTBotJava
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: dab85559a67e157cfcec45ca7acd9195205aac22
Choose a base ref
..
head repository: UnitTestBot/UTBotJava
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: a32a1dedf3fe554e3b6c8bb40505704bdbdba237
Choose a head ref
Original file line number Diff line number Diff line change
@@ -98,6 +98,22 @@ data class UtMethodTestSet(
val clustersInfo: List<Pair<UtClusterInfo?, IntRange>> = listOf(null to executions.indices)
)

data class CgMethodTestSet private constructor(
val executableId: ExecutableId,
val executions: List<UtExecution> = emptyList(),
val jimpleBody: JimpleBody? = null,
val errors: Map<String, Int> = emptyMap(),
val clustersInfo: List<Pair<UtClusterInfo?, IntRange>> = listOf(null to executions.indices)
) {
constructor(from: UtMethodTestSet) : this(
from.method.callable.executableId,
from.executions,
from.jimpleBody,
from.errors,
from.clustersInfo
)
}

data class Step(
val stmt: Stmt,
val depth: Int,
27 changes: 21 additions & 6 deletions utbot-framework/src/main/kotlin/org/utbot/engine/Extensions.kt
Original file line number Diff line number Diff line change
@@ -46,6 +46,8 @@ import kotlin.reflect.jvm.javaMethod
import kotlinx.collections.immutable.PersistentMap
import kotlinx.collections.immutable.persistentHashMapOf
import org.utbot.engine.pc.UtSolverStatusUNDEFINED
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.api.util.executableId
import soot.ArrayType
import soot.PrimType
import soot.RefLikeType
@@ -326,15 +328,28 @@ val <R> UtMethod<R>.signature: String
return "${methodName}()"
}

val ExecutableId.displayName: String
get() {
val executableName = this.name
val parameters = this.parameters.joinToString(separator = ", ") { it.canonicalName }
return "$executableName($parameters)"
}

val Constructor<*>.displayName: String
get() = executableId.displayName

val Method.displayName: String
get() = executableId.displayName

val <R> UtMethod<R>.displayName: String
get() {
val methodName = this.callable.name
val javaMethod = this.javaMethod ?: this.javaConstructor
if (javaMethod != null) {
val parameters = javaMethod.parameters.joinToString(separator = ", ") { "${it.type.canonicalName}" }
return "${methodName}($parameters)"
val executableId = this.javaMethod?.executableId ?: this.javaConstructor?.executableId
return if (executableId != null) {
executableId.displayName
} else {
val methodName = this.callable.name
return "${methodName}()"
}
return "${methodName}()"
}


Original file line number Diff line number Diff line change
@@ -12,15 +12,16 @@ import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor
import org.utbot.framework.codegen.model.constructor.tree.TestsGenerationReport
import org.utbot.framework.codegen.model.tree.CgTestClassFile
import org.utbot.framework.codegen.model.visitor.CgAbstractRenderer
import org.utbot.framework.plugin.api.CgMethodTestSet
import org.utbot.framework.plugin.api.CodegenLanguage
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.api.MockFramework
import org.utbot.framework.plugin.api.UtMethod
import org.utbot.framework.plugin.api.UtMethodTestSet
import org.utbot.framework.plugin.api.util.id

class CodeGenerator(
private val classUnderTest: Class<*>,
params: MutableMap<UtMethod<*>, List<String>> = mutableMapOf(),
paramNames: MutableMap<ExecutableId, List<String>> = mutableMapOf(),
testFramework: TestFramework = TestFramework.defaultItem,
mockFramework: MockFramework? = MockFramework.defaultItem,
staticsMocking: StaticsMocking = StaticsMocking.defaultItem,
@@ -35,7 +36,7 @@ class CodeGenerator(
) {
private var context: CgContext = CgContext(
classUnderTest = classUnderTest.id,
paramNames = params,
paramNames = paramNames,
testFramework = testFramework,
mockFramework = mockFramework ?: MockFramework.MOCKITO,
codegenLanguage = codegenLanguage,
@@ -57,13 +58,20 @@ class CodeGenerator(
fun generateAsStringWithTestReport(
testSets: Collection<UtMethodTestSet>,
testClassCustomName: String? = null,
): TestsCodeWithTestReport =
withCustomContext(testClassCustomName) {
context.withClassScope {
val testClassFile = CgTestClassConstructor(context).construct(testSets)
TestsCodeWithTestReport(renderClassFile(testClassFile), testClassFile.testsGenerationReport)
}
}
): TestsCodeWithTestReport {
val cgTestSets = testSets.map { CgMethodTestSet(it) }.toList()
return generateAsStringWithTestReport(cgTestSets, testClassCustomName)
}

fun generateAsStringWithTestReport(
cgTestSets: List<CgMethodTestSet>,
testClassCustomName: String? = null,
): TestsCodeWithTestReport = withCustomContext(testClassCustomName) {
context.withClassScope {
val testClassFile = CgTestClassConstructor(context).construct(cgTestSets)
TestsCodeWithTestReport(renderClassFile(testClassFile), testClassFile.testsGenerationReport)
}
}

/**
* Wrapper function that configures context as needed for utbot-online:
Original file line number Diff line number Diff line change
@@ -42,10 +42,8 @@ import org.utbot.framework.plugin.api.FieldId
import org.utbot.framework.plugin.api.MethodId
import org.utbot.framework.plugin.api.MockFramework
import org.utbot.framework.plugin.api.UtExecution
import org.utbot.framework.plugin.api.UtMethod
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.UtReferenceModel
import org.utbot.framework.plugin.api.UtMethodTestSet
import java.util.IdentityHashMap
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.PersistentMap
@@ -55,7 +53,7 @@ import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.persistentSetOf
import org.utbot.framework.codegen.model.constructor.builtin.streamsDeepEqualsMethodId
import org.utbot.framework.codegen.model.tree.CgParameterKind
import org.utbot.framework.plugin.api.util.executableId
import org.utbot.framework.plugin.api.CgMethodTestSet
import org.utbot.framework.plugin.api.util.id
import org.utbot.framework.plugin.api.util.isCheckedException
import org.utbot.framework.plugin.api.util.isSubtypeOf
@@ -121,7 +119,7 @@ internal interface CgContextOwner {
val prevStaticFieldValues: MutableMap<FieldId, CgVariable>

// names of parameters of methods under test
val paramNames: Map<UtMethod<*>, List<String>>
val paramNames: Map<ExecutableId, List<String>>

// UtExecution we currently generate a test method for.
// It is null when no test method is being generated at the moment.
@@ -176,7 +174,7 @@ internal interface CgContextOwner {
// map from a set of tests for a method to another map
// which connects code generation error message
// with the number of times it occurred
val codeGenerationErrors: MutableMap<UtMethodTestSet, MutableMap<String, Int>>
val codeGenerationErrors: MutableMap<CgMethodTestSet, MutableMap<String, Int>>

// package for generated test class
val testClassPackageName: String
@@ -233,8 +231,8 @@ internal interface CgContextOwner {
currentBlock = currentBlock.add(it)
}

fun updateCurrentExecutable(method: UtMethod<*>) {
currentExecutable = method.callable.executableId
fun updateCurrentExecutable(executableId: ExecutableId) {
currentExecutable = executableId
}

fun addExceptionIfNeeded(exception: ClassId) {
@@ -399,7 +397,7 @@ internal data class CgContext(
override val testMethods: MutableList<CgTestMethod> = mutableListOf(),
override val existingMethodNames: MutableSet<String> = mutableSetOf(),
override val prevStaticFieldValues: MutableMap<FieldId, CgVariable> = mutableMapOf(),
override val paramNames: Map<UtMethod<*>, List<String>>,
override val paramNames: Map<ExecutableId, List<String>>,
override var currentExecution: UtExecution? = null,
override val testFramework: TestFramework,
override val mockFramework: MockFramework,
@@ -415,7 +413,7 @@ internal data class CgContext(
override var declaredExecutableRefs: PersistentMap<ExecutableId, CgVariable> = persistentMapOf(),
override var thisInstance: CgValue? = null,
override val methodArguments: MutableList<CgValue> = mutableListOf(),
override val codeGenerationErrors: MutableMap<UtMethodTestSet, MutableMap<String, Int>> = mutableMapOf(),
override val codeGenerationErrors: MutableMap<CgMethodTestSet, MutableMap<String, Int>> = mutableMapOf(),
override val testClassPackageName: String = classUnderTest.packageName,
override var shouldOptimizeImports: Boolean = false,
override var testClassCustomName: String? = null,
Original file line number Diff line number Diff line change
@@ -7,9 +7,8 @@ import org.utbot.framework.codegen.model.constructor.util.infiniteInts
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.CodegenLanguage
import org.utbot.framework.plugin.api.ConstructorId
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.api.MethodId
import org.utbot.framework.plugin.api.UtMethod
import org.utbot.framework.plugin.api.util.executableId
import org.utbot.framework.plugin.api.util.isArray

/**
@@ -45,7 +44,7 @@ internal interface CgNameGenerator {
/**
* Generate a new test method name.
*/
fun testMethodNameFor(method: UtMethod<*>, customName: String? = null): String
fun testMethodNameFor(executableId: ExecutableId, customName: String? = null): String

/**
* Generates a new parameterized test method name by data provider method name.
@@ -55,12 +54,12 @@ internal interface CgNameGenerator {
/**
* Generates a new data for parameterized test provider method name
*/
fun dataProviderMethodNameFor(method: UtMethod<*>): String
fun dataProviderMethodNameFor(executableId: ExecutableId): String

/**
* Generate a new error method name
*/
fun errorMethodNameFor(method: UtMethod<*>): String
fun errorMethodNameFor(executableId: ExecutableId): String
}

/**
@@ -86,11 +85,9 @@ internal class CgNameGeneratorImpl(private val context: CgContext)
return variableName(baseName.decapitalize(), isMock)
}

override fun testMethodNameFor(method: UtMethod<*>, customName: String?): String {
val executableName = when (val id = method.callable.executableId) {
is ConstructorId -> id.classId.prettifiedName // TODO: maybe we need some suffix e.g. "Ctor"?
is MethodId -> id.name
}
override fun testMethodNameFor(executableId: ExecutableId, customName: String?): String {
val executableName = createExecutableName(executableId)

// no index suffix allowed only when there's a vacant custom name
val name = if (customName != null && customName !in existingMethodNames) {
customName
@@ -107,18 +104,16 @@ internal class CgNameGeneratorImpl(private val context: CgContext)
override fun parameterizedTestMethodName(dataProviderMethodName: String) =
dataProviderMethodName.replace(dataProviderMethodPrefix, "parameterizedTestsFor")

override fun dataProviderMethodNameFor(method: UtMethod<*>): String {
val indexedName = nextIndexedMethodName(method.callable.name.capitalize(), skipOne = true)
override fun dataProviderMethodNameFor(executableId: ExecutableId): String {
val executableName = createExecutableName(executableId)
val indexedName = nextIndexedMethodName(executableName.capitalize(), skipOne = true)

existingMethodNames += indexedName
return "$dataProviderMethodPrefix$indexedName"
}

override fun errorMethodNameFor(method: UtMethod<*>): String {
val executableName = when (val id = method.callable.executableId) {
is ConstructorId -> id.classId.prettifiedName
is MethodId -> id.name
}
override fun errorMethodNameFor(executableId: ExecutableId): String {
val executableName = createExecutableName(executableId)
val newName = when (val base = "test${executableName.capitalize()}_errors") {
!in existingMethodNames -> base
else -> nextIndexedMethodName(base)
@@ -152,6 +147,13 @@ internal class CgNameGeneratorImpl(private val context: CgContext)
if (baseName !in existingVariableNames) "`$baseName`" else nextIndexedVarName(baseName)
}
}

private fun createExecutableName(executableId: ExecutableId): String {
return when (executableId) {
is ConstructorId -> executableId.classId.prettifiedName // TODO: maybe we need some suffix e.g. "Ctor"?
is MethodId -> executableId.name
}
}
}

/**

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -23,10 +23,12 @@ import org.utbot.framework.codegen.model.tree.buildTestClass
import org.utbot.framework.codegen.model.tree.buildTestClassBody
import org.utbot.framework.codegen.model.tree.buildTestClassFile
import org.utbot.framework.codegen.model.visitor.importUtilMethodDependencies
import org.utbot.framework.plugin.api.CgMethodTestSet
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.api.MethodId
import org.utbot.framework.plugin.api.UtMethod
import org.utbot.framework.plugin.api.UtMethodTestSet
import org.utbot.framework.plugin.api.util.description
import org.utbot.framework.plugin.api.util.kClass
import kotlin.reflect.KClass

internal class CgTestClassConstructor(val context: CgContext) :
@@ -43,15 +45,15 @@ internal class CgTestClassConstructor(val context: CgContext) :
/**
* Given a list of test sets constructs CgTestClass
*/
fun construct(testSets: Collection<UtMethodTestSet>): CgTestClassFile {
fun construct(testSets: Collection<CgMethodTestSet>): CgTestClassFile {
return buildTestClassFile {
testClass = buildTestClass {
// TODO: obtain test class from plugin
id = currentTestClass
body = buildTestClassBody {
cgDataProviderMethods.clear()
for (testSet in testSets) {
updateCurrentExecutable(testSet.method)
updateCurrentExecutable(testSet.executableId)
val currentMethodUnderTestRegions = construct(testSet) ?: continue
val executableUnderTestCluster = CgExecutableUnderTestCluster(
"Test suites for executable $currentExecutable",
@@ -76,7 +78,7 @@ internal class CgTestClassConstructor(val context: CgContext) :
}
}

private fun construct(testSet: UtMethodTestSet): List<CgRegion<CgMethod>>? {
private fun construct(testSet: CgMethodTestSet): List<CgRegion<CgMethod>>? {
if (testSet.executions.isEmpty()) {
return null
}
@@ -106,7 +108,7 @@ internal class CgTestClassConstructor(val context: CgContext) :
}
ParametrizedTestSource.PARAMETRIZE -> {
runCatching {
val dataProviderMethodName = nameGenerator.dataProviderMethodNameFor(testSet.method)
val dataProviderMethodName = nameGenerator.dataProviderMethodNameFor(testSet.executableId)

val parameterizedTestMethod =
methodConstructor.createParameterizedTestMethod(testSet, dataProviderMethodName)
@@ -126,14 +128,14 @@ internal class CgTestClassConstructor(val context: CgContext) :

val errors = testSet.allErrors
if (errors.isNotEmpty()) {
regions += methodConstructor.errorMethod(testSet.method, errors)
regions += methodConstructor.errorMethod(testSet.executableId, errors)
testsGenerationReport.addMethodErrors(testSet, errors)
}

return regions
}

private fun processFailure(testSet: UtMethodTestSet, failure: Throwable) {
private fun processFailure(testSet: CgMethodTestSet, failure: Throwable) {
codeGenerationErrors
.getOrPut(testSet) { mutableMapOf() }
.merge(failure.description, 1, Int::plus)
@@ -172,23 +174,23 @@ internal class CgTestClassConstructor(val context: CgContext) :
/**
* Engine errors + codegen errors for a given [UtMethodTestSet]
*/
private val UtMethodTestSet.allErrors: Map<String, Int>
private val CgMethodTestSet.allErrors: Map<String, Int>
get() = errors + codeGenerationErrors.getOrDefault(this, mapOf())
}

typealias MethodGeneratedTests = MutableMap<UtMethod<*>, MutableSet<CgTestMethod>>
typealias MethodGeneratedTests = MutableMap<ExecutableId, MutableSet<CgTestMethod>>
typealias ErrorsCount = Map<String, Int>

data class TestsGenerationReport(
val executables: MutableSet<UtMethod<*>> = mutableSetOf(),
val executables: MutableSet<ExecutableId> = mutableSetOf(),
var successfulExecutions: MethodGeneratedTests = mutableMapOf(),
var timeoutExecutions: MethodGeneratedTests = mutableMapOf(),
var failedExecutions: MethodGeneratedTests = mutableMapOf(),
var crashExecutions: MethodGeneratedTests = mutableMapOf(),
var errors: MutableMap<UtMethod<*>, ErrorsCount> = mutableMapOf()
var errors: MutableMap<ExecutableId, ErrorsCount> = mutableMapOf()
) {
val classUnderTest: KClass<*>
get() = executables.firstOrNull()?.clazz
get() = executables.firstOrNull()?.classId?.kClass
?: error("No executables found in test report")

val initialWarnings: MutableList<() -> String> = mutableListOf()
@@ -215,12 +217,12 @@ data class TestsGenerationReport(
appendHtmlLine("Not generated because of internal errors test methods: $overallErrors")
}

fun addMethodErrors(testSet: UtMethodTestSet, errors: Map<String, Int>) {
this.errors[testSet.method] = errors
fun addMethodErrors(testSet: CgMethodTestSet, errors: Map<String, Int>) {
this.errors[testSet.executableId] = errors
}

fun addTestsByType(testSet: UtMethodTestSet, testMethods: List<CgTestMethod>) {
with(testSet.method) {
fun addTestsByType(testSet: CgMethodTestSet, testMethods: List<CgTestMethod>) {
with(testSet.executableId) {
executables += this

testMethods.forEach {
@@ -257,19 +259,19 @@ data class TestsGenerationReport(

override fun toString(): String = toString(false)

private fun UtMethod<*>.countTestMethods(): TestMethodStatistic = TestMethodStatistic(
private fun ExecutableId.countTestMethods(): TestMethodStatistic = TestMethodStatistic(
testMethodsNumber(successfulExecutions),
testMethodsNumber(failedExecutions),
testMethodsNumber(timeoutExecutions),
testMethodsNumber(crashExecutions)
)

private fun UtMethod<*>.countErrors(): Int = errors.getOrDefault(this, emptyMap()).values.sum()
private fun ExecutableId.countErrors(): Int = errors.getOrDefault(this, emptyMap()).values.sum()

private fun UtMethod<*>.testMethodsNumber(executables: MethodGeneratedTests): Int =
private fun ExecutableId.testMethodsNumber(executables: MethodGeneratedTests): Int =
executables.getOrDefault(this, emptySet()).size

private fun UtMethod<*>.updateExecutions(it: CgTestMethod, executions: MethodGeneratedTests) {
private fun ExecutableId.updateExecutions(it: CgTestMethod, executions: MethodGeneratedTests) {
executions.getOrPut(this) { mutableSetOf() } += it
}

Original file line number Diff line number Diff line change
@@ -7,13 +7,12 @@ import org.utbot.common.bracket
import org.utbot.common.info
import org.utbot.common.packageName
import org.utbot.examples.TestFrameworkConfiguration
import org.utbot.examples.conflictTriggers
import org.utbot.framework.codegen.ExecutionStatus.SUCCESS
import org.utbot.framework.codegen.model.CodeGenerator
import org.utbot.framework.plugin.api.CodegenLanguage
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.api.MockFramework
import org.utbot.framework.plugin.api.MockStrategyApi
import org.utbot.framework.plugin.api.UtMethod
import org.utbot.framework.plugin.api.UtMethodTestSet
import org.utbot.framework.plugin.api.util.UtContext
import org.utbot.framework.plugin.api.util.description
@@ -217,12 +216,12 @@ class TestCodeGeneratorPipeline(private val testFrameworkConfiguration: TestFram
testSets: List<UtMethodTestSet>,
classUnderTest: KClass<*>
): String {
val params = mutableMapOf<UtMethod<*>, List<String>>()
val params = mutableMapOf<ExecutableId, List<String>>()

val codeGenerator = with(testFrameworkConfiguration) {
CodeGenerator(
classUnderTest.java,
params = params,
paramNames = params,
testFramework = testFramework,
staticsMocking = staticsMocking,
forceStaticMocking = forceStaticMocking,
Original file line number Diff line number Diff line change
@@ -55,9 +55,10 @@ import org.utbot.framework.codegen.model.CodeGenerator
import org.utbot.framework.codegen.model.TestsCodeWithTestReport
import org.utbot.framework.codegen.model.constructor.tree.TestsGenerationReport
import org.utbot.framework.plugin.api.CodegenLanguage
import org.utbot.framework.plugin.api.UtMethod
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.api.UtMethodTestSet
import org.utbot.framework.plugin.api.util.UtContext
import org.utbot.framework.plugin.api.util.executableId
import org.utbot.framework.plugin.api.util.withUtContext
import org.utbot.framework.util.Conflict
import org.utbot.intellij.plugin.generator.CodeGenerationController.Target.*
@@ -100,7 +101,7 @@ object CodeGenerationController {
val file = testClass.containingFile
runWriteCommandAction(model.project, "Generate tests with UtBot", null, {
try {
generateCodeAndReport(testClass, file, testSets, model, latch, reports)
generateCodeAndReport(srcClass, testClass, file, testSets, model, latch, reports)
} catch (e: IncorrectOperationException) {
showCreatingClassError(model.project, createTestClassName(srcClass))
}
@@ -250,31 +251,27 @@ object CodeGenerationController {
}

private fun generateCodeAndReport(
srcClass: PsiClass,
testClass: PsiClass,
file: PsiFile,
testSets: List<UtMethodTestSet>,
model: GenerateTestsModel,
reportsCountDown: CountDownLatch,
reports: MutableList<TestsGenerationReport>,
) {
val selectedMethods = TestIntegrationUtils.extractClassMethods(testClass, false)
val testFramework = model.testFramework
val mockito = model.mockFramework
val staticsMocking = model.staticsMocking

val classUnderTest = testSets.first().method.clazz

val params = DumbService.getInstance(model.project)
.runReadActionInSmartMode(Computable { findMethodParams(classUnderTest, selectedMethods) })
val classMethods = TestIntegrationUtils.extractClassMethods(srcClass, false)
val paramNames = DumbService.getInstance(model.project)
.runReadActionInSmartMode(Computable { findMethodParamNames(classUnderTest, classMethods) })

val codeGenerator = CodeGenerator(
classUnderTest = classUnderTest.java,
params = params.toMutableMap(),
testFramework = testFramework,
mockFramework = mockito,
paramNames = paramNames.toMutableMap(),
testFramework = model.testFramework,
mockFramework = model.mockFramework,
codegenLanguage = model.codegenLanguage,
parameterizedTestSource = model.parametrizedTestSource,
staticsMocking = staticsMocking,
staticsMocking = model.staticsMocking,
forceStaticMocking = model.forceStaticMocking,
generateWarningsForStaticMocking = model.generateWarningsForStaticMocking,
runtimeExceptionTestsBehaviour = model.runtimeExceptionTestsBehaviour,
@@ -347,13 +344,11 @@ object CodeGenerationController {
}
}

private fun findMethodParams(clazz: KClass<*>, methods: List<MemberInfo>): Map<UtMethod<*>, List<String>> {
private fun findMethodParamNames(clazz: KClass<*>, methods: List<MemberInfo>): Map<ExecutableId, List<String>> {
val bySignature = methods.associate { it.signature() to it.paramNames() }
return clazz.functions.mapNotNull { method ->
bySignature[method.signature()]?.let { params ->
UtMethod(method, clazz) to params
}
}.toMap()
return clazz.functions
.mapNotNull { method -> bySignature[method.signature()]?.let { params -> method.executableId to params } }
.toMap()
}

private fun MemberInfo.paramNames(): List<String> =