Skip to content

Commit

Permalink
Refactor Class rendering in codegen (merge abstractions for regular a…
Browse files Browse the repository at this point in the history
…nd test classes) #1255 #1256 (#1275)


* Refactor Class rendering in codegen (merge abstractions for regular and test classes)
  • Loading branch information
volivan239 authored Nov 1, 2022
1 parent 95d503a commit e625e45
Show file tree
Hide file tree
Showing 14 changed files with 178 additions and 253 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import org.utbot.framework.plugin.api.util.isPrimitive
import org.utbot.framework.plugin.api.util.isStatic
import org.utbot.framework.plugin.api.util.jClass
import org.utbot.framework.plugin.api.util.jField
import org.utbot.framework.plugin.api.util.kClass
import org.utbot.framework.plugin.api.util.longClassId
import org.utbot.framework.plugin.api.util.method
import org.utbot.framework.plugin.api.util.primitiveTypeJvmNameOrNull
Expand Down Expand Up @@ -793,6 +794,9 @@ open class ClassId @JvmOverloads constructor(
open val isSynthetic: Boolean
get() = jClass.isSynthetic

open val isKotlinObject: Boolean
get() = kClass.objectInstance != null

/**
* Collects all declared methods (including private and protected) from class and all its superclasses to sequence
*/
Expand Down Expand Up @@ -887,6 +891,7 @@ class BuiltinClassId(
override val isInner: Boolean = false,
override val isNested: Boolean = false,
override val isSynthetic: Boolean = false,
override val isKotlinObject: Boolean = false,
override val typeParameters: TypeParameters = TypeParameters(),
override val allMethods: Sequence<MethodId> = emptySequence(),
override val allConstructors: Sequence<ConstructorId> = emptySequence(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,16 @@ import org.utbot.framework.codegen.model.constructor.context.CgContextOwner
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor
import org.utbot.framework.codegen.model.constructor.tree.CgUtilClassConstructor
import org.utbot.framework.codegen.model.constructor.tree.TestsGenerationReport
import org.utbot.framework.codegen.model.tree.AbstractCgClassFile
import org.utbot.framework.codegen.model.tree.CgRegularClassFile
import org.utbot.framework.codegen.model.tree.CgClassFile
import org.utbot.framework.codegen.model.visitor.CgAbstractRenderer
import org.utbot.framework.plugin.api.ClassId
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.UtMethodTestSet
import org.utbot.framework.codegen.model.constructor.TestClassModel
import org.utbot.framework.codegen.model.tree.CgComment
import org.utbot.framework.codegen.model.tree.CgSingleLineComment
import org.utbot.framework.codegen.model.tree.CgDocRegularStmt
import org.utbot.framework.codegen.model.tree.CgDocumentationComment

class CodeGenerator(
private val classUnderTest: ClassId,
Expand Down Expand Up @@ -106,7 +105,7 @@ class CodeGenerator(
}
}

private fun renderClassFile(file: AbstractCgClassFile<*>): String {
private fun renderClassFile(file: CgClassFile): String {
val renderer = CgAbstractRenderer.makeRenderer(context)
file.accept(renderer)
return renderer.toString()
Expand Down Expand Up @@ -145,30 +144,24 @@ sealed class UtilClassKind(
private val priority: Int
) : Comparable<UtilClassKind> {

/**
* Contains comments specifying the version and the kind of util class being generated and
*/
val utilClassDocumentation: CgDocumentationComment
get() = CgDocumentationComment(
listOf(
CgDocRegularStmt(utilClassKindCommentText),
CgDocRegularStmt("$UTIL_CLASS_VERSION_COMMENT_PREFIX${utilClassVersion}"),
)
)

/**
* The version of util class being generated.
* For more details see [UtilClassFileMethodProvider.UTIL_CLASS_VERSION].
*/
val utilClassVersion: String
get() = UtilClassFileMethodProvider.UTIL_CLASS_VERSION

/**
* The comment specifying the version of util class being generated.
*
* @see UtilClassFileMethodProvider.UTIL_CLASS_VERSION
*/
val utilClassVersionComment: CgComment
get() = CgSingleLineComment("$UTIL_CLASS_VERSION_COMMENT_PREFIX${utilClassVersion}")


/**
* The comment specifying the kind of util class being generated.
*
* @see utilClassKindCommentText
*/
val utilClassKindComment: CgComment
get() = CgSingleLineComment(utilClassKindCommentText)

/**
* The text of comment specifying the kind of util class.
* At the moment, there are two kinds: [RegularUtUtils] (without Mockito) and [UtUtilsWithMockito].
Expand Down Expand Up @@ -200,7 +193,7 @@ sealed class UtilClassKind(
}

/**
* Construct an util class file as a [CgRegularClassFile] and render it.
* Construct an util class file as a [CgClassFile] and render it.
* @return the text of the generated util class file.
*/
fun getUtilClassText(codegenLanguage: CodegenLanguage): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import org.utbot.framework.codegen.model.constructor.context.CgContextOwner
import org.utbot.framework.codegen.model.tree.CgAnnotation
import org.utbot.framework.codegen.model.tree.CgMethod
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.codegen.model.tree.CgTestClass
import org.utbot.framework.codegen.model.tree.CgClass

/**
* This class stores context information needed to build [CgTestClass].
* This class stores context information needed to build [CgClass].
* Should only be used in [CgContextOwner].
*/
internal data class TestClassContext(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ internal val utUtilsClassId: ClassId
canonicalName = "org.utbot.runtime.utils.UtUtils",
simpleName = "UtUtils",
isFinal = true,
isKotlinObject = true
)

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,8 @@ internal data class CgContext(
val name = "$packagePrefix$simpleName"
BuiltinClassId(
canonicalName = name,
simpleName = simpleName
simpleName = simpleName,
isFinal = true,
)
}

Expand Down Expand Up @@ -551,7 +552,8 @@ internal data class CgContext(
val simpleName = "${testClassModel.classUnderTest.simpleName}Test"
return BuiltinClassId(
canonicalName = currentTestClass.canonicalName + "." + simpleName,
simpleName = simpleName
simpleName = simpleName,
isFinal = true,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,21 @@ import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor
import org.utbot.framework.codegen.model.constructor.util.CgStatementConstructor
import org.utbot.framework.codegen.model.constructor.util.CgStatementConstructorImpl
import org.utbot.framework.codegen.model.tree.CgAuxiliaryClass
import org.utbot.framework.codegen.model.tree.CgExecutableUnderTestCluster
import org.utbot.framework.codegen.model.tree.CgMethodsCluster
import org.utbot.framework.codegen.model.tree.CgMethod
import org.utbot.framework.codegen.model.tree.CgRegion
import org.utbot.framework.codegen.model.tree.CgSimpleRegion
import org.utbot.framework.codegen.model.tree.CgStaticsRegion
import org.utbot.framework.codegen.model.tree.CgTestClass
import org.utbot.framework.codegen.model.tree.CgClass
import org.utbot.framework.codegen.model.tree.CgRealNestedClassesRegion
import org.utbot.framework.codegen.model.tree.CgTestClassFile
import org.utbot.framework.codegen.model.tree.CgTestMethod
import org.utbot.framework.codegen.model.tree.CgTestMethodCluster
import org.utbot.framework.codegen.model.tree.CgTripleSlashMultilineComment
import org.utbot.framework.codegen.model.tree.CgUtilEntity
import org.utbot.framework.codegen.model.tree.CgUtilMethod
import org.utbot.framework.codegen.model.tree.buildTestClass
import org.utbot.framework.codegen.model.tree.buildTestClassBody
import org.utbot.framework.codegen.model.tree.buildClass
import org.utbot.framework.codegen.model.tree.buildClassBody
import org.utbot.framework.codegen.model.tree.buildTestClassFile
import org.utbot.framework.codegen.model.visitor.importUtilMethodDependencies
import org.utbot.framework.plugin.api.ClassId
Expand Down Expand Up @@ -69,8 +70,8 @@ internal class CgTestClassConstructor(val context: CgContext) :
}
}

private fun constructTestClass(testClassModel: TestClassModel): CgTestClass {
return buildTestClass {
private fun constructTestClass(testClassModel: TestClassModel): CgClass {
return buildClass {
id = currentTestClass

if (currentTestClass != outerMostTestClass) {
Expand All @@ -86,9 +87,9 @@ internal class CgTestClassConstructor(val context: CgContext) :
}
}

body = buildTestClassBody {
body = buildClassBody(currentTestClass) {
for (nestedClass in testClassModel.nestedClasses) {
nestedClassRegions += CgSimpleRegion(
nestedClassRegions += CgRealNestedClassesRegion(
"Tests for ${nestedClass.classUnderTest.simpleName}",
listOf(
withNestedClassScope(nestedClass) { constructTestClass(nestedClass) }
Expand All @@ -99,11 +100,11 @@ internal class CgTestClassConstructor(val context: CgContext) :
for (testSet in testClassModel.methodTestSets) {
updateCurrentExecutable(testSet.executableId)
val currentMethodUnderTestRegions = constructTestSet(testSet) ?: continue
val executableUnderTestCluster = CgExecutableUnderTestCluster(
val executableUnderTestCluster = CgMethodsCluster(
"Test suites for executable $currentExecutable",
currentMethodUnderTestRegions
)
testMethodRegions += executableUnderTestCluster
methodRegions += executableUnderTestCluster
}

val currentTestClassDataProviderMethods = currentTestClassContext.cgDataProviderMethods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,34 @@ import org.utbot.framework.codegen.model.CodeGenerator
import org.utbot.framework.codegen.model.UtilClassKind
import org.utbot.framework.codegen.model.constructor.builtin.utUtilsClassId
import org.utbot.framework.codegen.model.tree.CgAuxiliaryClass
import org.utbot.framework.codegen.model.tree.CgRegularClassFile
import org.utbot.framework.codegen.model.tree.CgAuxiliaryNestedClassesRegion
import org.utbot.framework.codegen.model.tree.CgClassFile
import org.utbot.framework.codegen.model.tree.CgStaticsRegion
import org.utbot.framework.codegen.model.tree.CgUtilMethod
import org.utbot.framework.codegen.model.tree.buildRegularClass
import org.utbot.framework.codegen.model.tree.buildRegularClassBody
import org.utbot.framework.codegen.model.tree.buildRegularClassFile
import org.utbot.framework.codegen.model.tree.buildClass
import org.utbot.framework.codegen.model.tree.buildClassBody
import org.utbot.framework.codegen.model.tree.buildClassFile

/**
* This class is used to construct a file containing an util class UtUtils.
* The util class is constructed when the argument `generateUtilClassFile` in the [CodeGenerator] is true.
*/
internal object CgUtilClassConstructor {
fun constructUtilsClassFile(utilClassKind: UtilClassKind): CgRegularClassFile {
fun constructUtilsClassFile(utilClassKind: UtilClassKind): CgClassFile {
val utilMethodProvider = utilClassKind.utilMethodProvider
return buildRegularClassFile {
return buildClassFile {
// imports are empty, because we use fully qualified classes and static methods,
// so they will be imported once IDEA reformatting action has worked
declaredClass = buildRegularClass {
declaredClass = buildClass {
id = utUtilsClassId
body = buildRegularClassBody {
content += utilClassKind.utilClassVersionComment
content += utilClassKind.utilClassKindComment
content += utilMethodProvider.utilMethodIds.map { CgUtilMethod(it) }
content += CgAuxiliaryClass(utilMethodProvider.capturedArgumentClassId)
body = buildClassBody(utUtilsClassId) {
documentation = utilClassKind.utilClassDocumentation
staticDeclarationRegions += CgStaticsRegion("Util methods", utilMethodProvider.utilMethodIds.map { CgUtilMethod(it) })
nestedClassRegions += CgAuxiliaryNestedClassesRegion(
nestedClasses = listOf(
CgAuxiliaryClass(utilMethodProvider.capturedArgumentClassId)
)
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,71 +12,49 @@ interface CgBuilder<T : CgElement> {

// Code entities

class CgRegularClassFileBuilder : CgBuilder<CgRegularClassFile> {
class CgClassFileBuilder : CgBuilder<CgClassFile> {
val imports: MutableList<Import> = mutableListOf()
lateinit var declaredClass: CgRegularClass
lateinit var declaredClass: CgClass

override fun build() = CgRegularClassFile(imports, declaredClass)
override fun build() = CgClassFile(imports, declaredClass)
}

fun buildRegularClassFile(init: CgRegularClassFileBuilder.() -> Unit) = CgRegularClassFileBuilder().apply(init).build()
fun buildClassFile(init: CgClassFileBuilder.() -> Unit) = CgClassFileBuilder().apply(init).build()

class CgTestClassFileBuilder : CgBuilder<CgTestClassFile> {
val imports: MutableList<Import> = mutableListOf()
lateinit var declaredClass: CgTestClass
lateinit var declaredClass: CgClass
lateinit var testsGenerationReport: TestsGenerationReport

override fun build() = CgTestClassFile(imports, declaredClass, testsGenerationReport)
}

fun buildTestClassFile(init: CgTestClassFileBuilder.() -> Unit) = CgTestClassFileBuilder().apply(init).build()

class CgRegularClassBuilder : CgBuilder<CgRegularClass> {
class CgClassBuilder : CgBuilder<CgClass> {
lateinit var id: ClassId
val annotations: MutableList<CgAnnotation> = mutableListOf()
var superclass: ClassId? = null
val interfaces: MutableList<ClassId> = mutableListOf()
lateinit var body: CgRegularClassBody
var isStatic: Boolean = false
var isNested: Boolean = false
lateinit var body: CgClassBody

override fun build() = CgRegularClass(id, annotations, superclass, interfaces, body, isStatic, isNested)
override fun build() = CgClass(id, annotations, superclass, interfaces, body, isStatic, isNested)
}

fun buildRegularClass(init: CgRegularClassBuilder.() -> Unit) = CgRegularClassBuilder().apply(init).build()
fun buildClass(init: CgClassBuilder.() -> Unit) = CgClassBuilder().apply(init).build()

class CgTestClassBuilder : CgBuilder<CgTestClass> {
lateinit var id: ClassId
val annotations: MutableList<CgAnnotation> = mutableListOf()
var superclass: ClassId? = null
val interfaces: MutableList<ClassId> = mutableListOf()
var isStatic: Boolean = false
var isNested: Boolean = false
lateinit var body: CgTestClassBody

override fun build() = CgTestClass(id, annotations, superclass, interfaces, body, isStatic, isNested)
}

fun buildTestClass(init: CgTestClassBuilder.() -> Unit) = CgTestClassBuilder().apply(init).build()

class CgTestClassBodyBuilder : CgBuilder<CgTestClassBody> {
val testMethodRegions: MutableList<CgExecutableUnderTestCluster> = mutableListOf()
class CgClassBodyBuilder(val classId: ClassId) : CgBuilder<CgClassBody> {
var documentation: CgDocumentationComment? = null
val methodRegions: MutableList<CgMethodsCluster> = mutableListOf()
val staticDeclarationRegions: MutableList<CgStaticsRegion> = mutableListOf()
val nestedClassRegions: MutableList<CgRegion<CgTestClass>> = mutableListOf()
val nestedClassRegions: MutableList<CgNestedClassesRegion<*>> = mutableListOf()

override fun build() = CgTestClassBody(testMethodRegions, staticDeclarationRegions, nestedClassRegions)
override fun build() = CgClassBody(classId, documentation, methodRegions, staticDeclarationRegions, nestedClassRegions)
}

fun buildTestClassBody(init: CgTestClassBodyBuilder.() -> Unit) = CgTestClassBodyBuilder().apply(init).build()

class CgRegularClassBodyBuilder : CgBuilder<CgRegularClassBody> {
val content: MutableList<CgElement> = mutableListOf()

override fun build() = CgRegularClassBody(content)
}

fun buildRegularClassBody(init: CgRegularClassBodyBuilder.() -> Unit) = CgRegularClassBodyBuilder().apply(init).build()

fun buildClassBody(classId: ClassId, init: CgClassBodyBuilder.() -> Unit) = CgClassBodyBuilder(classId).apply(init).build()
// Methods

interface CgMethodBuilder<T : CgMethod> : CgBuilder<T> {
Expand Down
Loading

0 comments on commit e625e45

Please sign in to comment.