Skip to content
Next Next commit
Exclude coverage stats from third-party libs
  • Loading branch information
sofurihafe committed Jun 23, 2023
commit 031638b59f7d7aa6dcb8adfdeae02e1ec337ca44
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,17 @@ abstract class UtExecution(
var summary: List<DocStatement>? = null,
var testMethodName: String? = null,
var displayName: String? = null
) : UtResult()
) : UtResult() {
abstract fun copy(
stateBefore: EnvironmentModels = this.stateBefore,
stateAfter: EnvironmentModels = this.stateAfter,
result: UtExecutionResult = this.result,
coverage: Coverage? = this.coverage,
summary: List<DocStatement>? = this.summary,
testMethodName: String? = this.testMethodName,
displayName: String? = this.displayName,
): UtExecution
}

/**
* Symbolic execution.
Expand Down Expand Up @@ -176,6 +186,27 @@ class UtSymbolicExecution(

var containsMocking: Boolean = false

override fun copy(
stateBefore: EnvironmentModels,
stateAfter: EnvironmentModels,
result: UtExecutionResult,
coverage: Coverage?,
summary: List<DocStatement>?,
testMethodName: String?,
displayName: String?
): UtExecution = UtSymbolicExecution(
stateBefore = stateBefore,
stateAfter = stateAfter,
result = result,
instrumentation = instrumentation,
path = path,
fullPath = fullPath,
coverage = coverage,
summary = summary,
testMethodName = testMethodName,
displayName = displayName
)

override fun toString(): String = buildString {
append("UtSymbolicExecution(")
appendLine()
Expand All @@ -200,25 +231,28 @@ class UtSymbolicExecution(
}

fun copy(
stateAfter: EnvironmentModels,
result: UtExecutionResult,
coverage: Coverage,
stateBefore: EnvironmentModels = this.stateBefore,
stateAfter: EnvironmentModels = this.stateAfter,
result: UtExecutionResult = this.result,
coverage: Coverage? = this.coverage,
summary: List<DocStatement>? = this.summary,
testMethodName: String? = this.testMethodName,
displayName: String? = this.displayName,
instrumentation: List<UtInstrumentation> = this.instrumentation,
): UtResult {
return UtSymbolicExecution(
stateBefore,
stateAfter,
result,
instrumentation,
path,
fullPath,
coverage,
summary,
testMethodName,
displayName,
symbolicSteps,
)
}
path: MutableList<Step> = this.path,
fullPath: List<Step> = this.fullPath
): UtExecution = UtSymbolicExecution(
stateBefore = stateBefore,
stateAfter = stateAfter,
result = result,
instrumentation = instrumentation,
path = path,
fullPath = fullPath,
coverage = coverage,
summary = summary,
testMethodName = testMethodName,
displayName = displayName
)
}

/**
Expand All @@ -235,12 +269,31 @@ class UtSymbolicExecution(
*/
class UtFailedExecution(
stateBefore: EnvironmentModels,
result: UtExecutionFailure,
result: UtExecutionResult,
coverage: Coverage? = null,
summary: List<DocStatement>? = null,
testMethodName: String? = null,
displayName: String? = null
) : UtExecution(stateBefore, MissingState, result, coverage, summary, testMethodName, displayName)
) : UtExecution(stateBefore, MissingState, result, coverage, summary, testMethodName, displayName) {
override fun copy(
stateBefore: EnvironmentModels,
stateAfter: EnvironmentModels,
result: UtExecutionResult,
coverage: Coverage?,
summary: List<DocStatement>?,
testMethodName: String?,
displayName: String?
): UtExecution {
return UtFailedExecution(
stateBefore,
result,
coverage,
summary,
testMethodName,
displayName
)
}
}

open class EnvironmentModels(
val thisInstance: UtModel?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import org.utbot.framework.plugin.api.UtSpringContextModel
object SpringModelUtils {
val applicationContextClassId = ClassId("org.springframework.context.ApplicationContext")
val crudRepositoryClassId = ClassId("org.springframework.data.repository.CrudRepository")
val entityClassId = ClassId("javax.persistence.Entity")

val getBeanMethodId = MethodId(
classId = applicationContextClassId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,8 @@ private fun UtModel.calculateSize(used: MutableSet<UtModel> = mutableSetOf()): I

private fun UtStatementModel.calculateSize(used: MutableSet<UtModel> = mutableSetOf()): Int =
when (this) {
is UtDirectSetFieldModel -> 1 + fieldModel.calculateSize(used)
is UtStatementCallModel -> 1 + params.sumOf { it.calculateSize(used) }
is UtDirectSetFieldModel -> 1 + fieldModel.calculateSize(used) + instance.calculateSize(used)
is UtStatementCallModel -> 1 + params.sumOf { it.calculateSize(used) } + (instance?.calculateSize(used) ?: 0)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import org.utbot.common.measureTime
import org.utbot.common.runBlockingWithCancellationPredicate
import org.utbot.common.runIgnoringCancellationException
import org.utbot.common.trace
import org.utbot.common.PathUtil.toURL
import org.utbot.common.tryLoadClass
import org.utbot.engine.EngineController
import org.utbot.engine.Mocker
import org.utbot.engine.UtBotSymbolicEngine
Expand All @@ -27,7 +29,9 @@ import org.utbot.framework.UtSettings.utBotGenerationTimeoutInMillis
import org.utbot.framework.UtSettings.warmupConcreteExecution
import org.utbot.framework.plugin.api.utils.checkFrameworkDependencies
import org.utbot.framework.minimization.minimizeTestCase
import org.utbot.framework.plugin.api.util.SpringModelUtils.entityClassId
import org.utbot.framework.plugin.api.util.id
import org.utbot.framework.plugin.api.util.jClass
import org.utbot.framework.plugin.api.util.utContext
import org.utbot.framework.plugin.services.JdkInfo
import org.utbot.framework.util.Conflict
Expand All @@ -41,6 +45,7 @@ import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrument
import org.utbot.instrumentation.warmup
import org.utbot.taint.TaintConfigurationProvider
import java.io.File
import java.net.URLClassLoader
import java.nio.file.Path
import kotlin.coroutines.cancellation.CancellationException
import kotlin.math.min
Expand Down Expand Up @@ -117,11 +122,13 @@ open class TestCaseGenerator(
}

fun minimizeExecutions(executions: List<UtExecution>): List<UtExecution> =
if (UtSettings.testMinimizationStrategyType == TestSelectionStrategyType.DO_NOT_MINIMIZE_STRATEGY) {
executions
} else {
minimizeTestCase(executions) { it.result::class.java }
}
if (UtSettings.testMinimizationStrategyType == TestSelectionStrategyType.DO_NOT_MINIMIZE_STRATEGY) executions
else minimizeTestCase(
when (applicationContext) {
is SpringApplicationContext -> executions.filterCoveredInstructions()
else -> executions
}
) { it.result::class.java }

@Throws(CancellationException::class)
fun generateAsync(
Expand Down Expand Up @@ -351,6 +358,79 @@ open class TestCaseGenerator(
}
}

private fun List<UtExecution>.filterCoveredInstructions(): List<UtExecution> {
// Do nothing when we launched not from IDEA
if (buildDirs.isEmpty()) return this

val annotationsToIgnore =
listOfNotNull(utContext.classLoader.tryLoadClass(entityClassId.name))

val urls = buildDirs.map { it.toURL() }.toTypedArray()

// We pass null as a parent here because we do not want to include standard java libs
val buildDirsClassLoader = URLClassLoader(urls, null)

val isClassOnUserClasspathCache = mutableMapOf<String, Boolean>()
fun isClassOnUserClasspath(fullyQualifiedName: String): Boolean =
isClassOnUserClasspathCache.getOrPut(fullyQualifiedName) {
buildDirsClassLoader.tryLoadClass(fullyQualifiedName) != null
}

// Here we filter out instructions from third-party libraries
// Also, we filter out instructions that operate
// in classes marked with annotation from [annotationsToIgnore]
// and in standard java libs
return this.map { execution ->
val coverage = execution.coverage ?: return@map execution

val filteredCoveredInstructions =
coverage.coveredInstructions
.filter { instruction ->
val instrClassName = instruction.className.replace('/', '.')

// We do not want to filter out instructions that are in class under test
if (execution.stateAfter.thisInstance?.classId?.jClass?.canonicalName == instrClassName ||
execution.stateBefore.thisInstance?.classId?.jClass?.canonicalName == instrClassName
) {
return@filter true
}

if (!isClassOnUserClasspath(instrClassName)) return@filter false

// We do not want to take instructions in classes marked as @Entity
if (annotationsToIgnore.isNotEmpty()) {
val hasEntityAnnotation =
utContext
.classLoader
.loadClass(instrClassName)
.annotations
.any { annotation ->
annotationsToIgnore.any { annotationToIgnore ->
annotationToIgnore.isInstance(annotation)
}
}

if (hasEntityAnnotation) return@filter false
}

return@filter true
}
.ifEmpty {
coverage.coveredInstructions
.also {
logger.warn("Execution covered instruction list became empty. Proceeding with not filtered instruction list.")
}
}

execution.copy(
coverage = Coverage(
coveredInstructions = filteredCoveredInstructions,
instructionsCount = coverage.instructionsCount,
missedInstructions = coverage.missedInstructions
)
)
}
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
Instrumenter.adapter = RdInstrumenter(realProtocol.rdInstrumenterAdapter)
val applicationContext: ApplicationContext = kryoHelper.readObject(params.applicationContext)

testGenerator = TestCaseGenerator(buildDirs = params.buildDir.map { Paths.get(it) },
testGenerator = TestCaseGenerator(
buildDirs = params.buildDir.map { Paths.get(it) },
classpath = params.classpath,
dependencyPaths = params.dependencyPaths,
jdkInfo = JdkInfo(Paths.get(params.jdkInfo.path), params.jdkInfo.version),
Expand All @@ -115,7 +116,8 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
runBlocking {
model.isCancelled.startSuspending(Unit)
}
})
}
)
}
watchdog.measureTimeForActiveCall(generate, "Generating tests") { params ->
val methods: List<ExecutableId> = kryoHelper.readObject(params.methods)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,29 @@ class UtFuzzedExecution(
val staticFields: Set<FieldId>
get() = stateBefore.statics.keys // TODO: should we keep it for the Fuzzed Execution?

override fun copy(
stateBefore: EnvironmentModels,
stateAfter: EnvironmentModels,
result: UtExecutionResult,
coverage: Coverage?,
summary: List<DocStatement>?,
testMethodName: String?,
displayName: String?
): UtExecution {
return UtFuzzedExecution(
stateBefore,
stateAfter,
result,
coverage,
summary,
testMethodName,
displayName,
fuzzingValues = fuzzingValues,
fuzzedMethodDescription = fuzzedMethodDescription,
instrumentation = instrumentation,
)
}

override fun toString(): String = buildString {
append("UtFuzzedExecution(")
appendLine()
Expand All @@ -51,4 +74,28 @@ class UtFuzzedExecution(
append(result)
append(")")
}

fun copy(
stateBefore: EnvironmentModels = this.stateBefore,
stateAfter: EnvironmentModels = this.stateAfter,
result: UtExecutionResult = this.result,
coverage: Coverage? = this.coverage,
summary: List<DocStatement>? = this.summary,
testMethodName: String? = this.testMethodName,
displayName: String? = this.displayName,
fuzzingValues: List<FuzzedValue>? = this.fuzzingValues,
fuzzedMethodDescription: FuzzedMethodDescription? = this.fuzzedMethodDescription,
instrumentation: List<UtInstrumentation> = this.instrumentation,
): UtExecution = UtFuzzedExecution(
stateBefore = stateBefore,
stateAfter = stateAfter,
result = result,
coverage = coverage,
summary = summary,
testMethodName = testMethodName,
displayName = displayName,
fuzzingValues = fuzzingValues,
fuzzedMethodDescription = fuzzedMethodDescription,
instrumentation = instrumentation,
)
}
22 changes: 18 additions & 4 deletions utbot-js/src/main/kotlin/framework/api/js/JsUtFuzzedExecution.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
package framework.api.js

import org.utbot.framework.plugin.api.EnvironmentModels
import org.utbot.framework.plugin.api.UtExecution
import org.utbot.framework.plugin.api.UtExecutionResult
import org.utbot.framework.plugin.api.*

class JsUtFuzzedExecution(
stateBefore: EnvironmentModels,
stateAfter: EnvironmentModels,
result: UtExecutionResult
) : UtExecution(stateBefore, stateAfter, result, null, null, null, null)
) : UtExecution(stateBefore, stateAfter, result, null, null, null, null) {
override fun copy(
stateBefore: EnvironmentModels,
stateAfter: EnvironmentModels,
result: UtExecutionResult,
coverage: Coverage?,
summary: List<DocStatement>?,
testMethodName: String?,
displayName: String?
): UtExecution {
return JsUtFuzzedExecution(
stateBefore = stateBefore,
stateAfter = stateAfter,
result = result
)
}
}
Loading