Skip to content
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

Cg util class #689

Merged
merged 30 commits into from
Sep 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
21393ab
Move all util methods into a separate module
ArsenHD Jul 14, 2022
e613702
Split util methods and data providers into two separate regions.
ArsenHD Jul 21, 2022
d9ff922
Create CgElements for regular classes (not just test classes)
ArsenHD Aug 5, 2022
06ed384
Update util method providers
ArsenHD Aug 5, 2022
d7d0df9
Support util class generation in plugin
ArsenHD Aug 8, 2022
9c501c1
Simplify some util methods checks
ArsenHD Aug 10, 2022
c0e1161
Collect required util methods regardless of util method provider
ArsenHD Aug 10, 2022
c2ba7a0
Put information about whether mocks were used into CodeGenerationResult
ArsenHD Aug 10, 2022
3956238
Carefully consider different cases when generating an util class
ArsenHD Aug 10, 2022
54f8a86
Update util methods collection in code generation taking into account…
ArsenHD Aug 10, 2022
1b8212b
Simplify util method kinds
ArsenHD Aug 11, 2022
51a09f7
Generate tests with and without separate util class when running all …
ArsenHD Aug 11, 2022
227c971
Check multiple test source roots for util class to avoid util class d…
ArsenHD Aug 12, 2022
233f24d
Support isStatic and isNested flags in CgRegularClass and its builder
ArsenHD Aug 12, 2022
3e6811d
Drop unused imports and property
ArsenHD Aug 12, 2022
ac193b9
Fix wrong property name
ArsenHD Aug 12, 2022
29b2d29
Use outermost test class for test class util method provider and rend…
ArsenHD Aug 12, 2022
721e983
Do render nested classes in Java and Kotlin
ArsenHD Aug 12, 2022
c0d48b3
Fix obtaining exception types of util methods
ArsenHD Aug 12, 2022
39e77be
Remove utbot-codegen-utils library, because now we will work with a s…
ArsenHD Aug 12, 2022
61c2b42
Fix code generator pipeline for cases when util class is generated se…
ArsenHD Aug 12, 2022
abda14d
TODO: uncommit, there are some unfinished todo's
ArsenHD Aug 17, 2022
65b34da
Obtain version comment from utils class, not file
ArsenHD Aug 18, 2022
286e54c
Remove unnecеssary 'open' modifier
ArsenHD Aug 22, 2022
62c76c6
Remove unnecessary file for class Patterns
ArsenHD Aug 22, 2022
2b51d74
Add documentation for CgRegularClass
ArsenHD Aug 22, 2022
694e018
Add documentation for CgRegularClass
ArsenHD Aug 22, 2022
b9c740d
Store version of util class in a comment and use it to decide whether…
ArsenHD Aug 23, 2022
d463ae2
Store kind of util class in a comment to obtain the kind of an existi…
ArsenHD Aug 23, 2022
1c01865
Fix incorrect import
ArsenHD Aug 31, 2022
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 @@ -6,24 +6,32 @@ import org.utbot.framework.codegen.ParametrizedTestSource
import org.utbot.framework.codegen.RuntimeExceptionTestsBehaviour
import org.utbot.framework.codegen.StaticsMocking
import org.utbot.framework.codegen.TestFramework
import org.utbot.framework.codegen.model.constructor.builtin.TestClassUtilMethodProvider
import org.utbot.framework.codegen.model.constructor.builtin.UtilClassFileMethodProvider
import org.utbot.framework.codegen.model.constructor.CgMethodTestSet
import org.utbot.framework.codegen.model.constructor.context.CgContext
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.CgTestClassFile
import org.utbot.framework.codegen.model.tree.AbstractCgClassFile
import org.utbot.framework.codegen.model.tree.CgRegularClassFile
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

class CodeGenerator(
private val classUnderTest: ClassId,
paramNames: MutableMap<ExecutableId, List<String>> = mutableMapOf(),
generateUtilClassFile: Boolean = false,
testFramework: TestFramework = TestFramework.defaultItem,
mockFramework: MockFramework? = MockFramework.defaultItem,
mockFramework: MockFramework = MockFramework.defaultItem,
staticsMocking: StaticsMocking = StaticsMocking.defaultItem,
forceStaticMocking: ForceStaticMocking = ForceStaticMocking.defaultItem,
generateWarningsForStaticMocking: Boolean = true,
Expand All @@ -36,9 +44,10 @@ class CodeGenerator(
) {
private var context: CgContext = CgContext(
classUnderTest = classUnderTest,
generateUtilClassFile = generateUtilClassFile,
paramNames = paramNames,
testFramework = testFramework,
mockFramework = mockFramework ?: MockFramework.MOCKITO,
mockFramework = mockFramework,
codegenLanguage = codegenLanguage,
parametrizedTestSource = parameterizedTestSource,
staticsMocking = staticsMocking,
Expand All @@ -58,19 +67,23 @@ class CodeGenerator(
fun generateAsStringWithTestReport(
testSets: Collection<UtMethodTestSet>,
testClassCustomName: String? = null,
): TestsCodeWithTestReport {
): CodeGeneratorResult {
val cgTestSets = testSets.map { CgMethodTestSet(it) }.toList()
return generateAsStringWithTestReport(cgTestSets, testClassCustomName)
}

private fun generateAsStringWithTestReport(
cgTestSets: List<CgMethodTestSet>,
testClassCustomName: String? = null,
): TestsCodeWithTestReport = withCustomContext(testClassCustomName) {
): CodeGeneratorResult = withCustomContext(testClassCustomName) {
context.withTestClassFileScope {
val testClassModel = TestClassModel.fromTestSets(classUnderTest, cgTestSets)
val testClassFile = CgTestClassConstructor(context).construct(testClassModel)
TestsCodeWithTestReport(renderClassFile(testClassFile), testClassFile.testsGenerationReport)
CodeGeneratorResult(
generatedCode = renderClassFile(testClassFile),
utilClassKind = UtilClassKind.fromCgContextOrNull(context),
testsGenerationReport = testClassFile.testsGenerationReport
)
}
}

Expand All @@ -92,12 +105,153 @@ class CodeGenerator(
}
}

private fun renderClassFile(file: CgTestClassFile): String {
private fun renderClassFile(file: AbstractCgClassFile<*>): String {
val renderer = CgAbstractRenderer.makeRenderer(context)
file.accept(renderer)
return renderer.toString()
}
}

data class TestsCodeWithTestReport(val generatedCode: String, val testsGenerationReport: TestsGenerationReport)
/**
* @property generatedCode the source code of the test class
* @property utilClassKind the kind of util class if it is required, otherwise - null
* @property testsGenerationReport some info about test generation process
*/
data class CodeGeneratorResult(
val generatedCode: String,
// null if no util class needed, e.g. when we are generating utils directly into test class
val utilClassKind: UtilClassKind?,
val testsGenerationReport: TestsGenerationReport,
)

/**
* A kind of util class. See the description of each kind at their respective classes.
* @property utilMethodProvider a [UtilClassFileMethodProvider] containing information about
* utilities that come from a separately generated UtUtils class
* (as opposed to utils that are declared directly in the test class, for example).
* @property mockFrameworkUsed a flag indicating if a mock framework was used.
* For detailed description see [CgContextOwner.mockFrameworkUsed].
* @property mockFramework a framework used to create mocks
* @property priority when we generate multiple test classes, they can require different [UtilClassKind].
* We will generate an util class corresponding to the kind with the greatest priority.
* For example, one test class may not use mocks, but the other one does.
* Then we will generate an util class with mocks, because it has a greater priority (see [UtUtilsWithMockito]).
*/
sealed class UtilClassKind(
internal val utilMethodProvider: UtilClassFileMethodProvider,
internal val mockFrameworkUsed: Boolean,
internal val mockFramework: MockFramework = MockFramework.MOCKITO,
private val priority: Int
) : Comparable<UtilClassKind> {

/**
* 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].
*
* This comment is needed when the plugin decides whether to overwrite an existing util class or not.
* When making that decision, it is important to determine if the existing class uses mocks or not,
* and this comment will help do that.
*/
abstract val utilClassKindCommentText: String

/**
* A kind of regular UtUtils class. "Regular" here means that this class does not use a mock framework.
*/
object RegularUtUtils : UtilClassKind(UtilClassFileMethodProvider, mockFrameworkUsed = false, priority = 0) {
override val utilClassKindCommentText: String
get() = "This is a regular UtUtils class (without mock framework usage)"
}

/**
* A kind of UtUtils class that uses a mock framework. At the moment the framework is Mockito.
*/
object UtUtilsWithMockito : UtilClassKind(UtilClassFileMethodProvider, mockFrameworkUsed = true, priority = 1) {
override val utilClassKindCommentText: String
get() = "This is UtUtils class with Mockito support"
}

override fun compareTo(other: UtilClassKind): Int {
return priority.compareTo(other.priority)
}

/**
* Construct an util class file as a [CgRegularClassFile] and render it.
* @return the text of the generated util class file.
*/
fun getUtilClassText(codegenLanguage: CodegenLanguage): String {
val utilClassFile = CgUtilClassConstructor.constructUtilsClassFile(this)
val renderer = CgAbstractRenderer.makeRenderer(this, codegenLanguage)
utilClassFile.accept(renderer)
return renderer.toString()
}

companion object {

/**
* Class UtUtils will contain a comment specifying the version of this util class
* (if we ever change util methods, then util class will be different, hence the update of its version).
* This is a prefix that will go before the version in the comment.
*/
const val UTIL_CLASS_VERSION_COMMENT_PREFIX = "UtUtils class version: "

fun utilClassKindByCommentOrNull(comment: String): UtilClassKind? {
return when (comment) {
RegularUtUtils.utilClassKindCommentText -> RegularUtUtils
UtUtilsWithMockito.utilClassKindCommentText -> UtUtilsWithMockito
else -> null
}
}

/**
* Check if an util class is required, and if so, what kind.
* @return `null` if [CgContext.utilMethodProvider] is not [UtilClassFileMethodProvider],
* because it means that util methods will be taken from some other provider (e.g. [TestClassUtilMethodProvider]).
*/
internal fun fromCgContextOrNull(context: CgContext): UtilClassKind? {
if (context.requiredUtilMethods.isEmpty()) return null
if (!context.mockFrameworkUsed) {
return RegularUtUtils
}
return when (context.mockFramework) {
MockFramework.MOCKITO -> UtUtilsWithMockito
// in case we will add any other mock frameworks, newer Kotlin compiler versions
// will report a non-exhaustive 'when', so we will not forget to support them here as well
}
}

const val UT_UTILS_PACKAGE_NAME = "org.utbot.runtime.utils"
const val UT_UTILS_CLASS_NAME = "UtUtils"
const val PACKAGE_DELIMITER = "."

/**
* List of package names of UtUtils class.
* See whole package name at [UT_UTILS_PACKAGE_NAME].
*/
val utilsPackages: List<String>
get() = UT_UTILS_PACKAGE_NAME.split(PACKAGE_DELIMITER)
}
}
Loading