From 00ddef4f250aca977a4ed6c95ebf56387f40837e Mon Sep 17 00:00:00 2001 From: iryabov Date: Tue, 28 Apr 2026 08:36:01 +0200 Subject: [PATCH 1/2] feat: refactor probes handling to use BitString representation --- .../drill/admin/metrics/DataIngestClient.kt | 16 +++++++------ .../writer/rawdata/config/ProbesColumnType.kt | 24 ++++++++----------- .../admin/writer/rawdata/entity/Coverage.kt | 4 +++- .../repository/impl/CoverageRepositoryImpl.kt | 2 +- .../rawdata/route/payload/CoveragePayload.kt | 3 ++- .../service/impl/RawDataServiceImpl.kt | 10 +++++--- .../writer/rawdata/ProbesColumnTypeTest.kt | 15 ++++++------ 7 files changed, 40 insertions(+), 34 deletions(-) diff --git a/admin-metrics/src/test/kotlin/com/epam/drill/admin/metrics/DataIngestClient.kt b/admin-metrics/src/test/kotlin/com/epam/drill/admin/metrics/DataIngestClient.kt index fb9f28d5a..5ff674e01 100644 --- a/admin-metrics/src/test/kotlin/com/epam/drill/admin/metrics/DataIngestClient.kt +++ b/admin-metrics/src/test/kotlin/com/epam/drill/admin/metrics/DataIngestClient.kt @@ -15,6 +15,7 @@ */ package com.epam.drill.admin.metrics +import com.epam.drill.admin.writer.rawdata.config.toBitString import com.epam.drill.admin.writer.rawdata.route.payload.* import com.jayway.jsonpath.JsonPath import io.ktor.client.* @@ -74,18 +75,19 @@ suspend fun HttpClient.launchTest( instanceId = instance.instanceId, buildVersion = instance.buildVersion, commitSha = instance.commitSha, - coverage = coverage.filter { c -> c.second.any { it != 0 } }.map { + coverage = coverage.filter { c -> c.second.any { it != 0 } }.map { (method, probes) -> SingleMethodCoveragePayload( signature = listOf( - it.first.classname, - it.first.name, - it.first.params, - it.first.returnType + method.classname, + method.name, + method.params, + method.returnType ).joinToString(":"), - bodyChecksum = it.first.bodyChecksum, + bodyChecksum = method.bodyChecksum, testId = testLaunchId, testSessionId = session.id, - probes = it.second.map { probe -> probe != 0 }.toBooleanArray() + probes = null, + stringProbes = probes.joinToString(separator = "") { if (it != 0) "1" else "0" } ) } ) diff --git a/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/config/ProbesColumnType.kt b/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/config/ProbesColumnType.kt index edd7f9587..c957f9969 100644 --- a/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/config/ProbesColumnType.kt +++ b/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/config/ProbesColumnType.kt @@ -18,31 +18,27 @@ package com.epam.drill.admin.writer.rawdata.config import org.jetbrains.exposed.sql.IColumnType import org.postgresql.util.PGobject -class ProbesColumnType(override var nullable: Boolean = false) : IColumnType { +typealias BitString = String + +class ProbesColumnType(override var nullable: Boolean = false) : IColumnType { override fun sqlType(): String = "VARBIT" - override fun valueFromDB(value: Any): BooleanArray = + override fun valueFromDB(value: Any): BitString = when (value) { - is String -> stringToBooleanArray(value) - is PGobject -> stringToBooleanArray(value.value ?: "") + is String -> value + is PGobject -> value.value ?: "" else -> throw IllegalStateException("Unsupported value type: ${value::class}") } - override fun notNullValueToDB(value: BooleanArray): Any { + override fun notNullValueToDB(value: BitString): Any { return PGobject().apply { this.type = "VARBIT" - this.value = booleanArrayToString(value) + this.value = value } } } -internal fun stringToBooleanArray(str: String): BooleanArray { - return BooleanArray(str.length) { index -> - str[index] == '1' - } -} - -internal fun booleanArrayToString(arr: BooleanArray): String { - return arr.joinToString("") { if (it) "1" else "0" } +fun BooleanArray.toBitString(): BitString { + return joinToString(separator = "") { if (it) "1" else "0" } } diff --git a/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/entity/Coverage.kt b/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/entity/Coverage.kt index 0a2684581..6d87ade3e 100644 --- a/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/entity/Coverage.kt +++ b/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/entity/Coverage.kt @@ -15,6 +15,8 @@ */ package com.epam.drill.admin.writer.rawdata.entity +import com.epam.drill.admin.writer.rawdata.config.BitString + class Coverage( val groupId: String, val appId: String, @@ -23,5 +25,5 @@ class Coverage( val methodId: String, val testId: String?, val testSessionId: String?, - val probes: BooleanArray + val probes: BitString ) \ No newline at end of file diff --git a/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/repository/impl/CoverageRepositoryImpl.kt b/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/repository/impl/CoverageRepositoryImpl.kt index 48d18a720..142cb662b 100644 --- a/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/repository/impl/CoverageRepositoryImpl.kt +++ b/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/repository/impl/CoverageRepositoryImpl.kt @@ -38,7 +38,7 @@ class CoverageRepositoryImpl : CoverageRepository { this[MethodCoverageTable.testId] = it.testId this[MethodCoverageTable.testSessionId] = it.testSessionId this[MethodCoverageTable.probes] = it.probes - this[MethodCoverageTable.probesCount] = it.probes.size + this[MethodCoverageTable.probesCount] = it.probes.length } } diff --git a/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/route/payload/CoveragePayload.kt b/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/route/payload/CoveragePayload.kt index b3bd8fef7..7f1ee2ec9 100644 --- a/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/route/payload/CoveragePayload.kt +++ b/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/route/payload/CoveragePayload.kt @@ -33,5 +33,6 @@ class SingleMethodCoveragePayload( val bodyChecksum: String, val testId: String?, val testSessionId: String?, - val probes: BooleanArray, + val probes: BooleanArray?, + val stringProbes: String? ) diff --git a/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/service/impl/RawDataServiceImpl.kt b/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/service/impl/RawDataServiceImpl.kt index 2cf4314d8..62d62e368 100644 --- a/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/service/impl/RawDataServiceImpl.kt +++ b/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/service/impl/RawDataServiceImpl.kt @@ -18,6 +18,7 @@ package com.epam.drill.admin.writer.rawdata.service.impl import com.epam.drill.admin.common.principal.User import com.epam.drill.admin.common.service.generateBuildId import com.epam.drill.admin.writer.rawdata.config.RawDataWriterDatabaseConfig.transaction +import com.epam.drill.admin.writer.rawdata.config.toBitString import com.epam.drill.admin.writer.rawdata.entity.* import com.epam.drill.admin.writer.rawdata.repository.* import com.epam.drill.admin.writer.rawdata.route.payload.* @@ -172,7 +173,10 @@ class RawDataServiceImpl( coveragePayload.commitSha, coveragePayload.buildVersion ) - coveragePayload.coverage.filter { probes -> probes.probes.any { it } }.map { coverage -> + val hasProbes: (SingleMethodCoveragePayload) -> Boolean = { coverage -> + coverage.probes?.any { it } ?: coverage.stringProbes?.contains('1') ?: false + } + coveragePayload.coverage.filter(hasProbes).map { coverage -> Coverage( groupId = coveragePayload.groupId, appId = coveragePayload.appId, @@ -181,11 +185,11 @@ class RawDataServiceImpl( methodId = listOf( coverage.signature, coverage.bodyChecksum, - coverage.probes.size + coverage.probes?.size ?: coverage.stringProbes?.length ?: 0 ).joinToString(":").md5(), testId = coverage.testId, testSessionId = coverage.testSessionId, - probes = coverage.probes + probes = coverage.probes?.toBitString() ?: coverage.stringProbes ?: "" ) } .chunked(EXEC_DATA_BATCH_SIZE) diff --git a/admin-writer/src/test/kotlin/com/epam/drill/admin/writer/rawdata/ProbesColumnTypeTest.kt b/admin-writer/src/test/kotlin/com/epam/drill/admin/writer/rawdata/ProbesColumnTypeTest.kt index dee5bb4c9..e1411eb8e 100644 --- a/admin-writer/src/test/kotlin/com/epam/drill/admin/writer/rawdata/ProbesColumnTypeTest.kt +++ b/admin-writer/src/test/kotlin/com/epam/drill/admin/writer/rawdata/ProbesColumnTypeTest.kt @@ -18,6 +18,7 @@ package com.epam.drill.admin.writer.rawdata import com.epam.drill.admin.writer.rawdata.config.ProbesColumnType import com.epam.drill.admin.test.* import com.epam.drill.admin.writer.rawdata.config.RawDataWriterDatabaseConfig +import com.epam.drill.admin.writer.rawdata.config.toBitString import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.SchemaUtils.create import org.jetbrains.exposed.sql.insert @@ -31,24 +32,24 @@ class ProbesColumnTypeTest : DatabaseTests({ RawDataWriterDatabaseConfig.init(it @BeforeEach fun initSchema() { withRollback { - create(BoolArrays) + create(CoverageTable) } } @Test fun `test storing and retrieving Probes`() = withRollback { - val originalProbes = booleanArrayOf(true, false, true, false, false, true) + val originalProbes = booleanArrayOf(true, false, true, false, false, true).toBitString() - BoolArrays.insert { - it[boolArrays] = originalProbes + CoverageTable.insert { + it[probes] = originalProbes } - val retrievedProbes = BoolArrays.selectAll().single()[BoolArrays.boolArrays] + val retrievedProbes = CoverageTable.selectAll().single()[CoverageTable.probes] assertTrue(originalProbes.contentEquals(retrievedProbes)) } } -object BoolArrays : IntIdTable() { - val boolArrays = registerColumn("bool_arrays", ProbesColumnType()) +object CoverageTable : IntIdTable() { + val probes = registerColumn("probes", ProbesColumnType()) } \ No newline at end of file From 44c49e5dd624e567825f4d195d83b66dd9bd425c Mon Sep 17 00:00:00 2001 From: iryabov Date: Tue, 28 Apr 2026 10:54:10 +0200 Subject: [PATCH 2/2] feat: update coverage payload to deprecate boolean probes in favor of string representation --- admin-app/src/main/resources/openapi.yml | 10 ++++++++-- .../writer/rawdata/route/payload/CoveragePayload.kt | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/admin-app/src/main/resources/openapi.yml b/admin-app/src/main/resources/openapi.yml index 6d517867b..81805d9cf 100644 --- a/admin-app/src/main/resources/openapi.yml +++ b/admin-app/src/main/resources/openapi.yml @@ -1936,13 +1936,19 @@ components: description: Unique identifier of the test session during which the coverage was recorded. Reference to the test session id (saved via /api/data-ingest/test-session). When absent, indicates global coverage data collected outside of any specific test session context. probes: type: array - description: Array of boolean values representing the execution state of each bytecode probe (instruction) in the method. Index position corresponds to probe position in bytecode. True indicates the probe was executed; false indicates it was not executed during the test. + deprecated: true + description: | + Array of boolean values representing the execution state of each bytecode probe (instruction) in the method. + Index position corresponds to probe position in bytecode. True indicates the probe was executed; false indicates it was not executed during the test. + Deprecated: use "stringProbes" instead. items: type: boolean + stringProbes: + type: string + description: Representing the execution state of each bytecode probe (instruction) in the method as a string of '1's and '0's, where '1' represents true (executed) and '0' represents false (not executed). required: - signature - bodyChecksum - - probes MethodsPayload: type: object description: | diff --git a/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/route/payload/CoveragePayload.kt b/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/route/payload/CoveragePayload.kt index 7f1ee2ec9..6d9115b28 100644 --- a/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/route/payload/CoveragePayload.kt +++ b/admin-writer/src/main/kotlin/com/epam/drill/admin/writer/rawdata/route/payload/CoveragePayload.kt @@ -33,6 +33,7 @@ class SingleMethodCoveragePayload( val bodyChecksum: String, val testId: String?, val testSessionId: String?, + @Deprecated ("Use stringProbes instead") val probes: BooleanArray?, val stringProbes: String? )