diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/Test2Code.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/Test2Code.kt index f9ebf777..eda52523 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/Test2Code.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/Test2Code.kt @@ -66,7 +66,8 @@ class Test2Code( pageSize = configuration.parameters[Test2CodeParameterDefinitions.COVERAGE_SEND_PAGE_SIZE], sender = sender, collectReleasedProbes = { coverageManager.pollRecorded() }, - collectUnreleasedProbes = { coverageManager.getUnreleased() } + collectUnreleasedProbes = { coverageManager.getUnreleased() }, + classMethodsMetadata = coverageManager.classMethodsMetadata ) private val coverageCollectionEnabled = configuration.parameters[COVERAGE_COLLECTION_ENABLED] private val classScanningEnabled = configuration.parameters[Test2CodeParameterDefinitions.CLASS_SCANNING_ENABLED] diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/classparsing/Ast.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/classparsing/Ast.kt index b76f35b6..b8988121 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/classparsing/Ast.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/classparsing/Ast.kt @@ -131,9 +131,7 @@ fun parseAstClass(className: String, classBytes: ByteArray): List { } } -private fun AstMethod.classSignature() = - "${name}/${params}/${returnType}" - +private fun AstMethod.classSignature() = "${classname}:${name}:${params}:${returnType}" private fun getReturnType(methodNode: MethodNode): String { val returnTypeDesc: String = Type.getReturnType(methodNode.desc).descriptor diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/classparsing/Checksum.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/classparsing/Checksum.kt index f8375874..e182fc9f 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/classparsing/Checksum.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/classparsing/Checksum.kt @@ -24,7 +24,7 @@ import java.io.ByteArrayInputStream val logger = KotlinLogging.logger { } -internal fun calculateMethodsChecksums( +fun calculateMethodsChecksums( classBytes: ByteArray, className: String ): Map = ClassParser(ByteArrayInputStream(classBytes), className) @@ -32,12 +32,11 @@ internal fun calculateMethodsChecksums( .methods // Filter needed for skipping interfaces, which have no opcodes for calculating checksum .filter { it.code != null } - .map { method -> method.classSignature() to calculateChecksum(method, className) } + .map { method -> method.classSignature(className) to calculateChecksum(method, className) } .filter { it.second != "" } .associate { it.first to it.second } -fun Method.classSignature() = - "${name}/${argumentTypes.asSequence().map { type -> type.toString() }.joinToString(separator = ",")}/${returnType}" +fun Method.classSignature(className: String) = "${className}:${name}:${argumentTypes.asSequence().map { type -> type.toString() }.joinToString(separator = ",")}:${returnType}" private fun calculateChecksum( method: Method, diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageManager.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageManager.kt index 95c9116b..3d1dc810 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageManager.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageManager.kt @@ -24,7 +24,7 @@ open class CoverageManager( ) : IProbesProxy, ICoverageRecorder by threadCoverageRecorder { - private val classProbePositions: ConcurrentHashMap>> = ConcurrentHashMap() + val classMethodsMetadata: ConcurrentHashMap = ConcurrentHashMap() override fun invoke( id: Long, @@ -39,15 +39,14 @@ open class CoverageManager( id = id, probes = AgentProbes(probeCount), sessionId = coverage.context.sessionId, - testId = coverage.context.testId, - probePositions = classProbePositions[id]!! // TODO do not throw + testId = coverage.context.testId ) } return execDatum.probes } - override fun addProbePositions(classId: Long, probePositions: Map>) { - classProbePositions[classId] = probePositions + override fun addClassMethodsMetadata(classId: Long, methodsMetadata: ClassMethodsMetadata) { + classMethodsMetadata[classId] = methodsMetadata } override fun pollRecorded(): Sequence { diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageSender.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageSender.kt index 041679e9..0e6ff035 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageSender.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageSender.kt @@ -24,6 +24,7 @@ import com.epam.drill.agent.test2code.common.api.MethodCoverage import com.epam.drill.agent.test2code.common.api.toBitSet import com.epam.drill.agent.test2code.common.transport.CoveragePayload import kotlinx.serialization.KSerializer +import java.util.concurrent.ConcurrentHashMap interface CoverageSender { fun startSendingCoverage() @@ -40,7 +41,8 @@ class IntervalCoverageSender( private val pageSize: Int, private val sender: AgentMessageSender = StubSender(), private val collectReleasedProbes: () -> Sequence = { emptySequence() }, - private val collectUnreleasedProbes: () -> Sequence = { emptySequence() } + private val collectUnreleasedProbes: () -> Sequence = { emptySequence() }, + private val classMethodsMetadata: ConcurrentHashMap ) : CoverageSender { private val scheduledThreadPool = Executors.newSingleThreadScheduledExecutor() private val destination = AgentMessageDestination("POST", "coverage") @@ -75,11 +77,17 @@ class IntervalCoverageSender( */ private fun sendProbes(dataToSend: Sequence) { dataToSend - .flatMap { it.probePositions.mapNotNull { (signature, positions) -> - val methodProbes = it.probes.values.copyOfRange(positions.first, positions.first + positions.second).toBitSet() - if (!methodProbes.isEmpty) null + .flatMap { + classMethodsMetadata[it.id]!!.mapNotNull { (signature, metadata) -> + val methodProbes = it.probes.values.copyOfRange( + metadata.probesStartPos, + metadata.probesStartPos + metadata.probesCount + ).toBitSet() + + if (methodProbes.isEmpty) null else MethodCoverage( signature = signature, + bodyChecksum = metadata.bodyChecksum, testId = it.testId, testSessionId = it.sessionId, probes = methodProbes diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/Instrumentation.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/Instrumentation.kt index ea5c8114..59bec08e 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/Instrumentation.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/Instrumentation.kt @@ -22,7 +22,7 @@ import com.epam.drill.agent.jacoco.DrillClassProbesAdapter import com.epam.drill.agent.jacoco.DrillDuplicateFrameEliminator import com.epam.drill.agent.jacoco.DrillMethodInstrumenter import com.epam.drill.agent.test2code.classparsing.ClassProbeCounter -import com.epam.drill.agent.test2code.classparsing.ProbeCounter +import com.epam.drill.agent.test2code.classparsing.calculateMethodsChecksums import org.jacoco.core.internal.data.CRC64 import org.jacoco.core.internal.flow.* import org.jacoco.core.internal.instr.* @@ -47,9 +47,17 @@ class DrillInstrumenter( val counter = ClassProbeCounter(className) reader.accept(DrillClassProbesAdapter(counter, false), 0) - probesProxy.addProbePositions(classId, counter.methods.associate { m -> - "${m.classname}:${m.name}:${m.params}:${m.returnType}" to Pair(m.probesStartPos, m.probesCount) - }) + val bodyChecksums = calculateMethodsChecksums(initialBytes, className) + val classMethodsMetadata: ClassMethodsMetadata = counter.methods.associate { m -> + val signature = "${m.classname}:${m.name}:${m.params}:${m.returnType}" + signature to ClassMethodMetadata( + probesStartPos = m.probesStartPos, + probesCount = m.probesCount, + bodyChecksum = bodyChecksums[signature] ?: "" // interface methods don't have a body + ) + } + + probesProxy.addClassMethodsMetadata(classId, classMethodsMetadata) val genId = classCounter.incrementAndGet() val probeCount = counter.count diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/Models.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/Models.kt index 1f2a14ac..9ec545df 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/Models.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/Models.kt @@ -30,6 +30,5 @@ data class ExecDatum( val id: ClassId, val probes: AgentProbes, val sessionId: String, - val testId: String, - val probePositions: Map> + val testId: String ) diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/ProbesProvider.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/ProbesProvider.kt index 7d056a94..ebefebba 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/ProbesProvider.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/ProbesProvider.kt @@ -24,9 +24,16 @@ import com.epam.drill.agent.jacoco.AgentProbes interface IProbesProxy { fun invoke(id: ClassId, num: Int, name: String, probeCount: Int): AgentProbes - fun addProbePositions(classId: Long, probePositions: Map>) + fun addClassMethodsMetadata(classId: Long, methodsMetadata: ClassMethodsMetadata) } +typealias ClassMethodsMetadata = Map +data class ClassMethodMetadata( + val probesStartPos: Int, + val probesCount: Int, + val bodyChecksum: String +) + const val SESSION_CONTEXT_NONE = "SESSION_CONTEXT_NONE" const val TEST_CONTEXT_NONE = "TEST_CONTEXT_NONE" const val SESSION_CONTEXT_AMBIENT = "GLOBAL"