From 777a77d1af3151170f0ffa73c37a8d99991f95c0 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Wed, 13 Jul 2022 19:16:20 +0300 Subject: [PATCH 1/4] Refactor TestCaseGenerator and CodeGenerator --- .../test/kotlin/org/utbot/examples/UtValueTestCaseChecker.kt | 2 +- .../org/utbot/gradle/plugin/GenerateTestsAndSarifReportTask.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/UtValueTestCaseChecker.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/UtValueTestCaseChecker.kt index 43957a73cf..bbb28788aa 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/UtValueTestCaseChecker.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/UtValueTestCaseChecker.kt @@ -2550,7 +2550,7 @@ abstract class UtValueTestCaseChecker( val substituteStatics: Boolean ) - data class MethodResult(val testCase: UtMethodTestSet, val coverage: Coverage) + data class MethodResult(val testSet: UtMethodTestSet, val coverage: Coverage) } @Suppress("UNCHECKED_CAST") diff --git a/utbot-gradle/src/main/kotlin/org/utbot/gradle/plugin/GenerateTestsAndSarifReportTask.kt b/utbot-gradle/src/main/kotlin/org/utbot/gradle/plugin/GenerateTestsAndSarifReportTask.kt index 9c2f3b0eb2..c21a3555ef 100644 --- a/utbot-gradle/src/main/kotlin/org/utbot/gradle/plugin/GenerateTestsAndSarifReportTask.kt +++ b/utbot-gradle/src/main/kotlin/org/utbot/gradle/plugin/GenerateTestsAndSarifReportTask.kt @@ -46,6 +46,7 @@ open class GenerateTestsAndSarifReportTask @Inject constructor( return } try { + generateForProjectRecursively(rootGradleProject) GenerateTestsAndSarifReportFacade.mergeReports( sarifReports = rootGradleProject.collectReportsRecursively(), From 87ca3db9a14d3694ed66fedac1c6cec0ccd39640 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Mon, 18 Jul 2022 16:04:52 +0300 Subject: [PATCH 2/4] Tmp --- .../org/utbot/framework/plugin/api/Api.kt | 16 +- .../model/constructor/context/CgContext.kt | 17 +- .../constructor/tree/CgMethodConstructor.kt | 154 +++++++++++------- .../constructor/tree/CgVariableConstructor.kt | 10 +- .../framework/codegen/model/tree/CgElement.kt | 59 +++++-- .../model/visitor/CgAbstractRenderer.kt | 3 - .../codegen/model/visitor/CgJavaRenderer.kt | 12 +- .../codegen/model/visitor/CgKotlinRenderer.kt | 7 +- .../codegen/model/visitor/CgVisitor.kt | 7 +- 9 files changed, 181 insertions(+), 104 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index 621e1377a2..c16e8357b9 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -612,8 +612,16 @@ val Type.classId: ClassId */ open class ClassId( val name: String, - val elementClassId: ClassId? = null + val elementClassId: ClassId? = null, + // Treat simple class ids as non-nullable + open val isNullable: Boolean = false ) { + /** + * This constructor is for calls from Java (e.g. `UtBotJavaApiTest`), + * because we cannot skip default parameters in Java. + */ + constructor(name: String, elementClassId: ClassId?) : this(name, elementClassId, false) + open val canonicalName: String get() = jClass.canonicalName ?: error("ClassId $name does not have canonical name") @@ -677,9 +685,9 @@ open class ClassId( open val isSynthetic: Boolean get() = jClass.isSynthetic - open val isNullable: Boolean - // Treat simple class ids as non-nullable - get() = false +// open val isNullable: Boolean +// // Treat simple class ids as non-nullable +// get() = false /** * Collects all declared methods (including private and protected) from class and all its superclasses to sequence diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt index 7b10d5d469..9c4349322c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt @@ -54,6 +54,7 @@ import kotlinx.collections.immutable.persistentListOf 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.CgParameterType import org.utbot.framework.plugin.api.util.executableId import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.isCheckedException @@ -70,8 +71,8 @@ import org.utbot.framework.plugin.api.util.jClass * For example, [currentTestClass] and [currentExecutable] can be reassigned * when we start generating another method or test class * - * [variables] and [existingVariableNames] are 'var' properties - * that can be reverted to their previous values on exit from a name scope + * [existingVariableNames] is a 'var' property + * that can be reverted to its previous value on exit from a name scope * * @see [CgContextOwner.withNameScope] */ @@ -155,9 +156,6 @@ internal interface CgContextOwner { // variable names being used in the current name scope var existingVariableNames: PersistentSet - // all declared variables in the current name scope - var variables: PersistentSet - // variables of java.lang.Class type declared in the current name scope var declaredClassRefs: PersistentMap @@ -190,6 +188,9 @@ internal interface CgContextOwner { // use it to compare stateBefore and result variables - in case of equality do not create new variable var valueByModelId: MutableMap + // parameters of the method currently being generated + val currentMethodParameters: MutableMap + val testClassCustomName: String? /** @@ -282,7 +283,6 @@ internal interface CgContextOwner { } fun updateVariableScope(variable: CgVariable, model: UtModel? = null) { - variables = variables.add(variable) model?.let { valueByModel[it] = variable (model as UtReferenceModel).let { refModel -> @@ -293,7 +293,6 @@ internal interface CgContextOwner { fun withNameScope(block: () -> R): R { val prevVariableNames = existingVariableNames - val prevVariables = variables val prevDeclaredClassRefs = declaredClassRefs val prevDeclaredExecutableRefs = declaredExecutableRefs val prevValueByModel = IdentityHashMap(valueByModel) @@ -302,7 +301,6 @@ internal interface CgContextOwner { block() } finally { existingVariableNames = prevVariableNames - variables = prevVariables declaredClassRefs = prevDeclaredClassRefs declaredExecutableRefs = prevDeclaredExecutableRefs valueByModel = prevValueByModel @@ -413,7 +411,6 @@ internal data class CgContext( override var mockFrameworkUsed: Boolean = false, override var currentBlock: PersistentList = persistentListOf(), override var existingVariableNames: PersistentSet = persistentSetOf(), - override var variables: PersistentSet = persistentSetOf(), override var declaredClassRefs: PersistentMap = persistentMapOf(), override var declaredExecutableRefs: PersistentMap = persistentMapOf(), override var thisInstance: CgValue? = null, @@ -454,5 +451,7 @@ internal data class CgContext( override var valueByModelId: MutableMap = mutableMapOf() + override val currentMethodParameters: MutableMap = mutableMapOf() + override val testClassThisInstance: CgThisInstance = CgThisInstance(currentTestClass) } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt index 59e0d032a8..c2d73deb6e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt @@ -49,7 +49,9 @@ import org.utbot.framework.codegen.model.tree.CgLiteral import org.utbot.framework.codegen.model.tree.CgMethod import org.utbot.framework.codegen.model.tree.CgMethodCall import org.utbot.framework.codegen.model.tree.CgMultilineComment +import org.utbot.framework.codegen.model.tree.CgNotNullAssertion import org.utbot.framework.codegen.model.tree.CgParameterDeclaration +import org.utbot.framework.codegen.model.tree.CgParameterType import org.utbot.framework.codegen.model.tree.CgParameterizedTestDataProviderMethod import org.utbot.framework.codegen.model.tree.CgRegion import org.utbot.framework.codegen.model.tree.CgReturnStatement @@ -144,6 +146,7 @@ import org.utbot.framework.plugin.api.util.isInnerClassEnclosingClassReference import org.utbot.framework.plugin.api.util.isIterableOrMap import org.utbot.framework.plugin.api.util.isPrimitive import org.utbot.framework.plugin.api.util.isPrimitiveArray +import org.utbot.framework.plugin.api.util.isRefType import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.kClass import org.utbot.framework.plugin.api.util.longClassId @@ -450,28 +453,23 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } else { resultModel = result - val expected = variableConstructor.parameterizedVariable( - result.classId, - expectedResultVarName, - isNotNull = true - ) - - val actualVariableName = when (codegenLanguage) { - CodegenLanguage.JAVA -> when (method.returnType) { - intClassId -> "${intWrapperClassId.simpleName.capitalize()}.valueOf(actual)" - shortClassId -> "${shortWrapperClassId.simpleName.capitalize()}.valueOf(actual)" - longClassId -> "${longWrapperClassId.simpleName.capitalize()}.valueOf(actual)" - byteClassId -> "${byteWrapperClassId.simpleName.capitalize()}.valueOf(actual)" - booleanClassId -> "${booleanWrapperClassId.simpleName.capitalize()}.valueOf(actual)" - charClassId -> "${charWrapperClassId.simpleName.capitalize()}.valueOf(actual)" - floatClassId -> "${floatWrapperClassId.simpleName.capitalize()}.valueOf(actual)" - doubleWrapperClassId -> "${doubleWrapperClassId.simpleName.capitalize()}.valueOf(actual)" - else -> "actual" - } - CodegenLanguage.KOTLIN -> "actual" - } - - assertEquality(expected, CgVariable(actualVariableName, method.returnType)) + val expected = CgNotNullAssertion(currentMethodParameters[CgParameterType.ExpectedResult]!!) + +// val actualVariableName = when (codegenLanguage) { +// CodegenLanguage.JAVA -> when (method.returnType) { +// intClassId -> "${intWrapperClassId.simpleName.capitalize()}.valueOf(actual)" +// shortClassId -> "${shortWrapperClassId.simpleName.capitalize()}.valueOf(actual)" +// longClassId -> "${longWrapperClassId.simpleName.capitalize()}.valueOf(actual)" +// byteClassId -> "${byteWrapperClassId.simpleName.capitalize()}.valueOf(actual)" +// booleanClassId -> "${booleanWrapperClassId.simpleName.capitalize()}.valueOf(actual)" +// charClassId -> "${charWrapperClassId.simpleName.capitalize()}.valueOf(actual)" +// floatClassId -> "${floatWrapperClassId.simpleName.capitalize()}.valueOf(actual)" +// doubleWrapperClassId -> "${doubleWrapperClassId.simpleName.capitalize()}.valueOf(actual)" +// else -> "actual" +// } +// CodegenLanguage.KOTLIN -> "actual" +// } + assertEquality(expected, actual)// CgVariable(actualVariableName, method.returnType)) } } .onFailure { thisInstance[method](*methodArguments.toTypedArray()).intercepted() } @@ -1084,14 +1082,18 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c ParametrizedTestSource.DO_NOT_PARAMETRIZE -> currentBlock = currentBlock.addAll(generateDeepEqualsAssertion(expected, actual)) ParametrizedTestSource.PARAMETRIZE -> { - val assertNullStmt = listOf(testFrameworkManager.assertions[testFramework.assertNull](actual).toStatement()) - currentBlock = currentBlock.add( - CgIfStatement( - CgEqualTo(expected, nullLiteral()), - assertNullStmt, - generateDeepEqualsAssertion(expected, actual) + currentBlock = if (actual.type.isPrimitive) { + currentBlock.addAll(generateDeepEqualsAssertion(expected, actual)) + } else { + val assertNullStmt = listOf(testFrameworkManager.assertions[testFramework.assertNull](actual).toStatement()) + currentBlock.add( + CgIfStatement( + CgEqualTo(expected, nullLiteral()), + assertNullStmt, + generateDeepEqualsAssertion(expected, actual) + ) ) - ) + } } } } @@ -1237,50 +1239,87 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c return withTestMethodScope(arbitraryExecution) { val testName = nameGenerator.parameterizedTestMethodName(dataProviderMethodName) - val testArguments = mutableListOf() - val mainBody = { - // build this instance - thisInstance = arbitraryExecution.stateBefore.thisInstance?.let { - val thisInstanceVariable = constructInstanceVariable(it) - testArguments += CgParameterDeclaration(thisInstanceVariable) - thisInstanceVariable + // TODO: move out to a separate method e.g. createMethodParameters() + // create parameter declarations + val testParameterDeclarations = mutableListOf().apply { + // this instance + val thisInstanceModel = arbitraryExecution.stateBefore.thisInstance + if (thisInstanceModel != null) { + val type = thisInstanceModel.classId + val thisInstance = CgParameterDeclaration( + parameter = declareParameter( + type = type, + name = nameGenerator.variableName(type) + ), + isReferenceType = true + ) + this += thisInstance + currentMethodParameters[CgParameterType.ThisInstance] = thisInstance.parameter } - - // build arguments for method under test and parameterized test + // arguments for (index in arbitraryExecution.stateBefore.parameters.indices) { val argumentName = paramNames[methodUnderTest]?.get(index) val paramIndex = if (methodUnderTest.isStatic) index else index + 1 val paramType = methodUnderTestParameters[paramIndex].type.javaType - val argumentClassId = when { + val argumentType = when { paramType is Class<*> && paramType.isArray -> paramType.id paramType is ParameterizedTypeImpl -> paramType.rawType.id else -> ClassId(paramType.typeName) } - val argument = variableConstructor.parameterizedVariable(argumentClassId, argumentName) - methodArguments += argument - testArguments += CgParameterDeclaration( - argument.name, argument.type, isReferenceType = !argument.type.isPrimitive + val argument = CgParameterDeclaration( + parameter = declareParameter( + type = argumentType, + name = nameGenerator.variableName(argumentType, argumentName), + ), + isReferenceType = argumentType.isRefType ) + this += argument + currentMethodParameters[CgParameterType.Argument(index)] = argument.parameter } + val method = currentExecutable as MethodId val containsFailureExecution = containsFailureExecution(testSet) val expectedResultClassId = wrapTypeIfRequired(method.returnType) if (expectedResultClassId != voidClassId) { - testArguments += CgParameterDeclaration( - expectedResultVarName, resultClassId(expectedResultClassId), - isReferenceType = containsFailureExecution || !expectedResultClassId.isPrimitive + val resultType = resultClassId(expectedResultClassId) + val expectedResult = CgParameterDeclaration( + parameter = declareParameter( + type = resultType, + name = nameGenerator.variableName(expectedResultVarName) + ), + isReferenceType = resultType.isRefType ) + this += expectedResult + currentMethodParameters[CgParameterType.ExpectedResult] = expectedResult.parameter } + if (containsFailureExecution) { - testArguments += CgParameterDeclaration( - expectedErrorVarName, - throwableClassId(), + val expectedException = CgParameterDeclaration( + parameter = declareParameter( + type = throwableClassId(), + name = nameGenerator.variableName(expectedErrorVarName) + ), + // exceptions are always reference type isReferenceType = true ) + this += expectedException + currentMethodParameters[CgParameterType.ExpectedException] = expectedException.parameter + } + } + +// currentMethodParameters += testParameterDeclarations.map { it.parameter } + + val mainBody = { + // build this instance + thisInstance = arbitraryExecution.stateBefore.thisInstance?.let { currentMethodParameters[0] } + + // build arguments for method under test and parameterized test + for (index in arbitraryExecution.stateBefore.parameters.indices) { + methodArguments += currentMethodParameters[CgParameterType.Argument(index)]!! } //record result and generate result assertions @@ -1292,7 +1331,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c testMethod( testName, displayName = null, - testArguments, + testParameterDeclarations, parameterized = true, dataProviderMethodName ) { @@ -1313,13 +1352,13 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } } - /** - * Constructs a variable for the instance parameter of parametrized test. - */ - private fun constructInstanceVariable(instanceModel: UtModel): CgVariable { - val className = instanceModel.classId.simpleName.decapitalize() - return variableConstructor.parameterizedVariable(instanceModel.classId, className) - } +// /** +// * Constructs a variable for the instance parameter of parametrized test. +// */ +// private fun constructInstanceVariable(instanceModel: UtModel): CgValue { +// val className = instanceModel.classId.simpleName.decapitalize() +// return variableConstructor.getParameterByName(className) +// } /** * Constructs data provider method for parameterized tests. @@ -1420,6 +1459,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c methodArguments.clear() currentExecution = null mockFrameworkManager.clearExecutionResources() + currentMethodParameters.clear() } /** diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt index 534505dec5..e66b988daa 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt @@ -18,7 +18,7 @@ import org.utbot.framework.codegen.model.tree.CgExpression import org.utbot.framework.codegen.model.tree.CgFieldAccess import org.utbot.framework.codegen.model.tree.CgGetJavaClass import org.utbot.framework.codegen.model.tree.CgLiteral -import org.utbot.framework.codegen.model.tree.CgNotNullVariable +import org.utbot.framework.codegen.model.tree.CgNotNullAssertion import org.utbot.framework.codegen.model.tree.CgStaticFieldAccess import org.utbot.framework.codegen.model.tree.CgValue import org.utbot.framework.codegen.model.tree.CgVariable @@ -114,14 +114,6 @@ internal class CgVariableConstructor(val context: CgContext) : } } - /** - * Creates general variable of type (to replace with concrete value in each test case). - */ - fun parameterizedVariable(classId: ClassId, baseName: String? = null, isNotNull: Boolean = false): CgVariable { - val name = nameGenerator.variableName(type = classId, base = baseName, isMock = false) - return if (isNotNull) CgNotNullVariable(name, classId) else CgVariable(name, classId) - } - private fun constructComposite(model: UtCompositeModel, baseName: String): CgVariable { val obj = if (model.isMock) { mockFrameworkManager.createMockFor(model, baseName) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt index c36aac6f28..8b68ba6992 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt @@ -4,6 +4,7 @@ import org.utbot.common.WorkaroundReason import org.utbot.common.workaround import org.utbot.framework.codegen.Import import org.utbot.framework.codegen.model.constructor.tree.TestsGenerationReport +import org.utbot.framework.codegen.model.constructor.util.CgStatementConstructor import org.utbot.framework.codegen.model.util.CgExceptionHandler import org.utbot.framework.codegen.model.visitor.CgVisitor import org.utbot.framework.plugin.api.ClassId @@ -25,6 +26,7 @@ import org.utbot.framework.plugin.api.util.intClassId import org.utbot.framework.plugin.api.util.objectArrayClassId import org.utbot.framework.plugin.api.util.objectClassId import org.utbot.framework.plugin.api.util.voidClassId +import kotlin.math.exp interface CgElement { // TODO: order of cases is important here due to inheritance between some of the element types @@ -76,7 +78,8 @@ interface CgElement { is CgTypeCast -> visit(element) is CgThisInstance -> visit(element) //Not that order of variables is important - is CgNotNullVariable -> visit(element) +// is CgNotNullVariable -> visit(element) + is CgNotNullAssertion -> visit(element) is CgVariable -> visit(element) is CgParameterDeclaration -> visit(element) is CgLiteral -> visit(element) @@ -449,7 +452,7 @@ data class CgTryCatch( data class CgErrorWrapper( val message: String, val expression: CgExpression, -): CgExpression { +) : CgExpression { override val type: ClassId get() = expression.type } @@ -520,6 +523,7 @@ interface CgExpression : CgStatement { } // marker interface representing expressions returning reference +// TODO: talk to Yury (?), because it seems that not all CgValues are reference expressions interface CgReferenceExpression : CgExpression /** @@ -580,7 +584,18 @@ open class CgVariable( * - in Java it is an equivalent of [CgVariable] * - in Kotlin the difference is in addition of "!!" to the name */ -class CgNotNullVariable(name: String, type: ClassId) : CgVariable(name, type) +//class CgNotNullVariable(name: String, type: ClassId) : CgVariable(name, type) +class CgNotNullAssertion(val expression: CgExpression) : CgValue { + override val type: ClassId + get() { + val expressionType = expression.type + return ClassId( + expressionType.name, + expressionType.elementClassId, + isNullable = false + ) + } +} /** * Method parameters declaration @@ -604,6 +619,21 @@ data class CgParameterDeclaration( get() = parameter.type } +/** + * Test methd parameter can be one of the following types: + * - this instance for method under test (MUT) + * - argument of MUT with a certain index + * - result expected from MUT with the given arguments + * - exception expected from MUT with the given arguments + */ +sealed class CgParameterType { + object ThisInstance : CgParameterType() + class Argument(val index: Int) : CgParameterType() + object ExpectedResult : CgParameterType() + object ExpectedException : CgParameterType() +} + + // Primitive and String literals class CgLiteral(override val type: ClassId, val value: Any?) : CgValue { @@ -627,7 +657,7 @@ class CgLiteral(override val type: ClassId, val value: Any?) : CgValue { } // Runnable like this::toString or (new Object())::toString (non-static) or Random::nextRandomInt (static) etc -abstract class CgRunnable(override val type: ClassId, val methodId: MethodId): CgValue +abstract class CgRunnable(override val type: ClassId, val methodId: MethodId) : CgValue /** * [referenceExpression] is "this" in this::toString or (new Object()) in (new Object())::toString (non-static) @@ -636,18 +666,23 @@ class CgNonStaticRunnable( type: ClassId, val referenceExpression: CgReferenceExpression, methodId: MethodId -): CgRunnable(type, methodId) +) : CgRunnable(type, methodId) /** * [classId] is Random is Random::nextRandomInt (static) etc */ -class CgStaticRunnable(type: ClassId, val classId: ClassId, methodId: MethodId): CgRunnable(type, methodId) +class CgStaticRunnable(type: ClassId, val classId: ClassId, methodId: MethodId) : CgRunnable(type, methodId) // Array allocation -open class CgAllocateArray(type: ClassId, elementType: ClassId, val size: Int) - : CgReferenceExpression { - override val type: ClassId by lazy { CgClassId(type.name, updateElementType(elementType), isNullable = type.isNullable) } +open class CgAllocateArray(type: ClassId, elementType: ClassId, val size: Int) : CgReferenceExpression { + override val type: ClassId by lazy { + CgClassId( + type.name, + updateElementType(elementType), + isNullable = type.isNullable + ) + } val elementType: ClassId by lazy { workaround(WorkaroundReason.ARRAY_ELEMENT_TYPES_ALWAYS_NULLABLE) { // for now all array element types are nullable @@ -663,13 +698,13 @@ open class CgAllocateArray(type: ClassId, elementType: ClassId, val size: Int) } } -class CgAllocateInitializedArray(val model: UtArrayModel) - : CgAllocateArray(model.classId, model.classId.elementClassId!!, model.length) +class CgAllocateInitializedArray(val model: UtArrayModel) : + CgAllocateArray(model.classId, model.classId.elementClassId!!, model.length) // Spread operator (for Kotlin, empty for Java) -class CgSpread(override val type: ClassId, val array: CgExpression): CgExpression +class CgSpread(override val type: ClassId, val array: CgExpression) : CgExpression // Enum constant diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgAbstractRenderer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgAbstractRenderer.kt index 866787fb17..0b3d68d737 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgAbstractRenderer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgAbstractRenderer.kt @@ -50,7 +50,6 @@ import org.utbot.framework.codegen.model.tree.CgMultilineComment import org.utbot.framework.codegen.model.tree.CgMultipleArgsAnnotation import org.utbot.framework.codegen.model.tree.CgNamedAnnotationArgument import org.utbot.framework.codegen.model.tree.CgNonStaticRunnable -import org.utbot.framework.codegen.model.tree.CgNotNullVariable import org.utbot.framework.codegen.model.tree.CgParameterDeclaration import org.utbot.framework.codegen.model.tree.CgParameterizedTestDataProviderMethod import org.utbot.framework.codegen.model.tree.CgRegion @@ -540,8 +539,6 @@ internal abstract class CgAbstractRenderer(val context: CgContext, val printer: print(element.name.escapeNamePossibleKeyword()) } - abstract override fun visit(element: CgNotNullVariable) - // Method parameters abstract override fun visit(element: CgParameterDeclaration) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt index dd6882690c..90b60d72d0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgJavaRenderer.kt @@ -22,7 +22,7 @@ import org.utbot.framework.codegen.model.tree.CgGetKotlinClass import org.utbot.framework.codegen.model.tree.CgGetLength import org.utbot.framework.codegen.model.tree.CgInnerBlock import org.utbot.framework.codegen.model.tree.CgMethod -import org.utbot.framework.codegen.model.tree.CgNotNullVariable +import org.utbot.framework.codegen.model.tree.CgNotNullAssertion import org.utbot.framework.codegen.model.tree.CgParameterDeclaration import org.utbot.framework.codegen.model.tree.CgParameterizedTestDataProviderMethod import org.utbot.framework.codegen.model.tree.CgReturnStatement @@ -127,6 +127,12 @@ internal class CgJavaRenderer(context: CgContext, printer: CgPrinter = CgPrinter element.expression.accept(this) } + // Not-null assertion + + override fun visit(element: CgNotNullAssertion) { + element.expression.accept(this) + } + override fun visit(element: CgParameterDeclaration) { if (element.isVararg) { print(element.type.elementClassId!!.asString()) @@ -150,10 +156,6 @@ internal class CgJavaRenderer(context: CgContext, printer: CgPrinter = CgPrinter error("KClass attempted to be used in the Java test class") } - override fun visit(element: CgNotNullVariable) { - print(element.name.escapeNamePossibleKeyword()) - } - override fun visit(element: CgAllocateArray) { // TODO: Arsen strongly required to rewrite later val typeName = element.type.canonicalName.substringBefore("[") diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt index c5fe8c3bd0..28f5626f16 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt @@ -27,7 +27,7 @@ import org.utbot.framework.codegen.model.tree.CgGetKotlinClass import org.utbot.framework.codegen.model.tree.CgGetLength import org.utbot.framework.codegen.model.tree.CgInnerBlock import org.utbot.framework.codegen.model.tree.CgMethod -import org.utbot.framework.codegen.model.tree.CgNotNullVariable +import org.utbot.framework.codegen.model.tree.CgNotNullAssertion import org.utbot.framework.codegen.model.tree.CgParameterDeclaration import org.utbot.framework.codegen.model.tree.CgParameterizedTestDataProviderMethod import org.utbot.framework.codegen.model.tree.CgSpread @@ -231,8 +231,9 @@ internal class CgKotlinRenderer(context: CgContext, printer: CgPrinter = CgPrint print("::class") } - override fun visit(element: CgNotNullVariable) { - print("${element.name.escapeNamePossibleKeyword()}!!") + override fun visit(element: CgNotNullAssertion) { + element.expression.accept(this) + print("!!") } override fun visit(element: CgAllocateArray) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgVisitor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgVisitor.kt index 23cf060706..db1146154f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgVisitor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgVisitor.kt @@ -52,7 +52,7 @@ import org.utbot.framework.codegen.model.tree.CgMultilineComment import org.utbot.framework.codegen.model.tree.CgMultipleArgsAnnotation import org.utbot.framework.codegen.model.tree.CgNamedAnnotationArgument import org.utbot.framework.codegen.model.tree.CgNonStaticRunnable -import org.utbot.framework.codegen.model.tree.CgNotNullVariable +import org.utbot.framework.codegen.model.tree.CgNotNullAssertion import org.utbot.framework.codegen.model.tree.CgParameterDeclaration import org.utbot.framework.codegen.model.tree.CgParameterizedTestDataProviderMethod import org.utbot.framework.codegen.model.tree.CgReturnStatement @@ -184,7 +184,10 @@ interface CgVisitor { // Variables fun visit(element: CgVariable): R - fun visit(element: CgNotNullVariable): R + + // Not-null assertion + + fun visit(element: CgNotNullAssertion): R // Method parameters fun visit(element: CgParameterDeclaration): R From 08928ee98c794c4d52a40a83955c123056f24397 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Mon, 18 Jul 2022 17:40:47 +0300 Subject: [PATCH 3/4] Additional work --- .../org/utbot/framework/plugin/api/Api.kt | 4 - .../utbot/framework/plugin/api/util/IdUtil.kt | 15 + .../assemble/AssemblePrimitiveWrapper.kt | 48 +++ .../constructor/tree/CgMethodConstructor.kt | 296 ++++++++---------- .../framework/codegen/model/tree/CgElement.kt | 7 +- 5 files changed, 203 insertions(+), 167 deletions(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssemblePrimitiveWrapper.kt diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index c16e8357b9..cd86dc6e37 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -685,10 +685,6 @@ open class ClassId( open val isSynthetic: Boolean get() = jClass.isSynthetic -// open val isNullable: Boolean -// // Treat simple class ids as non-nullable -// get() = false - /** * Collects all declared methods (including private and protected) from class and all its superclasses to sequence */ diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt index 9d2173115b..02779a6e93 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt @@ -236,6 +236,21 @@ val idToPrimitive: Map> = mapOf( */ fun isPrimitiveWrapperOrString(type: ClassId): Boolean = (type in primitiveWrappers) || (type == stringClassId) +/** + * Returns a wrapper of a given type if it is primitive or a type itself otherwise. + */ +fun wrapIfPrimitive(type: ClassId): ClassId = when (type) { + booleanClassId -> booleanWrapperClassId + byteClassId -> byteWrapperClassId + charClassId -> charWrapperClassId + shortClassId -> shortWrapperClassId + intClassId -> intWrapperClassId + longClassId -> longWrapperClassId + floatClassId -> floatWrapperClassId + doubleClassId -> doubleWrapperClassId + else -> type +} + /** * Note: currently uses class$innerClass form to load classes with classloader. */ diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssemblePrimitiveWrapper.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssemblePrimitiveWrapper.kt new file mode 100644 index 0000000000..b688548cde --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssemblePrimitiveWrapper.kt @@ -0,0 +1,48 @@ +package org.utbot.framework.assemble + +import org.utbot.framework.plugin.api.UtAssembleModel +import org.utbot.framework.plugin.api.UtExecutableCallModel +import org.utbot.framework.plugin.api.UtPrimitiveModel +import org.utbot.framework.plugin.api.util.booleanClassId +import org.utbot.framework.plugin.api.util.charClassId +import org.utbot.framework.plugin.api.util.doubleClassId +import org.utbot.framework.plugin.api.util.executableId +import org.utbot.framework.plugin.api.util.floatClassId +import org.utbot.framework.plugin.api.util.intClassId +import org.utbot.framework.plugin.api.util.longClassId +import org.utbot.framework.plugin.api.util.shortClassId +import org.utbot.framework.plugin.api.util.wrapIfPrimitive + +/** + * Creates [UtAssembleModel] of the wrapper for a given [UtPrimitiveModel]. + */ +fun assemble(model: UtPrimitiveModel): UtAssembleModel { + val modelType = model.classId + val assembledModelType = wrapIfPrimitive(modelType) + + val constructorCall = when (modelType) { + shortClassId -> java.lang.Short::class.java.getConstructor(Short::class.java) + intClassId -> java.lang.Integer::class.java.getConstructor(Int::class.java) + longClassId -> java.lang.Long::class.java.getConstructor(Long::class.java) + charClassId -> java.lang.Character::class.java.getConstructor(Char::class.java) + booleanClassId -> java.lang.Boolean::class.java.getConstructor(Boolean::class.java) + floatClassId -> java.lang.Float::class.java.getConstructor(Float::class.java) + doubleClassId -> java.lang.Double::class.java.getConstructor(Double::class.java) + else -> error("Model type $modelType is void or non-primitive") + } + + val constructorCallModel = UtExecutableCallModel( + instance = null, + executable = constructorCall.executableId, + params = listOf(model), + returnValue = null, + ) + + return UtAssembleModel( + id = null, + classId = assembledModelType, + modelName = modelType.canonicalName, + instantiationChain = listOf(constructorCallModel), + modificationsChain = emptyList(), + ) +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt index c2d73deb6e..976c2e42dc 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt @@ -3,6 +3,7 @@ package org.utbot.framework.codegen.model.constructor.tree import org.utbot.common.PathUtil import org.utbot.common.packageName import org.utbot.engine.isStatic +import org.utbot.framework.assemble.assemble import org.utbot.framework.codegen.ForceStaticMocking import org.utbot.framework.codegen.JUNIT5_PARAMETERIZED_PACKAGE import org.utbot.framework.codegen.Junit4 @@ -113,23 +114,18 @@ import org.utbot.framework.plugin.api.UtExecutionFailure import org.utbot.framework.plugin.api.UtExecutionSuccess import org.utbot.framework.plugin.api.UtExplicitlyThrownException import org.utbot.framework.plugin.api.UtMethod +import org.utbot.framework.plugin.api.UtMethodTestSet import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation import org.utbot.framework.plugin.api.UtNullModel import org.utbot.framework.plugin.api.UtPrimitiveModel import org.utbot.framework.plugin.api.UtReferenceModel import org.utbot.framework.plugin.api.UtStaticMethodInstrumentation -import org.utbot.framework.plugin.api.UtMethodTestSet import org.utbot.framework.plugin.api.UtTimeoutException import org.utbot.framework.plugin.api.UtVoidModel import org.utbot.framework.plugin.api.onFailure import org.utbot.framework.plugin.api.onSuccess import org.utbot.framework.plugin.api.util.booleanClassId -import org.utbot.framework.plugin.api.util.booleanWrapperClassId -import org.utbot.framework.plugin.api.util.byteClassId -import org.utbot.framework.plugin.api.util.byteWrapperClassId -import org.utbot.framework.plugin.api.util.charClassId -import org.utbot.framework.plugin.api.util.charWrapperClassId import org.utbot.framework.plugin.api.util.doubleArrayClassId import org.utbot.framework.plugin.api.util.doubleClassId import org.utbot.framework.plugin.api.util.doubleWrapperClassId @@ -140,24 +136,21 @@ import org.utbot.framework.plugin.api.util.floatWrapperClassId import org.utbot.framework.plugin.api.util.hasField import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.intClassId -import org.utbot.framework.plugin.api.util.intWrapperClassId import org.utbot.framework.plugin.api.util.isArray import org.utbot.framework.plugin.api.util.isInnerClassEnclosingClassReference import org.utbot.framework.plugin.api.util.isIterableOrMap import org.utbot.framework.plugin.api.util.isPrimitive import org.utbot.framework.plugin.api.util.isPrimitiveArray +import org.utbot.framework.plugin.api.util.isPrimitiveWrapper import org.utbot.framework.plugin.api.util.isRefType import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.kClass -import org.utbot.framework.plugin.api.util.longClassId -import org.utbot.framework.plugin.api.util.longWrapperClassId import org.utbot.framework.plugin.api.util.methodId import org.utbot.framework.plugin.api.util.objectArrayClassId import org.utbot.framework.plugin.api.util.objectClassId -import org.utbot.framework.plugin.api.util.shortClassId -import org.utbot.framework.plugin.api.util.shortWrapperClassId import org.utbot.framework.plugin.api.util.stringClassId import org.utbot.framework.plugin.api.util.voidClassId +import org.utbot.framework.plugin.api.util.wrapIfPrimitive import org.utbot.framework.util.isUnit import org.utbot.summary.SummarySentenceConstants.TAB import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl @@ -451,25 +444,15 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c if (result.isUnit()) { +thisInstance[method](*methodArguments.toTypedArray()) } else { - resultModel = result - - val expected = CgNotNullAssertion(currentMethodParameters[CgParameterType.ExpectedResult]!!) - -// val actualVariableName = when (codegenLanguage) { -// CodegenLanguage.JAVA -> when (method.returnType) { -// intClassId -> "${intWrapperClassId.simpleName.capitalize()}.valueOf(actual)" -// shortClassId -> "${shortWrapperClassId.simpleName.capitalize()}.valueOf(actual)" -// longClassId -> "${longWrapperClassId.simpleName.capitalize()}.valueOf(actual)" -// byteClassId -> "${byteWrapperClassId.simpleName.capitalize()}.valueOf(actual)" -// booleanClassId -> "${booleanWrapperClassId.simpleName.capitalize()}.valueOf(actual)" -// charClassId -> "${charWrapperClassId.simpleName.capitalize()}.valueOf(actual)" -// floatClassId -> "${floatWrapperClassId.simpleName.capitalize()}.valueOf(actual)" -// doubleWrapperClassId -> "${doubleWrapperClassId.simpleName.capitalize()}.valueOf(actual)" -// else -> "actual" -// } -// CodegenLanguage.KOTLIN -> "actual" -// } - assertEquality(expected, actual)// CgVariable(actualVariableName, method.returnType)) + //"generic" expected variable is represented with a wrapper if + //actual result is primitive to support cases with exceptions. + resultModel = if (result is UtPrimitiveModel) assemble(result) else result + + val expectedVariable = currentMethodParameters[CgParameterType.ExpectedResult]!! + val expectedExpression = CgNotNullAssertion(expectedVariable) + + assertEquality(expectedExpression, actual) + println() } } .onFailure { thisInstance[method](*methodArguments.toTypedArray()).intercepted() } @@ -644,6 +627,11 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c ).toStatement() } is UtAssembleModel -> { + if (expectedModel.classId.isPrimitiveWrapper) { + statements += assertions[assertEquals](expected, actual).toStatement() + return + } + // UtCompositeModel deep equals is much more easier and human friendly expectedModel.origin?.let { assertDeepEquals(it, expected, actual, statements, depth, visitedModels) @@ -1057,13 +1045,19 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } } else -> { - if (expected is CgLiteral) { - // Literal can only be Primitive or String, can use equals here - testFrameworkManager.assertEquals(expected, actual) - return + when (expected) { + is CgLiteral -> { + // Literal can only be Primitive or String, can use equals here + testFrameworkManager.assertEquals(expected, actual) + } + is CgNotNullAssertion -> { + require(expected.expression is CgVariable) { + "Only Variable wrapped in CgNotNullAssertion is supported in deep equals" + } + currentBlock = currentBlock.addAll(generateDeepEqualsAssertion(expected.expression, actual)) + } + else -> generateDeepEqualsOrNullAssertion(expected, actual) } - - generateDeepEqualsOrNullAssertion(expected, actual) } } } @@ -1224,142 +1218,140 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c private val expectedErrorVarName = "expectedError" fun createParameterizedTestMethod(testSet: UtMethodTestSet, dataProviderMethodName: String): CgTestMethod? { - val methodUnderTest = testSet.method - val methodUnderTestParameters = testSet.method.callable.parameters - if (testSet.executions.isEmpty()) { return null } - //TODO: orientation on arbitrary execution may be misleading, but what is the alternative? + //TODO: orientation on generic execution may be misleading, but what is the alternative? //may be a heuristic to select a model with minimal number of internal nulls should be used - val arbitraryExecution = testSet.executions + val genericExecution = testSet.executions .firstOrNull { it.result is UtExecutionSuccess && (it.result as UtExecutionSuccess).model !is UtNullModel } ?: testSet.executions.first() - return withTestMethodScope(arbitraryExecution) { + return withTestMethodScope(genericExecution) { val testName = nameGenerator.parameterizedTestMethodName(dataProviderMethodName) - // TODO: move out to a separate method e.g. createMethodParameters() - // create parameter declarations - val testParameterDeclarations = mutableListOf().apply { - // this instance - val thisInstanceModel = arbitraryExecution.stateBefore.thisInstance - if (thisInstanceModel != null) { - val type = thisInstanceModel.classId - val thisInstance = CgParameterDeclaration( - parameter = declareParameter( - type = type, - name = nameGenerator.variableName(type) - ), - isReferenceType = true - ) - this += thisInstance - currentMethodParameters[CgParameterType.ThisInstance] = thisInstance.parameter - } - // arguments - for (index in arbitraryExecution.stateBefore.parameters.indices) { - val argumentName = paramNames[methodUnderTest]?.get(index) - val paramIndex = if (methodUnderTest.isStatic) index else index + 1 - val paramType = methodUnderTestParameters[paramIndex].type.javaType - - val argumentType = when { - paramType is Class<*> && paramType.isArray -> paramType.id - paramType is ParameterizedTypeImpl -> paramType.rawType.id - else -> ClassId(paramType.typeName) - } + withNameScope { + val testParameterDeclarations = createParameterDeclarations(testSet, genericExecution) + val mainBody = { + // build this instance + thisInstance = genericExecution.stateBefore.thisInstance?.let { currentMethodParameters[CgParameterType.ThisInstance] } - val argument = CgParameterDeclaration( - parameter = declareParameter( - type = argumentType, - name = nameGenerator.variableName(argumentType, argumentName), - ), - isReferenceType = argumentType.isRefType - ) - this += argument - currentMethodParameters[CgParameterType.Argument(index)] = argument.parameter - } + // build arguments for method under test and parameterized test + for (index in genericExecution.stateBefore.parameters.indices) { + methodArguments += currentMethodParameters[CgParameterType.Argument(index)]!! + } - val method = currentExecutable as MethodId - val containsFailureExecution = containsFailureExecution(testSet) - - val expectedResultClassId = wrapTypeIfRequired(method.returnType) - - if (expectedResultClassId != voidClassId) { - val resultType = resultClassId(expectedResultClassId) - val expectedResult = CgParameterDeclaration( - parameter = declareParameter( - type = resultType, - name = nameGenerator.variableName(expectedResultVarName) - ), - isReferenceType = resultType.isRefType - ) - this += expectedResult - currentMethodParameters[CgParameterType.ExpectedResult] = expectedResult.parameter + //record result and generate result assertions + recordActualResult() + generateAssertionsForParameterizedTest() } - if (containsFailureExecution) { - val expectedException = CgParameterDeclaration( - parameter = declareParameter( - type = throwableClassId(), - name = nameGenerator.variableName(expectedErrorVarName) - ), - // exceptions are always reference type - isReferenceType = true - ) - this += expectedException - currentMethodParameters[CgParameterType.ExpectedException] = expectedException.parameter + methodType = PARAMETRIZED + testMethod( + testName, + displayName = null, + testParameterDeclarations, + parameterized = true, + dataProviderMethodName + ) { + if (containsFailureExecution(testSet)) { + +tryBlock(mainBody) + .catch(Throwable::class.java.id) { e -> + val pseudoExceptionVarName = when (codegenLanguage) { + CodegenLanguage.JAVA -> "${expectedErrorVarName}.isInstance(${e.name.decapitalize()})" + CodegenLanguage.KOTLIN -> "${expectedErrorVarName}!!.isInstance(${e.name.decapitalize()})" + } + + testFrameworkManager.assertBoolean(CgVariable(pseudoExceptionVarName, booleanClassId)) + } + } else { + mainBody() + } } } + } + } -// currentMethodParameters += testParameterDeclarations.map { it.parameter } - - val mainBody = { - // build this instance - thisInstance = arbitraryExecution.stateBefore.thisInstance?.let { currentMethodParameters[0] } + private fun createParameterDeclarations( + testSet: UtMethodTestSet, + genericExecution: UtExecution, + ): List { + val methodUnderTest = testSet.method + val methodUnderTestParameters = testSet.method.callable.parameters - // build arguments for method under test and parameterized test - for (index in arbitraryExecution.stateBefore.parameters.indices) { - methodArguments += currentMethodParameters[CgParameterType.Argument(index)]!! + return mutableListOf().apply { + // this instance + val thisInstanceModel = genericExecution.stateBefore.thisInstance + if (thisInstanceModel != null) { + val type = thisInstanceModel.classId + val thisInstance = CgParameterDeclaration( + parameter = declareParameter( + type = type, + name = nameGenerator.variableName(type) + ), + isReferenceType = true + ) + this += thisInstance + currentMethodParameters[CgParameterType.ThisInstance] = thisInstance.parameter + } + // arguments + for (index in genericExecution.stateBefore.parameters.indices) { + val argumentName = paramNames[methodUnderTest]?.get(index) + val paramIndex = if (methodUnderTest.isStatic) index else index + 1 + val paramType = methodUnderTestParameters[paramIndex].type.javaType + + val argumentType = when { + paramType is Class<*> && paramType.isArray -> paramType.id + paramType is ParameterizedTypeImpl -> paramType.rawType.id + else -> ClassId(paramType.typeName) } - //record result and generate result assertions - recordActualResult() - generateAssertionsForParameterizedTest() + val argument = CgParameterDeclaration( + parameter = declareParameter( + type = argumentType, + name = nameGenerator.variableName(argumentType, argumentName), + ), + isReferenceType = argumentType.isRefType + ) + this += argument + currentMethodParameters[CgParameterType.Argument(index)] = argument.parameter } - methodType = PARAMETRIZED - testMethod( - testName, - displayName = null, - testParameterDeclarations, - parameterized = true, - dataProviderMethodName - ) { - if (containsFailureExecution(testSet)) { - +tryBlock(mainBody) - .catch(Throwable::class.java.id) { e -> - val pseudoExceptionVarName = when (codegenLanguage) { - CodegenLanguage.JAVA -> "${expectedErrorVarName}.isInstance(${e.name.decapitalize()})" - CodegenLanguage.KOTLIN -> "${expectedErrorVarName}!!.isInstance(${e.name.decapitalize()})" - } + val method = currentExecutable as MethodId + val containsFailureExecution = containsFailureExecution(testSet) + + val expectedResultClassId = wrapTypeIfRequired(method.returnType) + + if (expectedResultClassId != voidClassId) { + val wrappedType = wrapIfPrimitive(expectedResultClassId) + //We are required to wrap the type of expected result if it is primitive + //to support nulls for throwing exceptions executions. + val expectedResult = CgParameterDeclaration( + parameter = declareParameter( + type = wrappedType, + name = nameGenerator.variableName(expectedResultVarName) + ), + isReferenceType = wrappedType.isRefType + ) + this += expectedResult + currentMethodParameters[CgParameterType.ExpectedResult] = expectedResult.parameter + } - testFrameworkManager.assertBoolean(CgVariable(pseudoExceptionVarName, booleanClassId)) - } - } else { - mainBody() - } + if (containsFailureExecution) { + val expectedException = CgParameterDeclaration( + parameter = declareParameter( + type = throwableClassId(), + name = nameGenerator.variableName(expectedErrorVarName) + ), + // exceptions are always reference type + isReferenceType = true + ) + this += expectedException + currentMethodParameters[CgParameterType.ExpectedException] = expectedException.parameter } } } -// /** -// * Constructs a variable for the instance parameter of parametrized test. -// */ -// private fun constructInstanceVariable(instanceModel: UtModel): CgValue { -// val className = instanceModel.classId.simpleName.decapitalize() -// return variableConstructor.getParameterByName(className) -// } - /** * Constructs data provider method for parameterized tests. * @@ -1589,18 +1581,6 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c private fun containsFailureExecution(testSet: UtMethodTestSet) = testSet.executions.any { it.result is UtExecutionFailure } - private fun resultClassId(returnType: ClassId): ClassId = when (returnType) { - booleanClassId -> booleanWrapperClassId - byteClassId -> byteWrapperClassId - charClassId -> charWrapperClassId - shortClassId -> shortWrapperClassId - intClassId -> intWrapperClassId - longClassId -> longWrapperClassId - floatClassId -> floatWrapperClassId - doubleClassId -> doubleWrapperClassId - else -> returnType - } - /** * A [ClassId] for Class. */ diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt index 8b68ba6992..d73f0270c9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt @@ -77,8 +77,6 @@ interface CgElement { is CgAssignment -> visit(element) is CgTypeCast -> visit(element) is CgThisInstance -> visit(element) - //Not that order of variables is important -// is CgNotNullVariable -> visit(element) is CgNotNullAssertion -> visit(element) is CgVariable -> visit(element) is CgParameterDeclaration -> visit(element) @@ -523,7 +521,7 @@ interface CgExpression : CgStatement { } // marker interface representing expressions returning reference -// TODO: talk to Yury (?), because it seems that not all CgValues are reference expressions +// TODO: it seems that not all [CgValue] implementations are reference expressions interface CgReferenceExpression : CgExpression /** @@ -584,7 +582,6 @@ open class CgVariable( * - in Java it is an equivalent of [CgVariable] * - in Kotlin the difference is in addition of "!!" to the name */ -//class CgNotNullVariable(name: String, type: ClassId) : CgVariable(name, type) class CgNotNullAssertion(val expression: CgExpression) : CgValue { override val type: ClassId get() { @@ -628,7 +625,7 @@ data class CgParameterDeclaration( */ sealed class CgParameterType { object ThisInstance : CgParameterType() - class Argument(val index: Int) : CgParameterType() + data class Argument(val index: Int) : CgParameterType() object ExpectedResult : CgParameterType() object ExpectedException : CgParameterType() } From dbbff7ca05fc27b4c4362c3345a44439d2b440f3 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Wed, 20 Jul 2022 14:30:13 +0300 Subject: [PATCH 4/4] Apply several fixes --- .../org/utbot/framework/plugin/api/Api.kt | 10 ++----- .../assemble/AssemblePrimitiveWrapper.kt | 2 ++ .../model/constructor/context/CgContext.kt | 6 ++-- .../constructor/tree/CgMethodConstructor.kt | 16 +++++------ .../framework/codegen/model/tree/CgElement.kt | 28 +++++++++++-------- 5 files changed, 32 insertions(+), 30 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index cd86dc6e37..e7c2e0bbc7 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -610,17 +610,12 @@ val Type.classId: ClassId * [elementClassId] if this class id represents an array class, then this property * represents the class id of the array's elements. Otherwise, this property is null. */ -open class ClassId( +open class ClassId @JvmOverloads constructor( val name: String, val elementClassId: ClassId? = null, // Treat simple class ids as non-nullable open val isNullable: Boolean = false ) { - /** - * This constructor is for calls from Java (e.g. `UtBotJavaApiTest`), - * because we cannot skip default parameters in Java. - */ - constructor(name: String, elementClassId: ClassId?) : this(name, elementClassId, false) open val canonicalName: String get() = jClass.canonicalName ?: error("ClassId $name does not have canonical name") @@ -759,6 +754,7 @@ class BuiltinClassId( override val simpleName: String, // by default we assume that the class is not a member class override val simpleNameWithEnclosings: String = simpleName, + override val isNullable: Boolean = false, override val isPublic: Boolean = true, override val isProtected: Boolean = false, override val isPrivate: Boolean = false, @@ -778,7 +774,7 @@ class BuiltinClassId( -1, 0 -> "" else -> canonicalName.substring(0, index) }, -) : ClassId(name) { +) : ClassId(name = name, isNullable = isNullable) { init { BUILTIN_CLASSES_BY_NAMES[name] = this } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssemblePrimitiveWrapper.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssemblePrimitiveWrapper.kt index b688548cde..2b83b35897 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssemblePrimitiveWrapper.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssemblePrimitiveWrapper.kt @@ -4,6 +4,7 @@ import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtExecutableCallModel import org.utbot.framework.plugin.api.UtPrimitiveModel import org.utbot.framework.plugin.api.util.booleanClassId +import org.utbot.framework.plugin.api.util.byteClassId import org.utbot.framework.plugin.api.util.charClassId import org.utbot.framework.plugin.api.util.doubleClassId import org.utbot.framework.plugin.api.util.executableId @@ -25,6 +26,7 @@ fun assemble(model: UtPrimitiveModel): UtAssembleModel { intClassId -> java.lang.Integer::class.java.getConstructor(Int::class.java) longClassId -> java.lang.Long::class.java.getConstructor(Long::class.java) charClassId -> java.lang.Character::class.java.getConstructor(Char::class.java) + byteClassId -> java.lang.Byte::class.java.getConstructor(Byte::class.java) booleanClassId -> java.lang.Boolean::class.java.getConstructor(Boolean::class.java) floatClassId -> java.lang.Float::class.java.getConstructor(Float::class.java) doubleClassId -> java.lang.Double::class.java.getConstructor(Double::class.java) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt index 9c4349322c..bd72b6bf77 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt @@ -54,7 +54,7 @@ import kotlinx.collections.immutable.persistentListOf 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.CgParameterType +import org.utbot.framework.codegen.model.tree.CgParameterKind import org.utbot.framework.plugin.api.util.executableId import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.isCheckedException @@ -189,7 +189,7 @@ internal interface CgContextOwner { var valueByModelId: MutableMap // parameters of the method currently being generated - val currentMethodParameters: MutableMap + val currentMethodParameters: MutableMap val testClassCustomName: String? @@ -451,7 +451,7 @@ internal data class CgContext( override var valueByModelId: MutableMap = mutableMapOf() - override val currentMethodParameters: MutableMap = mutableMapOf() + override val currentMethodParameters: MutableMap = mutableMapOf() override val testClassThisInstance: CgThisInstance = CgThisInstance(currentTestClass) } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt index 976c2e42dc..c9b7f2f231 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt @@ -52,7 +52,7 @@ import org.utbot.framework.codegen.model.tree.CgMethodCall import org.utbot.framework.codegen.model.tree.CgMultilineComment import org.utbot.framework.codegen.model.tree.CgNotNullAssertion import org.utbot.framework.codegen.model.tree.CgParameterDeclaration -import org.utbot.framework.codegen.model.tree.CgParameterType +import org.utbot.framework.codegen.model.tree.CgParameterKind import org.utbot.framework.codegen.model.tree.CgParameterizedTestDataProviderMethod import org.utbot.framework.codegen.model.tree.CgRegion import org.utbot.framework.codegen.model.tree.CgReturnStatement @@ -448,7 +448,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c //actual result is primitive to support cases with exceptions. resultModel = if (result is UtPrimitiveModel) assemble(result) else result - val expectedVariable = currentMethodParameters[CgParameterType.ExpectedResult]!! + val expectedVariable = currentMethodParameters[CgParameterKind.ExpectedResult]!! val expectedExpression = CgNotNullAssertion(expectedVariable) assertEquality(expectedExpression, actual) @@ -1234,11 +1234,11 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c val testParameterDeclarations = createParameterDeclarations(testSet, genericExecution) val mainBody = { // build this instance - thisInstance = genericExecution.stateBefore.thisInstance?.let { currentMethodParameters[CgParameterType.ThisInstance] } + thisInstance = genericExecution.stateBefore.thisInstance?.let { currentMethodParameters[CgParameterKind.ThisInstance] } // build arguments for method under test and parameterized test for (index in genericExecution.stateBefore.parameters.indices) { - methodArguments += currentMethodParameters[CgParameterType.Argument(index)]!! + methodArguments += currentMethodParameters[CgParameterKind.Argument(index)]!! } //record result and generate result assertions @@ -1292,7 +1292,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c isReferenceType = true ) this += thisInstance - currentMethodParameters[CgParameterType.ThisInstance] = thisInstance.parameter + currentMethodParameters[CgParameterKind.ThisInstance] = thisInstance.parameter } // arguments for (index in genericExecution.stateBefore.parameters.indices) { @@ -1314,7 +1314,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c isReferenceType = argumentType.isRefType ) this += argument - currentMethodParameters[CgParameterType.Argument(index)] = argument.parameter + currentMethodParameters[CgParameterKind.Argument(index)] = argument.parameter } val method = currentExecutable as MethodId @@ -1334,7 +1334,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c isReferenceType = wrappedType.isRefType ) this += expectedResult - currentMethodParameters[CgParameterType.ExpectedResult] = expectedResult.parameter + currentMethodParameters[CgParameterKind.ExpectedResult] = expectedResult.parameter } if (containsFailureExecution) { @@ -1347,7 +1347,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c isReferenceType = true ) this += expectedException - currentMethodParameters[CgParameterType.ExpectedException] = expectedException.parameter + currentMethodParameters[CgParameterKind.ExpectedException] = expectedException.parameter } } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt index d73f0270c9..faa3fc155a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt @@ -4,9 +4,9 @@ import org.utbot.common.WorkaroundReason import org.utbot.common.workaround import org.utbot.framework.codegen.Import import org.utbot.framework.codegen.model.constructor.tree.TestsGenerationReport -import org.utbot.framework.codegen.model.constructor.util.CgStatementConstructor import org.utbot.framework.codegen.model.util.CgExceptionHandler import org.utbot.framework.codegen.model.visitor.CgVisitor +import org.utbot.framework.plugin.api.BuiltinClassId import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ConstructorId import org.utbot.framework.plugin.api.DocClassLinkStmt @@ -26,7 +26,6 @@ import org.utbot.framework.plugin.api.util.intClassId import org.utbot.framework.plugin.api.util.objectArrayClassId import org.utbot.framework.plugin.api.util.objectClassId import org.utbot.framework.plugin.api.util.voidClassId -import kotlin.math.exp interface CgElement { // TODO: order of cases is important here due to inheritance between some of the element types @@ -584,12 +583,17 @@ open class CgVariable( */ class CgNotNullAssertion(val expression: CgExpression) : CgValue { override val type: ClassId - get() { - val expressionType = expression.type - return ClassId( + get() = when (val expressionType = expression.type) { + is BuiltinClassId -> BuiltinClassId( + name = expressionType.name, + canonicalName = expressionType.canonicalName, + simpleName = expressionType.simpleName, + isNullable = false, + ) + else -> ClassId( expressionType.name, expressionType.elementClassId, - isNullable = false + isNullable = false, ) } } @@ -617,17 +621,17 @@ data class CgParameterDeclaration( } /** - * Test methd parameter can be one of the following types: + * Test method parameter can be one of the following types: * - this instance for method under test (MUT) * - argument of MUT with a certain index * - result expected from MUT with the given arguments * - exception expected from MUT with the given arguments */ -sealed class CgParameterType { - object ThisInstance : CgParameterType() - data class Argument(val index: Int) : CgParameterType() - object ExpectedResult : CgParameterType() - object ExpectedException : CgParameterType() +sealed class CgParameterKind { + object ThisInstance : CgParameterKind() + data class Argument(val index: Int) : CgParameterKind() + object ExpectedResult : CgParameterKind() + object ExpectedException : CgParameterKind() }