Skip to content

Support parametrized test generation for constructors, even with parameters #637

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,8 @@ internal class CgNameGeneratorImpl(private val context: CgContext)
}

override fun testMethodNameFor(executableId: ExecutableId, customName: String?): String {
val executableName = when (executableId) {
is ConstructorId -> executableId.classId.prettifiedName // TODO: maybe we need some suffix e.g. "Ctor"?
is MethodId -> executableId.name
}
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
Expand All @@ -107,17 +105,15 @@ internal class CgNameGeneratorImpl(private val context: CgContext)
dataProviderMethodName.replace(dataProviderMethodPrefix, "parameterizedTestsFor")

override fun dataProviderMethodNameFor(executableId: ExecutableId): String {
val indexedName = nextIndexedMethodName(executableId.name.capitalize(), skipOne = true)
val executableName = createExecutableName(executableId)
val indexedName = nextIndexedMethodName(executableName.capitalize(), skipOne = true)

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

override fun errorMethodNameFor(executableId: ExecutableId): String {
val executableName = when (executableId) {
is ConstructorId -> executableId.classId.prettifiedName
is MethodId -> executableId.name
}
val executableName = createExecutableName(executableId)
val newName = when (val base = "test${executableName.capitalize()}_errors") {
!in existingMethodNames -> base
else -> nextIndexedMethodName(base)
Expand Down Expand Up @@ -151,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
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,30 +282,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
*/
private fun generateResultAssertions() {
when (currentExecutable) {
is ConstructorId -> {
// we cannot generate any assertions for constructor testing
// but we need to generate a constructor call
val constructorCall = currentExecutable as ConstructorId
val currentExecution = currentExecution!!
currentExecution.result
.onSuccess {
methodType = SUCCESSFUL

// TODO engine returns UtCompositeModel sometimes (concrete execution?)

// TODO support inner classes constructors testing JIRA:1461
require(!constructorCall.classId.isInner) {
"Inner class ${constructorCall.classId} constructor testing is not supported yet"
}

actual = newVar(constructorCall.classId, "actual") {
constructorCall(*methodArguments.toTypedArray())
}
}
.onFailure { exception ->
processExecutionFailure(currentExecution, exception)
}
}
is ConstructorId -> generateConstructorCall(currentExecutable!!, currentExecution!!)
is BuiltinMethodId -> error("Unexpected BuiltinMethodId $currentExecutable while generating result assertions")
is MethodId -> {
emptyLineIfNeeded()
Expand Down Expand Up @@ -430,24 +407,30 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
*/
private fun generateAssertionsForParameterizedTest() {
emptyLineIfNeeded()
val method = currentExecutable as MethodId
currentExecution!!.result
.onSuccess { result ->
if (result.isUnit()) {
+thisInstance[method](*methodArguments.toTypedArray())
} else {
//"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[CgParameterKind.ExpectedResult]!!
val expectedExpression = CgNotNullAssertion(expectedVariable)
when (currentExecutable) {
is ConstructorId -> generateConstructorCall(currentExecutable!!, currentExecution!!)
is MethodId -> {
val method = currentExecutable as MethodId
currentExecution!!.result
.onSuccess { result ->
if (result.isUnit()) {
+thisInstance[method](*methodArguments.toTypedArray())
} else {
//"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

assertEquality(expectedExpression, actual)
println()
}
val expectedVariable = currentMethodParameters[CgParameterKind.ExpectedResult]!!
val expectedExpression = CgNotNullAssertion(expectedVariable)

assertEquality(expectedExpression, actual)
println()
}
}
.onFailure { thisInstance[method](*methodArguments.toTypedArray()).intercepted() }
}
.onFailure { thisInstance[method](*methodArguments.toTypedArray()).intercepted() }
}
}

/**
Expand Down Expand Up @@ -1020,6 +1003,27 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
generateDeepEqualsOrNullAssertion(expected.expression, actual)
}

private fun generateConstructorCall(currentExecutableId: ExecutableId, currentExecution: UtExecution) {
// we cannot generate any assertions for constructor testing
// but we need to generate a constructor call
val constructorCall = currentExecutableId as ConstructorId
currentExecution.result
.onSuccess {
methodType = SUCCESSFUL

require(!constructorCall.classId.isInner) {
"Inner class ${constructorCall.classId} constructor testing is not supported yet"
}

actual = newVar(constructorCall.classId, "actual") {
constructorCall(*methodArguments.toTypedArray())
}
}
.onFailure { exception ->
processExecutionFailure(currentExecution, exception)
}
}

/**
* We can't use standard deepEquals method in parametrized tests
* because nullable objects require different asserts.
Expand Down Expand Up @@ -1258,9 +1262,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
currentMethodParameters[CgParameterKind.Argument(index)] = argument.parameter
}

val method = currentExecutable as MethodId
val containsFailureExecution = containsFailureExecution(testSet)

val method = currentExecutable!!
val expectedResultClassId = wrapTypeIfRequired(method.returnType)

if (expectedResultClassId != voidClassId) {
Expand All @@ -1278,6 +1280,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
currentMethodParameters[CgParameterKind.ExpectedResult] = expectedResult.parameter
}

val containsFailureExecution = containsFailureExecution(testSet)
if (containsFailureExecution) {
val classClassId = Class::class.id
val expectedException = CgParameterDeclaration(
Expand Down Expand Up @@ -1343,7 +1346,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
arguments += variableConstructor.getOrCreateVariable(paramModel, argumentName)
}

val method = currentExecutable as MethodId
val method = currentExecutable!!
val needsReturnValue = method.returnType != voidClassId
val containsFailureExecution = containsFailureExecution(testSet)
execution.result
Expand Down