Skip to content

Commit d9b299b

Browse files
committed
Refactor python coverage
1 parent f308bd2 commit d9b299b

File tree

16 files changed

+166
-222
lines changed

16 files changed

+166
-222
lines changed

utbot-cli-python/src/main/kotlin/org/utbot/cli/language/python/PythonGenerateTestsCommand.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ import org.parsers.python.PythonParser
1010
import org.utbot.framework.codegen.domain.RuntimeExceptionTestsBehaviour
1111
import org.utbot.framework.codegen.domain.TestFramework
1212
import org.utbot.framework.plugin.api.UtExecutionSuccess
13-
import org.utbot.python.evaluation.coverage.CoverageOutputFormat
13+
import org.utbot.python.coverage.CoverageOutputFormat
1414
import org.utbot.python.PythonMethodHeader
1515
import org.utbot.python.PythonTestGenerationConfig
1616
import org.utbot.python.PythonTestSet
1717
import org.utbot.python.TestFileInformation
1818
import org.utbot.python.utils.RequirementsInstaller
1919
import org.utbot.python.code.PythonCode
20-
import org.utbot.python.evaluation.coverage.PythonCoverageMode
20+
import org.utbot.python.coverage.PythonCoverageMode
2121
import org.utbot.python.framework.api.python.PythonClassId
2222
import org.utbot.python.framework.codegen.model.Pytest
2323
import org.utbot.python.framework.codegen.model.Unittest

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/CoverageApi.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ package org.utbot.framework.plugin.api
1010
*
1111
* @see <a href="CONFLUENCE:Test+Minimization">Test minimization</a>
1212
*/
13-
data class Instruction(
13+
open class Instruction(
1414
val internalName: String,
1515
val methodSignature: String,
16-
val lineNumber: Int,
17-
val id: Long
16+
open val lineNumber: Int,
17+
open val id: Long
1818
) {
1919
val className: String get() = internalName.replace('/', '.')
2020
}

utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,10 @@ import org.utbot.python.evaluation.PythonEvaluationSuccess
1616
import org.utbot.python.evaluation.PythonEvaluationTimeout
1717
import org.utbot.python.evaluation.PythonWorker
1818
import org.utbot.python.evaluation.PythonWorkerManager
19-
import org.utbot.python.evaluation.coverage.CoverageIdGenerator
20-
import org.utbot.python.evaluation.coverage.PyInstruction
21-
import org.utbot.python.evaluation.coverage.PythonCoverageMode
22-
import org.utbot.python.evaluation.coverage.calculateCoverage
23-
import org.utbot.python.evaluation.coverage.makeInstructions
19+
import org.utbot.python.coverage.CoverageIdGenerator
20+
import org.utbot.python.coverage.PyInstruction
21+
import org.utbot.python.coverage.PythonCoverageMode
22+
import org.utbot.python.coverage.buildCoverage
2423
import org.utbot.python.evaluation.serialization.MemoryDump
2524
import org.utbot.python.evaluation.serialization.toPythonTree
2625
import org.utbot.python.framework.api.python.PythonTree
@@ -121,7 +120,7 @@ class PythonEngine(
121120
val beforeThisObject = beforeThisObjectTree?.let { PythonTreeModel(it.tree) }
122121
val beforeModelList = beforeModelListTree.map { PythonTreeModel(it.tree) }
123122

124-
val coverage = Coverage(makeInstructions(coveredInstructions, methodUnderTest))
123+
val coverage = Coverage(coveredInstructions)
125124
val utFuzzedExecution = PythonUtExecution(
126125
stateInit = EnvironmentModels(beforeThisObject, beforeModelList, emptyMap(), executableToCall = null),
127126
stateBefore = EnvironmentModels(beforeThisObject, beforeModelList, emptyMap(), executableToCall = null),
@@ -185,7 +184,7 @@ class PythonEngine(
185184
stateAfter = EnvironmentModels(afterThisObject, afterModelList, emptyMap(), executableToCall = null),
186185
diffIds = evaluationResult.diffIds,
187186
result = executionResult,
188-
coverage = calculateCoverage(evaluationResult.coverage, methodUnderTest),
187+
coverage = buildCoverage(evaluationResult.coveredStatements, evaluationResult.missedStatements),
189188
testMethodName = testMethodName.testName?.camelToSnakeCase(),
190189
displayName = testMethodName.displayName,
191190
summary = summary.map { DocRegularStmt(it) },
@@ -263,7 +262,7 @@ class PythonEngine(
263262
}
264263

265264
is PythonEvaluationSuccess -> {
266-
val coveredInstructions = evaluationResult.coverage.coveredInstructions
265+
val coveredInstructions = evaluationResult.coveredStatements
267266

268267
val result = handleSuccessResult(
269268
arguments,

utbot-python/src/main/kotlin/org/utbot/python/PythonTestCaseGenerator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import org.utbot.framework.minimization.minimizeExecutions
66
import org.utbot.framework.plugin.api.UtError
77
import org.utbot.framework.plugin.api.UtExecution
88
import org.utbot.framework.plugin.api.UtExecutionSuccess
9-
import org.utbot.python.evaluation.coverage.PythonCoverageMode
9+
import org.utbot.python.coverage.PythonCoverageMode
1010
import org.utbot.python.framework.api.python.PythonUtExecution
1111
import org.utbot.python.framework.api.python.util.pythonStrClassId
1212
import org.utbot.python.fuzzing.*

utbot-python/src/main/kotlin/org/utbot/python/PythonTestGenerationConfig.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package org.utbot.python
22

33
import org.utbot.framework.codegen.domain.RuntimeExceptionTestsBehaviour
44
import org.utbot.framework.codegen.domain.TestFramework
5-
import org.utbot.python.evaluation.coverage.CoverageOutputFormat
6-
import org.utbot.python.evaluation.coverage.PythonCoverageMode
5+
import org.utbot.python.coverage.CoverageOutputFormat
6+
import org.utbot.python.coverage.PythonCoverageMode
77
import java.nio.file.Path
88

99
data class TestFileInformation(

utbot-python/src/main/kotlin/org/utbot/python/PythonTestGenerationProcessor.kt

Lines changed: 17 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@ import org.utbot.framework.plugin.api.UtExecutionSuccess
1010
import org.utbot.framework.plugin.api.util.UtContext
1111
import org.utbot.framework.plugin.api.util.withUtContext
1212
import org.utbot.python.code.PythonCode
13-
import org.utbot.python.evaluation.coverage.CoverageOutputFormat
14-
import org.utbot.python.evaluation.coverage.PyInstruction
13+
import org.utbot.python.coverage.CoverageFormat
14+
import org.utbot.python.coverage.CoverageInfo
15+
import org.utbot.python.coverage.CoverageOutputFormat
16+
import org.utbot.python.coverage.PyInstruction
17+
import org.utbot.python.coverage.filterMissedLines
18+
import org.utbot.python.coverage.getInstructionsList
19+
import org.utbot.python.coverage.getLinesList
1520
import org.utbot.python.framework.api.python.PythonClassId
1621
import org.utbot.python.framework.api.python.PythonMethodId
1722
import org.utbot.python.framework.api.python.PythonModel
@@ -255,69 +260,14 @@ abstract class PythonTestGenerationProcessor {
255260
paths
256261
}
257262

258-
sealed class CoverageFormat
259-
data class LineCoverage(val start: Int, val end: Int) : CoverageFormat() {
260-
override fun equals(other: Any?): Boolean {
261-
if (other is LineCoverage) {
262-
return start == other.start && end == other.end
263-
}
264-
return false
265-
}
266-
267-
override fun hashCode(): Int {
268-
var result = start
269-
result = 31 * result + end
270-
return result
271-
}
272-
}
273-
data class InstructionCoverage(val line: Int, val offset: Long) : CoverageFormat() {
274-
override fun equals(other: Any?): Boolean {
275-
if (other is InstructionCoverage) {
276-
return line == other.line && offset == other.offset
277-
}
278-
return false
279-
}
280-
281-
override fun hashCode(): Int {
282-
var result = line
283-
result = 31 * result + offset.hashCode()
284-
return result
285-
}
286-
}
287-
288-
data class CoverageInfo<T: CoverageFormat>(
289-
val covered: List<T>,
290-
val notCovered: List<T>,
291-
)
292-
293-
private fun getLinesList(instructions: Collection<PyInstruction>): List<LineCoverage> =
294-
instructions
295-
.map { it.lineNumber }
296-
.sorted()
297-
.fold(emptyList()) { acc, lineNumber ->
298-
if (acc.isEmpty())
299-
return@fold listOf(LineCoverage(lineNumber, lineNumber))
300-
val elem = acc.last()
301-
if (elem.end + 1 == lineNumber || elem.end == lineNumber )
302-
acc.dropLast(1) + listOf(LineCoverage(elem.start, lineNumber))
303-
else
304-
acc + listOf(LineCoverage(lineNumber, lineNumber))
305-
}
306-
307-
private fun filterMissedLines(covered: Collection<LineCoverage>, missed: Collection<PyInstruction>): List<PyInstruction> =
308-
missed.filterNot { missedInstruction -> covered.any { it.start <= missedInstruction.lineNumber && missedInstruction.lineNumber <= it.end } }
309-
310-
private fun getInstructionsList(instructions: Collection<PyInstruction>): List<CoverageFormat> =
311-
instructions.map { InstructionCoverage(it.lineNumber, it.offset) }.toSet().toList()
312-
313263
private fun getCoverageInfo(testSets: List<PythonTestSet>): CoverageInfo<CoverageFormat> {
314264
val covered = mutableSetOf<PyInstruction>()
315265
val missed = mutableSetOf<PyInstruction>()
316266
testSets.forEach { testSet ->
317267
testSet.executions.forEach inner@{ execution ->
318268
val coverage = execution.coverage ?: return@inner
319-
covered.addAll(coverage.coveredInstructions.map { PyInstruction(it.lineNumber, it.id) })
320-
missed.addAll(coverage.missedInstructions.map { PyInstruction(it.lineNumber, it.id) })
269+
covered.addAll(coverage.coveredInstructions.filterIsInstance<PyInstruction>())
270+
missed.addAll(coverage.missedInstructions.filterIsInstance<PyInstruction>())
321271
}
322272
}
323273
missed -= covered
@@ -328,29 +278,20 @@ abstract class PythonTestGenerationProcessor {
328278
val missedLines = getLinesList(filteredMissed)
329279
CoverageInfo(coveredLines, missedLines)
330280
}
331-
CoverageOutputFormat.Instructions -> CoverageInfo(getInstructionsList(covered), getInstructionsList(missed))
281+
CoverageOutputFormat.Instructions -> CoverageInfo(
282+
getInstructionsList(covered),
283+
getInstructionsList(missed)
284+
)
332285
}
333286
return CoverageInfo(info.covered.toSet().toList(), info.notCovered.toSet().toList())
334287
}
335288

336-
private fun toJson(coverageInfo: CoverageInfo<CoverageFormat>): String {
337-
val covered = coverageInfo.covered.map { toJson(it) }
338-
val notCovered = coverageInfo.notCovered.map { toJson(it) }
339-
return "{\"covered\": [${covered.joinToString(", ")}], \"notCovered\": [${notCovered.joinToString(", ")}]}"
340-
}
341-
342-
private fun toJson(coverageFormat: CoverageFormat): String {
343-
return when (coverageFormat) {
344-
is LineCoverage -> "{\"start\": ${coverageFormat.start}, \"end\": ${coverageFormat.end}}"
345-
is InstructionCoverage -> "{\"line\": ${coverageFormat.line}, \"offset\": ${coverageFormat.offset}}"
346-
}
347-
}
348-
349289
protected fun getStringCoverageInfo(testSets: List<PythonTestSet>): String {
350-
val value = getCoverageInfo(testSets)
351-
return toJson(value)
290+
val coverageInfo = getCoverageInfo(testSets)
291+
val covered = coverageInfo.covered.map { it.toJson() }
292+
val notCovered = coverageInfo.notCovered.map { it.toJson() }
293+
return "{\"covered\": [${covered.joinToString(", ")}], \"notCovered\": [${notCovered.joinToString(", ")}]}"
352294
}
353-
354295
}
355296

356297
data class SelectedMethodIsNotAFunctionDefinition(val methodName: String): Exception()
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package org.utbot.python.coverage
2+
3+
import org.utbot.framework.plugin.api.Coverage
4+
import org.utbot.framework.plugin.api.Instruction
5+
6+
enum class PythonCoverageMode {
7+
Lines {
8+
override fun toString() = "lines"
9+
},
10+
11+
Instructions {
12+
override fun toString() = "instructions"
13+
};
14+
15+
companion object {
16+
fun parse(name: String): PythonCoverageMode {
17+
return PythonCoverageMode.values().first {
18+
it.name.lowercase() == name.lowercase()
19+
}
20+
}
21+
}
22+
}
23+
24+
data class PyInstruction(
25+
override val lineNumber: Int,
26+
val offset: Long,
27+
val fromMainFrame: Boolean,
28+
): Instruction("", "", lineNumber, 0) {
29+
override fun toString(): String = listOf(lineNumber, offset, fromMainFrame).joinToString(":")
30+
31+
override val id: Long = (lineNumber.toLong() to offset).toCoverageId() * 2 + fromMainFrame.toLong()
32+
33+
constructor(lineNumber: Int) : this(lineNumber, lineNumber.toLong(), true)
34+
constructor(lineNumber: Int, id: Long) : this(lineNumber, id.floorDiv(2).toPair().second, id % 2 == 1L)
35+
}
36+
37+
fun Boolean.toLong() = if (this) 1L else 0L
38+
39+
fun String.toPyInstruction(): PyInstruction? {
40+
val data = this.split(":")
41+
when (data.size) {
42+
3 -> {
43+
val line = data[0].toInt()
44+
val offset = data[1].toLong()
45+
val fromMainFrame = data[2].toInt() != 0
46+
return PyInstruction(line, offset, fromMainFrame)
47+
}
48+
2 -> {
49+
val line = data[0].toInt()
50+
val offset = data[1].toLong()
51+
return PyInstruction(line, offset, true)
52+
}
53+
1 -> {
54+
val line = data[0].toInt()
55+
return PyInstruction(line)
56+
}
57+
else -> return null
58+
}
59+
}
60+
61+
fun buildCoverage(coveredStatements: List<PyInstruction>, missedStatements: List<PyInstruction>): Coverage {
62+
return Coverage(
63+
coveredInstructions = coveredStatements,
64+
instructionsCount = (coveredStatements.size + missedStatements.size).toLong(),
65+
missedInstructions = missedStatements
66+
)
67+
}
68+
69+
enum class CoverageOutputFormat {
70+
Lines,
71+
Instructions;
72+
73+
companion object {
74+
fun parse(name: String): CoverageOutputFormat {
75+
return CoverageOutputFormat.values().first {
76+
it.name.lowercase() == name.lowercase()
77+
}
78+
}
79+
}
80+
}

utbot-python/src/main/kotlin/org/utbot/python/evaluation/coverage/CoverageIdGenerator.kt renamed to utbot-python/src/main/kotlin/org/utbot/python/coverage/CoverageIdGenerator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.utbot.python.evaluation.coverage
1+
package org.utbot.python.coverage
22

33
import java.util.concurrent.atomic.AtomicLong
44

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package org.utbot.python.coverage
2+
3+
sealed interface CoverageFormat {
4+
fun toJson(): String
5+
}
6+
data class LineCoverage(val start: Int, val end: Int) : CoverageFormat {
7+
override fun toJson(): String = "{\"start\": ${start}, \"end\": ${end}}"
8+
}
9+
10+
data class InstructionCoverage(
11+
val line: Int,
12+
val offset: Long,
13+
val fromMainFrame: Boolean
14+
) : CoverageFormat {
15+
override fun toJson(): String = "{\"line\": ${line}, \"offset\": ${offset}, \"fromMainFrame\": ${fromMainFrame}}"
16+
}
17+
18+
data class CoverageInfo<T: CoverageFormat>(
19+
val covered: List<T>,
20+
val notCovered: List<T>,
21+
)
22+
23+
fun getLinesList(instructions: Collection<PyInstruction>): List<LineCoverage> =
24+
instructions
25+
.map { it.lineNumber }
26+
.sorted()
27+
.fold(emptyList()) { acc, lineNumber ->
28+
if (acc.isEmpty())
29+
return@fold listOf(LineCoverage(lineNumber, lineNumber))
30+
val elem = acc.last()
31+
if (elem.end + 1 == lineNumber || elem.end == lineNumber )
32+
acc.dropLast(1) + listOf(LineCoverage(elem.start, lineNumber))
33+
else
34+
acc + listOf(LineCoverage(lineNumber, lineNumber))
35+
}
36+
37+
fun filterMissedLines(covered: Collection<LineCoverage>, missed: Collection<PyInstruction>): List<PyInstruction> =
38+
missed.filterNot { missedInstruction -> covered.any { it.start <= missedInstruction.lineNumber && missedInstruction.lineNumber <= it.end } }
39+
40+
fun getInstructionsList(instructions: Collection<PyInstruction>): List<CoverageFormat> =
41+
instructions.map { InstructionCoverage(it.lineNumber, it.offset, it.fromMainFrame) }.toSet().toList()

utbot-python/src/main/kotlin/org/utbot/python/evaluation/coverage/Utils.kt renamed to utbot-python/src/main/kotlin/org/utbot/python/coverage/Utils.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.utbot.python.evaluation.coverage
1+
package org.utbot.python.coverage
22

33
import kotlin.math.ceil
44
import kotlin.math.max

0 commit comments

Comments
 (0)