Skip to content

Try to fix memory leak in CgComponents #1057

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 4 commits into from
Oct 4, 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 @@ -215,7 +215,10 @@ internal interface CgContextOwner {

var statesCache: EnvironmentFieldStateCache

var allExecutions: List<UtExecution>
/**
* Result models required to create generic execution in parametrized tests.
*/
var successfulExecutionsModels: List<UtModel>

fun block(init: () -> Unit): Block {
val prevBlock = currentBlock
Expand Down Expand Up @@ -463,7 +466,7 @@ internal data class CgContext(
) : CgContextOwner {
override lateinit var statesCache: EnvironmentFieldStateCache
override lateinit var actual: CgVariable
override lateinit var allExecutions: List<UtExecution>
override lateinit var successfulExecutionsModels: List<UtModel>

/**
* This property cannot be accessed outside of test class file scope
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import org.utbot.framework.codegen.model.constructor.builtin.setAccessible
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.CgCallableAccessManagerImpl.FieldAccessorSuitability.*
import org.utbot.framework.codegen.model.constructor.util.CgComponents
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getStatementConstructorBy
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getVariableConstructorBy
import org.utbot.framework.codegen.model.constructor.util.getAmbiguousOverloadsOf
import org.utbot.framework.codegen.model.constructor.util.importIfNeeded
import org.utbot.framework.codegen.model.constructor.util.isUtil
Expand Down Expand Up @@ -85,9 +86,9 @@ interface CgCallableAccessManager {
internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableAccessManager,
CgContextOwner by context {

private val statementConstructor by lazy { CgComponents.getStatementConstructorBy(context) }
private val statementConstructor by lazy { getStatementConstructorBy(context) }

private val variableConstructor by lazy { CgComponents.getVariableConstructorBy(context) }
private val variableConstructor by lazy { getVariableConstructorBy(context) }

override operator fun CgExpression?.get(methodId: MethodId): CgIncompleteMethodCall =
CgIncompleteMethodCall(methodId, this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import org.utbot.framework.codegen.model.constructor.builtin.forName
import org.utbot.framework.codegen.model.constructor.builtin.getArrayElement
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.util.CgComponents
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getCallableAccessManagerBy
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getStatementConstructorBy
import org.utbot.framework.codegen.model.constructor.util.CgFieldState
import org.utbot.framework.codegen.model.constructor.util.CgStatementConstructor
import org.utbot.framework.codegen.model.constructor.util.FieldStateCache
Expand Down Expand Up @@ -44,8 +45,8 @@ internal interface CgFieldStateManager {
internal class CgFieldStateManagerImpl(val context: CgContext)
: CgContextOwner by context,
CgFieldStateManager,
CgCallableAccessManager by CgComponents.getCallableAccessManagerBy(context),
CgStatementConstructor by CgComponents.getStatementConstructorBy(context) {
CgCallableAccessManager by getCallableAccessManagerBy(context),
CgStatementConstructor by getStatementConstructorBy(context) {

override fun rememberInitialEnvironmentState(info: StateModificationInfo) {
rememberThisInstanceState(info, FieldState.INITIAL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ import org.utbot.framework.codegen.model.constructor.builtin.invoke
import org.utbot.framework.codegen.model.constructor.builtin.newInstance
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.util.CgComponents
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getCallableAccessManagerBy
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getMockFrameworkManagerBy
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getNameGeneratorBy
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getStatementConstructorBy
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getTestFrameworkManagerBy
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getVariableConstructorBy
import org.utbot.framework.codegen.model.constructor.util.CgStatementConstructor
import org.utbot.framework.codegen.model.constructor.util.EnvironmentFieldStateCache
import org.utbot.framework.codegen.model.constructor.util.FieldStateCache
Expand Down Expand Up @@ -148,15 +153,14 @@ import java.lang.reflect.ParameterizedType
private const val DEEP_EQUALS_MAX_DEPTH = 5 // TODO move it to plugin settings?

internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by context,
CgFieldStateManager by CgComponents.getFieldStateManagerBy(context),
CgCallableAccessManager by CgComponents.getCallableAccessManagerBy(context),
CgStatementConstructor by CgComponents.getStatementConstructorBy(context) {
CgCallableAccessManager by getCallableAccessManagerBy(context),
CgStatementConstructor by getStatementConstructorBy(context) {

private val nameGenerator = CgComponents.getNameGeneratorBy(context)
private val testFrameworkManager = CgComponents.getTestFrameworkManagerBy(context)
private val nameGenerator = getNameGeneratorBy(context)
private val testFrameworkManager = getTestFrameworkManagerBy(context)

private val variableConstructor = CgComponents.getVariableConstructorBy(context)
private val mockFrameworkManager = CgComponents.getMockFrameworkManagerBy(context)
private val variableConstructor = getVariableConstructorBy(context)
private val mockFrameworkManager = getMockFrameworkManagerBy(context)

private val floatDelta: Float = 1e-6f
private val doubleDelta = 1e-6
Expand Down Expand Up @@ -937,13 +941,6 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
}

private fun collectExecutionsResultFields() {
val successfulExecutionsModels = allExecutions
.filter {
it.result is UtExecutionSuccess
}.map {
(it.result as UtExecutionSuccess).model
}

for (model in successfulExecutionsModels) {
when (model) {
is UtCompositeModel -> {
Expand Down Expand Up @@ -1275,6 +1272,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
rememberInitialStaticFields(statics)
val stateAnalyzer = ExecutionStateAnalyzer(execution)
val modificationInfo = stateAnalyzer.findModifiedFields()
val fieldStateManager = CgFieldStateManagerImpl(context)
// TODO: move such methods to another class and leave only 2 public methods: remember initial and final states
val mainBody = {
substituteStaticFields(statics)
Expand All @@ -1288,10 +1286,10 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
val name = paramNames[executableId]?.get(index)
methodArguments += variableConstructor.getOrCreateVariable(param, name)
}
rememberInitialEnvironmentState(modificationInfo)
fieldStateManager.rememberInitialEnvironmentState(modificationInfo)
recordActualResult()
generateResultAssertions()
rememberFinalEnvironmentState(modificationInfo)
fieldStateManager.rememberFinalEnvironmentState(modificationInfo)
generateFieldStateAssertions()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
package org.utbot.framework.codegen.model.constructor.tree

import org.utbot.common.appendHtmlLine
import org.utbot.framework.codegen.Junit4
import org.utbot.framework.codegen.Junit5
import org.utbot.framework.codegen.ParametrizedTestSource
import org.utbot.framework.codegen.TestNg
import org.utbot.framework.codegen.model.constructor.CgMethodTestSet
import org.utbot.framework.codegen.model.constructor.TestClassModel
import org.utbot.framework.codegen.model.constructor.builtin.TestClassUtilMethodProvider
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.util.CgComponents
import org.utbot.framework.codegen.model.constructor.name.CgNameGenerator
import org.utbot.framework.codegen.model.constructor.name.CgNameGeneratorImpl
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.clearContextRelatedStorage
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getMethodConstructorBy
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getNameGeneratorBy
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getStatementConstructorBy
import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getTestFrameworkManagerBy
import org.utbot.framework.codegen.model.constructor.util.CgStatementConstructor
import org.utbot.framework.codegen.model.tree.CgMethod
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.CgMethod
import org.utbot.framework.codegen.model.tree.CgParameterDeclaration
import org.utbot.framework.codegen.model.tree.CgRegion
import org.utbot.framework.codegen.model.tree.CgSimpleRegion
Expand All @@ -18,32 +29,32 @@ import org.utbot.framework.codegen.model.tree.CgTestClass
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.CgTestMethodType.*
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.buildTestClassFile
import org.utbot.framework.codegen.model.visitor.importUtilMethodDependencies
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.api.MethodId
import org.utbot.framework.plugin.api.UtExecutionSuccess
import org.utbot.framework.plugin.api.UtMethodTestSet
import org.utbot.framework.codegen.model.constructor.TestClassModel
import org.utbot.framework.codegen.model.tree.CgAuxiliaryClass
import org.utbot.framework.codegen.model.tree.CgUtilEntity
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.util.description
import org.utbot.framework.plugin.api.util.humanReadableName
import org.utbot.framework.plugin.api.util.kClass
import kotlin.reflect.KClass

internal class CgTestClassConstructor(val context: CgContext) :
CgContextOwner by context,
CgStatementConstructor by CgComponents.getStatementConstructorBy(context) {
CgStatementConstructor by getStatementConstructorBy(context) {

init {
clearContextRelatedStorage()
}

private val methodConstructor = CgComponents.getMethodConstructorBy(context)
private val nameGenerator = CgComponents.getNameGeneratorBy(context)
private val testFrameworkManager = CgComponents.getTestFrameworkManagerBy(context)
private val methodConstructor = getMethodConstructorBy(context)
private val nameGenerator = getNameGeneratorBy(context)
private val testFrameworkManager = getTestFrameworkManagerBy(context)

private val testsGenerationReport: TestsGenerationReport = TestsGenerationReport()

Expand Down Expand Up @@ -124,7 +135,10 @@ internal class CgTestClassConstructor(val context: CgContext) :
return null
}

allExecutions = testSet.executions
successfulExecutionsModels = testSet
.executions
.filter { it.result is UtExecutionSuccess }
.map { (it.result as UtExecutionSuccess).model }

val (methodUnderTest, _, _, clustersInfo) = testSet
val regions = mutableListOf<CgRegion<CgMethod>>()
Expand Down Expand Up @@ -275,106 +289,45 @@ internal class CgTestClassConstructor(val context: CgContext) :
*/
private val CgMethodTestSet.allErrors: Map<String, Int>
get() = errors + codeGenerationErrors.getOrDefault(this, mapOf())
}

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

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

val initialWarnings: MutableList<() -> String> = mutableListOf()
val hasWarnings: Boolean
get() = initialWarnings.isNotEmpty()

val detailedStatistics: String
get() = buildString {
appendHtmlLine("Class: ${classUnderTest.qualifiedName}")
val testMethodsStatistic = executables.map { it.countTestMethods() }
val errors = executables.map { it.countErrors() }
val overallErrors = errors.sum()

appendHtmlLine("Successful test methods: ${testMethodsStatistic.sumBy { it.successful }}")
appendHtmlLine(
"Failing because of unexpected exception test methods: ${testMethodsStatistic.sumBy { it.failing }}"
)
appendHtmlLine(
"Failing because of exceeding timeout test methods: ${testMethodsStatistic.sumBy { it.timeout }}"
)
appendHtmlLine(
"Failing because of possible JVM crash test methods: ${testMethodsStatistic.sumBy { it.crashes }}"
)
appendHtmlLine("Not generated because of internal errors test methods: $overallErrors")
internal object CgComponents {
/**
* Clears all stored data for current [CgContext].
* As far as context is created per class under test,
* no related data is required after it's processing.
*/
fun clearContextRelatedStorage() {
nameGenerators.clear()
statementConstructors.clear()
callableAccessManagers.clear()
testFrameworkManagers.clear()
mockFrameworkManagers.clear()
variableConstructors.clear()
methodConstructors.clear()
}

fun addMethodErrors(testSet: CgMethodTestSet, errors: Map<String, Int>) {
this.errors[testSet.executableId] = errors
}
private val nameGenerators: MutableMap<CgContext, CgNameGenerator> = mutableMapOf()
private val statementConstructors: MutableMap<CgContext, CgStatementConstructor> = mutableMapOf()
private val callableAccessManagers: MutableMap<CgContext, CgCallableAccessManager> = mutableMapOf()
private val testFrameworkManagers: MutableMap<CgContext, TestFrameworkManager> = mutableMapOf()
private val mockFrameworkManagers: MutableMap<CgContext, MockFrameworkManager> = mutableMapOf()

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

testMethods.forEach {
when (it.type) {
SUCCESSFUL -> updateExecutions(it, successfulExecutions)
FAILING -> updateExecutions(it, failedExecutions)
TIMEOUT -> updateExecutions(it, timeoutExecutions)
CRASH -> updateExecutions(it, crashExecutions)
PARAMETRIZED -> {
// Parametrized tests are not supported in the tests report yet
// TODO JIRA:1507
}
}
}
}
}

fun toString(isShort: Boolean): String = buildString {
appendHtmlLine("Target: ${classUnderTest.qualifiedName}")
if (initialWarnings.isNotEmpty()) {
initialWarnings.forEach { appendHtmlLine(it()) }
appendHtmlLine()
}

val testMethodsStatistic = executables.map { it.countTestMethods() }
val overallTestMethods = testMethodsStatistic.sumBy { it.count }
private val variableConstructors: MutableMap<CgContext, CgVariableConstructor> = mutableMapOf()
private val methodConstructors: MutableMap<CgContext, CgMethodConstructor> = mutableMapOf()

appendHtmlLine("Overall test methods: $overallTestMethods")
fun getNameGeneratorBy(context: CgContext) = nameGenerators.getOrPut(context) { CgNameGeneratorImpl(context) }
fun getCallableAccessManagerBy(context: CgContext) = callableAccessManagers.getOrPut(context) { CgCallableAccessManagerImpl(context) }
fun getStatementConstructorBy(context: CgContext) = statementConstructors.getOrPut(context) { CgStatementConstructorImpl(context) }

if (!isShort) {
appendHtmlLine(detailedStatistics)
fun getTestFrameworkManagerBy(context: CgContext) = when (context.testFramework) {
is Junit4 -> testFrameworkManagers.getOrPut(context) { Junit4Manager(context) }
is Junit5 -> testFrameworkManagers.getOrPut(context) { Junit5Manager(context) }
is TestNg -> testFrameworkManagers.getOrPut(context) { TestNgManager(context) }
}
}

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

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

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

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

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

private data class TestMethodStatistic(val successful: Int, val failing: Int, val timeout: Int, val crashes: Int) {
val count: Int = successful + failing + timeout + crashes
fun getMockFrameworkManagerBy(context: CgContext) = mockFrameworkManagers.getOrPut(context) { MockFrameworkManager(context) }
fun getVariableConstructorBy(context: CgContext) = variableConstructors.getOrPut(context) { CgVariableConstructor(context) }
fun getMethodConstructorBy(context: CgContext) = methodConstructors.getOrPut(context) { CgMethodConstructor(context) }
}
}

Loading