diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index 8303a67aa6..27bf8692e1 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -480,6 +480,11 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS */ var useBytecodeTransformation by getBooleanProperty(false) + /** + * Type of trace instrumentation. + */ + var traceInstrumentationType by getEnumProperty(TraceInstrumentationType.INSTRUCTION) + /** * Limit for number of generated tests per method (in each region) */ @@ -756,3 +761,16 @@ enum class ExploreThrowableDepth { */ EXPLORE_ALL_STATEMENTS } + +enum class TraceInstrumentationType { + + /** + * Insert probes before each bytecode instruction. + */ + INSTRUCTION, + + /** + * Insert probes into the [control flow graph](https://www.eclemma.org/jacoco/trunk/doc/flow.html). + */ + BRANCH +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/coverage/CoverageCalculator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/coverage/CoverageCalculator.kt index c662da9b31..4bc34a4cc3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/coverage/CoverageCalculator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/coverage/CoverageCalculator.kt @@ -1,19 +1,19 @@ package org.utbot.framework.coverage +import kotlinx.coroutines.runBlocking import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.UtValueExecution import org.utbot.framework.plugin.api.util.jClass import org.utbot.instrumentation.ConcreteExecutor import org.utbot.instrumentation.instrumentation.coverage.CoverageInfo -import org.utbot.instrumentation.instrumentation.coverage.CoverageInstrumentation +import org.utbot.instrumentation.instrumentation.coverage.InstructionCoverageInstrumentation import org.utbot.instrumentation.instrumentation.coverage.collectCoverage import org.utbot.instrumentation.util.StaticEnvironment -import kotlinx.coroutines.runBlocking fun methodCoverage(executable: ExecutableId, executions: List>, classpath: String): Coverage { val methodSignature = executable.signature val classId = executable.classId - return ConcreteExecutor(CoverageInstrumentation.Factory, classpath).let { executor -> + return ConcreteExecutor(InstructionCoverageInstrumentation.Factory, classpath).let { executor -> for (execution in executions) { val args = execution.stateBefore.params.map { it.value }.toMutableList() val caller = execution.stateBefore.caller diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestBranchCoverageInstrumentation.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestBranchCoverageInstrumentation.kt new file mode 100644 index 0000000000..03b5602f4b --- /dev/null +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestBranchCoverageInstrumentation.kt @@ -0,0 +1,93 @@ +package org.utbot.examples + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.utbot.examples.samples.ExampleClass +import org.utbot.examples.samples.et.ClassSimpleCatch +import org.utbot.examples.statics.substitution.StaticSubstitution +import org.utbot.examples.statics.substitution.StaticSubstitutionExamples +import org.utbot.framework.plugin.api.util.fieldId +import org.utbot.framework.plugin.api.util.signature +import org.utbot.instrumentation.execute +import org.utbot.instrumentation.instrumentation.coverage.BranchCoverageInstrumentation +import org.utbot.instrumentation.instrumentation.coverage.collectCoverage +import org.utbot.instrumentation.util.StaticEnvironment +import org.utbot.instrumentation.withInstrumentation + +class TestBranchCoverageInstrumentation { + lateinit var utContext: AutoCloseable + + @Test + fun testIfBranches() { + withInstrumentation( + BranchCoverageInstrumentation.Factory, + ExampleClass::class.java.protectionDomain.codeSource.location.path + ) { executor -> + val testObject = ExampleClass() + + executor.execute(ExampleClass::bar, arrayOf(testObject, 2)) + val coverageInfo1 = executor.collectCoverage(ExampleClass::class.java) + + assertEquals(2, coverageInfo1.visitedInstrs.size) + assertEquals(1..3, coverageInfo1.methodToInstrRange[ExampleClass::bar.signature]) + + executor.execute(ExampleClass::bar, arrayOf(testObject, 0)) + val coverageInfo2 = executor.collectCoverage(ExampleClass::class.java) + + assertEquals(2, coverageInfo2.visitedInstrs.size) + assertEquals(1..3, coverageInfo2.methodToInstrRange[ExampleClass::bar.signature]) + } + } + + @Test + fun testTryCatch() { + withInstrumentation( + BranchCoverageInstrumentation.Factory, + ClassSimpleCatch::class.java.protectionDomain.codeSource.location.path + ) { executor -> + executor.execute(ClassSimpleCatch::A_catches, emptyArray()) + val coverageInfo1 = executor.collectCoverage(ClassSimpleCatch::class.java) + + assertEquals(2, coverageInfo1.visitedInstrs.size) + assertEquals(3..5, coverageInfo1.methodToInstrRange[ClassSimpleCatch::A_catches.signature]) + + executor.execute(ClassSimpleCatch::A_catchesWrongException, emptyArray()) + val coverageInfo2 = executor.collectCoverage(ClassSimpleCatch::class.java) + + assertEquals(0, coverageInfo2.visitedInstrs.size) + assertEquals(7..9, coverageInfo2.methodToInstrRange[ClassSimpleCatch::A_catchesWrongException.signature]) + } + } + + @Test + fun testTernaryOperator() { + withInstrumentation( + BranchCoverageInstrumentation.Factory, + StaticSubstitutionExamples::class.java.protectionDomain.codeSource.location.path + ) { executor -> + val testObject = StaticSubstitutionExamples() + + val emptyStaticEnvironment = StaticEnvironment() + + val res1 = executor.execute( + StaticSubstitutionExamples::lessThanZero, + arrayOf(testObject), + parameters = emptyStaticEnvironment + ) + + val staticEnvironment = StaticEnvironment( + StaticSubstitution::mutableValue.fieldId to -1 + ) + val res2 = executor.execute( + StaticSubstitutionExamples::lessThanZero, + arrayOf(testObject), + parameters = staticEnvironment + ) + val coverageInfo = executor.collectCoverage(StaticSubstitutionExamples::class.java) + + assertEquals(res1.getOrNull(), 5) + assertEquals(res2.getOrNull(), 0) + assertEquals(coverageInfo.visitedInstrs, (1..3).toList()) + } + } +} \ No newline at end of file diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestConstructors.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestConstructors.kt index fea5acdb17..fecbe02f8f 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestConstructors.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestConstructors.kt @@ -6,7 +6,7 @@ import org.utbot.framework.plugin.api.util.signature import org.utbot.instrumentation.ConcreteExecutor import org.utbot.instrumentation.execute import org.utbot.instrumentation.instrumentation.InvokeInstrumentation -import org.utbot.instrumentation.instrumentation.coverage.CoverageInstrumentation +import org.utbot.instrumentation.instrumentation.coverage.InstructionCoverageInstrumentation import org.utbot.instrumentation.instrumentation.coverage.collectCoverage import org.utbot.instrumentation.instrumentation.et.ExecutionTraceInstrumentation import org.utbot.instrumentation.instrumentation.et.convert @@ -86,7 +86,7 @@ class TestConstructors { @Test fun testCoverageConstructor() { withInstrumentation( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, CLASSPATH ) { executor -> val constructors = ClassWithMultipleConstructors::class.constructors diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestCoverageInstrumentation.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestInstructionCoverageInstrumentation.kt similarity index 92% rename from utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestCoverageInstrumentation.kt rename to utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestInstructionCoverageInstrumentation.kt index 565ca75f12..47a8f70475 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestCoverageInstrumentation.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestInstructionCoverageInstrumentation.kt @@ -8,7 +8,7 @@ import org.utbot.framework.plugin.api.util.fieldId import org.utbot.framework.plugin.api.util.signature import org.utbot.instrumentation.ConcreteExecutor import org.utbot.instrumentation.execute -import org.utbot.instrumentation.instrumentation.coverage.CoverageInstrumentation +import org.utbot.instrumentation.instrumentation.coverage.InstructionCoverageInstrumentation import org.utbot.instrumentation.instrumentation.coverage.collectCoverage import org.utbot.instrumentation.util.InstrumentedProcessError import org.utbot.instrumentation.util.StaticEnvironment @@ -18,13 +18,13 @@ import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows -class TestCoverageInstrumentation { +class TestInstructionCoverageInstrumentation { lateinit var utContext: AutoCloseable @Test fun testCatchTargetException() { ConcreteExecutor( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -41,7 +41,7 @@ class TestCoverageInstrumentation { @Test fun testIfBranches() { ConcreteExecutor( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -63,7 +63,7 @@ class TestCoverageInstrumentation { @Test fun testWrongArgumentsException() { ConcreteExecutor( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -86,7 +86,7 @@ class TestCoverageInstrumentation { @Test fun testMultipleRunsInsideCoverage() { ConcreteExecutor( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -121,7 +121,7 @@ class TestCoverageInstrumentation { @Test fun testSameResult() { ConcreteExecutor( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -143,7 +143,7 @@ class TestCoverageInstrumentation { @Test fun testResult() { ConcreteExecutor( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -160,7 +160,7 @@ class TestCoverageInstrumentation { @Test fun testEmptyMethod() { ConcreteExecutor( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -176,7 +176,7 @@ class TestCoverageInstrumentation { @Test fun testTernaryOperator() { ConcreteExecutor( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, StaticSubstitutionExamples::class.java.protectionDomain.codeSource.location.path ).use { val testObject = StaticSubstitutionExamples() diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestStaticMethods.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestStaticMethods.kt index 0a75292636..b60ea80260 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestStaticMethods.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestStaticMethods.kt @@ -4,7 +4,7 @@ import org.utbot.examples.samples.staticenvironment.StaticExampleClass import org.utbot.framework.plugin.api.util.signature import org.utbot.instrumentation.ConcreteExecutor import org.utbot.instrumentation.execute -import org.utbot.instrumentation.instrumentation.coverage.CoverageInstrumentation +import org.utbot.instrumentation.instrumentation.coverage.InstructionCoverageInstrumentation import org.utbot.instrumentation.instrumentation.coverage.collectCoverage import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue @@ -17,7 +17,7 @@ class TestStaticMethods { @Test fun testStaticMethodCall() { ConcreteExecutor( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, StaticExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val res1 = it.execute(StaticExampleClass::inc, arrayOf()) @@ -44,7 +44,7 @@ class TestStaticMethods { @Test fun testNullableMethod() { ConcreteExecutor( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, StaticExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val res1 = it.execute( @@ -75,7 +75,7 @@ class TestStaticMethods { @Test fun testNullableMethodWithoutAnnotations() { ConcreteExecutor( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, StaticExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val res1 = it.execute( diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestWithInstrumentation.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestWithInstrumentation.kt index ae00e0f7a8..dbb95e5df1 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestWithInstrumentation.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestWithInstrumentation.kt @@ -6,7 +6,7 @@ import org.utbot.examples.samples.staticenvironment.StaticExampleClass import org.utbot.framework.plugin.api.util.signature import org.utbot.instrumentation.execute import org.utbot.instrumentation.instrumentation.InvokeInstrumentation -import org.utbot.instrumentation.instrumentation.coverage.CoverageInstrumentation +import org.utbot.instrumentation.instrumentation.coverage.InstructionCoverageInstrumentation import org.utbot.instrumentation.instrumentation.coverage.collectCoverage import org.utbot.instrumentation.withInstrumentation import kotlin.reflect.full.declaredMembers @@ -20,7 +20,7 @@ class TestWithInstrumentation { @Test fun testStaticMethodCall() { withInstrumentation( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, StaticExampleClass::class.java.protectionDomain.codeSource.location.path ) { executor -> val res1 = executor.execute(StaticExampleClass::inc, arrayOf()) @@ -70,7 +70,7 @@ class TestWithInstrumentation { @Test fun testInnerClasses() { withInstrumentation( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, ClassWithInnerClasses::class.java.protectionDomain.codeSource.location.path ) { executor -> val innerClazz = ClassWithInnerClasses.InnerClass::class.java diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/Benchmark.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/Benchmark.kt index d4e9ba2028..43ec154e52 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/Benchmark.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/Benchmark.kt @@ -2,7 +2,7 @@ package org.utbot.examples.benchmark import org.utbot.instrumentation.ConcreteExecutor import org.utbot.instrumentation.instrumentation.InvokeInstrumentation -import org.utbot.instrumentation.instrumentation.coverage.CoverageInstrumentation +import org.utbot.instrumentation.instrumentation.coverage.InstructionCoverageInstrumentation import org.utbot.instrumentation.instrumentation.coverage.collectCoverage import org.utbot.instrumentation.util.Isolated import kotlin.system.measureNanoTime @@ -12,7 +12,7 @@ import org.junit.jupiter.api.Assertions.assertEquals fun getBasicCoverageTime(count: Int): Double { var time: Long ConcreteExecutor( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, Repeater::class.java.protectionDomain.codeSource.location.path ).use { executor -> val dc0 = Repeater(", ") diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/BenchmarkFibonacci.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/BenchmarkFibonacci.kt index 07d788e4ce..88b3205ca2 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/BenchmarkFibonacci.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/BenchmarkFibonacci.kt @@ -5,7 +5,7 @@ import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.withUtContext import org.utbot.instrumentation.ConcreteExecutor import org.utbot.instrumentation.instrumentation.InvokeInstrumentation -import org.utbot.instrumentation.instrumentation.coverage.CoverageInstrumentation +import org.utbot.instrumentation.instrumentation.coverage.InstructionCoverageInstrumentation import org.utbot.instrumentation.instrumentation.coverage.collectCoverage import org.utbot.instrumentation.util.Isolated import kotlin.system.measureNanoTime @@ -13,7 +13,7 @@ import kotlin.system.measureNanoTime fun getBasicCoverageTime_fib(count: Int): Double { var time: Long ConcreteExecutor( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, Fibonacci::class.java.protectionDomain.codeSource.location.path ).use { val fib = Isolated(Fibonacci::calc, it) diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/TestBenchmarkClasses.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/TestBenchmarkClasses.kt index f189907c5d..31d0556f02 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/TestBenchmarkClasses.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/TestBenchmarkClasses.kt @@ -4,7 +4,7 @@ import org.utbot.examples.samples.benchmark.Fibonacci import org.utbot.instrumentation.ConcreteExecutor import org.utbot.instrumentation.execute import org.utbot.instrumentation.instrumentation.InvokeInstrumentation -import org.utbot.instrumentation.instrumentation.coverage.CoverageInstrumentation +import org.utbot.instrumentation.instrumentation.coverage.InstructionCoverageInstrumentation import java.math.BigInteger import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Disabled @@ -18,7 +18,7 @@ class TestBenchmarkClasses { @Disabled("Ask Sergey to check") fun testRepeater() { ConcreteExecutor( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, Repeater::class.java.protectionDomain.codeSource.location.path ).use { val dc0 = Repeater(", ") diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/et/TestExecutionBranchTraceInstrumentation.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/et/TestExecutionBranchTraceInstrumentation.kt new file mode 100644 index 0000000000..923e21fa1d --- /dev/null +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/et/TestExecutionBranchTraceInstrumentation.kt @@ -0,0 +1,127 @@ +package org.utbot.examples.et + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.utbot.examples.samples.et.ClassSimple +import org.utbot.examples.samples.et.ClassSimpleCatch +import org.utbot.instrumentation.instrumentation.et.* +import org.utbot.instrumentation.util.Isolated +import org.utbot.instrumentation.withInstrumentation + +class TestExecutionBranchTraceInstrumentation { + lateinit var utContext: AutoCloseable + + val CLASSPATH = ClassSimple::class.java.protectionDomain.codeSource.location.path + + @Test + fun testClassSimple() { + withInstrumentation( + ExecutionBranchTraceInstrumentation.Factory, + CLASSPATH + ) { executor -> + val doesNotThrow = Isolated(ClassSimple::doesNotThrow, executor) + val alwaysThrows = Isolated(ClassSimple::alwaysThrows, executor) + val maybeThrows = Isolated(ClassSimple::maybeThrows, executor) + + val et1 = doesNotThrow() + Assertions.assertEquals( + function(doesNotThrow.signature) { + pass() + ret() + }, + convert(et1) + ) + + val et2 = alwaysThrows() + Assertions.assertEquals( + function(alwaysThrows.signature) { + pass() + explThr() + }, + convert(et2) + ) + + val et3 = maybeThrows(-1) + Assertions.assertEquals( + function(maybeThrows.signature) { + pass() + explThr() + }, + convert(et3) + ) + + val et4 = maybeThrows(0) + Assertions.assertEquals( + function(maybeThrows.signature) { + pass() + ret() + }, + convert(et4) + ) + } + } + + @Test + fun testClasSimpleCatch() { + withInstrumentation( + ExecutionBranchTraceInstrumentation.Factory, + CLASSPATH + ) { executor -> + val A = Isolated(ClassSimpleCatch::A, executor) + val A_catches = Isolated(ClassSimpleCatch::A_catches, executor) + val A_catchesWrongException = Isolated(ClassSimpleCatch::A_catchesWrongException, executor) + val A_doesNotCatch = Isolated(ClassSimpleCatch::A_doesNotCatch, executor) + + val B = Isolated(ClassSimpleCatch::B, executor) + val B_throws = Isolated(ClassSimpleCatch::B_throws, executor) + + val et1 = A() + Assertions.assertEquals( + function(A.signature) { + pass() + invoke(B.signature) { + pass() + ret() + } + ret() + }, + convert(et1) + ) + + val et2 = A_catches() + Assertions.assertEquals( + function(A_catches.signature) { + pass() + invoke(B_throws.signature) { + implThr() + } + pass() + ret() + }, + convert(et2) + ) + + val et3 = A_catchesWrongException() + Assertions.assertEquals( + function(A_catchesWrongException.signature) { + pass() + invoke(B_throws.signature) { + implThr() + } + }, + convert(et3) + ) + + val et4 = A_doesNotCatch() + Assertions.assertEquals( + function(A_doesNotCatch.signature) { + pass() + invoke(B_throws.signature) { + implThr() + } + }, + convert(et4) + ) + } + } +} diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/jacoco/TestSameAsJaCoCo.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/jacoco/TestSameAsJaCoCo.kt index a289e1d5fa..5c946c0e3a 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/jacoco/TestSameAsJaCoCo.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/jacoco/TestSameAsJaCoCo.kt @@ -8,7 +8,7 @@ import org.utbot.framework.plugin.api.util.signature import org.utbot.instrumentation.execute import org.utbot.instrumentation.instrumentation.ArgumentList import org.utbot.instrumentation.instrumentation.coverage.CoverageInfo -import org.utbot.instrumentation.instrumentation.coverage.CoverageInstrumentation +import org.utbot.instrumentation.instrumentation.coverage.InstructionCoverageInstrumentation import org.utbot.instrumentation.instrumentation.coverage.collectCoverage import org.utbot.instrumentation.withInstrumentation import java.io.InputStream @@ -123,7 +123,7 @@ private fun methodCoverageWithJaCoCo(kClass: KClass<*>, method: KCallable<*>, ex private fun methodCoverage(kClass: KClass<*>, method: KCallable<*>, executions: List): Pair { return withInstrumentation( - CoverageInstrumentation.Factory, + InstructionCoverageInstrumentation.Factory, kClass.java.protectionDomain.codeSource.location.path ) { executor -> for (execution in executions) { diff --git a/utbot-instrumentation/build.gradle.kts b/utbot-instrumentation/build.gradle.kts index ca8ea1f469..172aaf4f9f 100644 --- a/utbot-instrumentation/build.gradle.kts +++ b/utbot-instrumentation/build.gradle.kts @@ -8,6 +8,8 @@ val rdVersion: String by rootProject val mockitoVersion: String by rootProject val mockitoInlineVersion: String by rootProject +val jacocoVersion: String by rootProject + plugins { id("com.github.johnrengelman.shadow") version "7.1.2" id("java") @@ -56,6 +58,8 @@ dependencies { implementation("org.mockito:mockito-core:$mockitoVersion") implementation("org.mockito:mockito-inline:$mockitoInlineVersion") + implementation("org.jacoco:org.jacoco.core:$jacocoVersion") + implementation(project(":utbot-spring-commons-api")) fetchSpringCommonsJar(project(":utbot-spring-commons", configuration = "springCommonsJar")) } diff --git a/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/Api.kt b/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/Api.kt new file mode 100644 index 0000000000..e8ba862153 --- /dev/null +++ b/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/Api.kt @@ -0,0 +1,62 @@ +package org.jacoco.core.internal.instr + +import org.jacoco.core.internal.flow.ClassProbesAdapter +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.ClassWriter +import org.utbot.instrumentation.Settings +import org.utbot.instrumentation.instrumentation.et.ProcessingStorage + +fun createClassVisitorForBranchCoverageInstrumentation( + writer: ClassWriter, + className: String +): ClassProbesAdapter { + val strategy = ProbeArrayStrategy(className) + return ClassProbesAdapter( + ClassInstrumenter(strategy, writer), + false + ) +} + +class MethodProbesCollector( + strategy: IProbeArrayStrategy, + writer: ClassVisitor, + val methodToProbes: MutableMap> = mutableMapOf() +) : ClassVisitor( + Settings.ASM_API, + ClassProbesAdapter( + NoneClassInstrumenter(strategy, writer, methodToProbes), + false + ) +) + +fun createClassVisitorForComputeMapOfRangesForBranchCoverage( + writer: ClassWriter +): MethodProbesCollector { + val strategy = NoneProbeArrayStrategy() + return MethodProbesCollector(strategy, writer) +} + +class ProbeIdGenerator(private val f: (Int) -> Long) { + + private var localId: Int = 0 + + fun currentId(): Long = f(localId) + + fun nextId(): Long = f(localId++) + +} + +fun createClassVisitorForTracingBranchInstructions( + className: String, + storage: ProcessingStorage, + writer: ClassWriter +): ClassProbesAdapter { + val strategy = TraceStrategy() + val probeIdGenerator = ProbeIdGenerator { localId -> + storage.computeId(className, localId) + } + return ClassProbesAdapter( + TraceClassInstrumenter(strategy, writer, className, storage, probeIdGenerator), + false + ) +} \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/NoneClassInstrumenter.kt b/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/NoneClassInstrumenter.kt new file mode 100644 index 0000000000..6fada8d8b2 --- /dev/null +++ b/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/NoneClassInstrumenter.kt @@ -0,0 +1,30 @@ +package org.jacoco.core.internal.instr + +import org.jacoco.core.internal.flow.MethodProbesVisitor +import org.objectweb.asm.ClassVisitor + +/** + * Just a copy of [ClassInstrumenter] with one overridden method. + * This class is used to replace probe inserter. + */ +class NoneClassInstrumenter( + probeArrayStrategy: IProbeArrayStrategy, + cv: ClassVisitor, + private val methodToProbes: MutableMap>, +) : ClassInstrumenter(probeArrayStrategy, cv) { + + override fun visitMethod( + access: Int, + name: String, + desc: String, + signature: String?, + exceptions: Array? + ): MethodProbesVisitor { + val mv = cv.visitMethod(access, name, desc, signature, exceptions) + + val frameEliminator = DuplicateFrameEliminator(mv) + val probeVariableInserter = NoneProbeInserter(name, desc, methodToProbes, frameEliminator) + return MethodInstrumenter(probeVariableInserter, probeVariableInserter) + } + +} \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/NoneProbeInserter.kt b/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/NoneProbeInserter.kt new file mode 100644 index 0000000000..cabce1c099 --- /dev/null +++ b/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/NoneProbeInserter.kt @@ -0,0 +1,22 @@ +package org.jacoco.core.internal.instr + +import org.objectweb.asm.MethodVisitor +import org.utbot.instrumentation.Settings + +/** + * This inserter does not emit any code at all. This is used to collect method probes. + */ +class NoneProbeInserter( + methodName: String, + descriptor: String, + private val methodToProbes: MutableMap>, + mv: MethodVisitor, +) : IProbeInserter, MethodVisitor(Settings.ASM_API, mv) { + + private val currentMethodSignature: String = methodName + descriptor + + override fun insertProbe(id: Int) { + methodToProbes.getOrPut(currentMethodSignature) { mutableListOf() }.add(id) + } + +} \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/ProbeArrayStrategy.kt b/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/ProbeArrayStrategy.kt new file mode 100644 index 0000000000..1057b4302f --- /dev/null +++ b/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/ProbeArrayStrategy.kt @@ -0,0 +1,20 @@ +package org.jacoco.core.internal.instr + +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import org.utbot.instrumentation.Settings + +class ProbeArrayStrategy(private val className: String) : IProbeArrayStrategy { + + override fun storeInstance(mv: MethodVisitor, clinit: Boolean, variable: Int): Int { + mv.visitFieldInsn(Opcodes.GETSTATIC, className, Settings.PROBES_ARRAY_NAME, Settings.PROBES_ARRAY_DESC) + mv.visitVarInsn(Opcodes.ASTORE, variable) + return 1 + } + + override fun addMembers(cv: ClassVisitor, probeCount: Int) { + // nothing to do + } + +} \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/TraceClassInstrumenter.kt b/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/TraceClassInstrumenter.kt new file mode 100644 index 0000000000..f996ff8f59 --- /dev/null +++ b/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/TraceClassInstrumenter.kt @@ -0,0 +1,40 @@ +package org.jacoco.core.internal.instr + +import org.jacoco.core.internal.flow.MethodProbesVisitor +import org.objectweb.asm.ClassVisitor +import org.utbot.instrumentation.instrumentation.et.ProcessingStorage + +/** + * Just a copy of [ClassInstrumenter] with one overridden method. + * This class is used to replace probe inserter and method instrumenter. + */ +class TraceClassInstrumenter( + private val probeArrayStrategy: IProbeArrayStrategy, + cv: ClassVisitor, + private val className: String, + private val storage: ProcessingStorage, + private val probeIdGenerator: ProbeIdGenerator +) : ClassInstrumenter(probeArrayStrategy, cv) { + + override fun visitMethod( + access: Int, + name: String, + desc: String, + signature: String?, + exceptions: Array? + ): MethodProbesVisitor { + val mv = cv.visitMethod(access, name, desc, signature, exceptions) + + val currentMethodSignature = name + desc + storage.addClassMethod(className, currentMethodSignature) + + val frameEliminator = DuplicateFrameEliminator(mv) + val probeVariableInserter = TraceProbeInserter( + access, name, desc, frameEliminator, probeArrayStrategy, probeIdGenerator + ) + return TraceMethodInstrumenter( + currentMethodSignature, probeVariableInserter, probeVariableInserter, storage, probeIdGenerator + ) + } + +} \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/TraceMethodInstrumenter.kt b/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/TraceMethodInstrumenter.kt new file mode 100644 index 0000000000..80f9b83fd9 --- /dev/null +++ b/utbot-instrumentation/src/main/kotlin/org/jacoco/core/internal/instr/TraceMethodInstrumenter.kt @@ -0,0 +1,155 @@ +package org.jacoco.core.internal.instr + +import org.jacoco.core.internal.flow.IFrame +import org.jacoco.core.internal.flow.LabelInfo +import org.objectweb.asm.Label +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import org.utbot.instrumentation.instrumentation.et.* +import org.utbot.instrumentation.instrumentation.instrumenter.visitors.util.InstructionVisitorMethodAdapter.Companion.returnInsns + +internal class TraceMethodInstrumenter( + private val currentMethodSignature: String, + mv: MethodVisitor, + private val probeInserter: IProbeInserter, + private val storage: ProcessingStorage, + private val probeIdGenerator: ProbeIdGenerator +) : MethodInstrumenter(mv, probeInserter) { + + private var currentLineNumber: Int = 0 + + // === MethodVisitor === + + override fun visitCode() { + storeInstruction(CommonInstruction(currentLineNumber, currentMethodSignature)) + super.visitCode() + } + + override fun visitMethodInsn( + opcode: Int, + owner: String?, + name: String?, + descriptor: String?, + isInterface: Boolean + ) { + storeInstruction(InvokeInstruction(currentLineNumber, currentMethodSignature)) + super.visitMethodInsn(opcode, owner, name, descriptor, isInterface) + } + + override fun visitLineNumber(line: Int, start: Label?) { + currentLineNumber = line + super.visitLineNumber(line, start) + } + + // === MethodInstrumenter === + + private fun storeInstruction(instructionData: InstructionData) { + val id = probeIdGenerator.currentId() + storage.addInstruction(id, instructionData) + } + + override fun visitProbe(localId: Int) { + storeInstruction(CommonInstruction(currentLineNumber, currentMethodSignature)) + super.visitProbe(localId) + } + + override fun visitInsnWithProbe(opcode: Int, localId: Int) { + when (opcode) { + in returnInsns -> { + storeInstruction(ReturnInstruction(currentLineNumber, currentMethodSignature)) + } + + Opcodes.ATHROW -> { + storeInstruction(ExplicitThrowInstruction(currentLineNumber, currentMethodSignature)) + } + } + super.visitInsnWithProbe(opcode, localId) + } + + override fun visitJumpInsnWithProbe(opcode: Int, label: Label?, localId: Int, frame: IFrame?) { + storeInstruction(CommonInstruction(currentLineNumber, currentMethodSignature)) + super.visitJumpInsnWithProbe(opcode, label, localId, frame) + } + + override fun visitTableSwitchInsnWithProbes( + min: Int, max: Int, + dflt: Label, labels: Array