Skip to content

Commit 99c66ea

Browse files
committed
Update cli arguments and coverage format
1 parent c9c1a0e commit 99c66ea

File tree

6 files changed

+80
-44
lines changed

6 files changed

+80
-44
lines changed

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import org.utbot.python.TestFileInformation
1818
import org.utbot.python.utils.RequirementsInstaller
1919
import org.utbot.python.code.PythonCode
2020
import org.utbot.python.evaluation.coverage.PythonCoverageMode
21-
import org.utbot.python.evaluation.coverage.toPythonCoverageMode
2221
import org.utbot.python.framework.api.python.PythonClassId
2322
import org.utbot.python.framework.codegen.model.Pytest
2423
import org.utbot.python.framework.codegen.model.Unittest
@@ -127,9 +126,9 @@ class PythonGenerateTestsCommand : CliktCommand(
127126
private val doNotSendCoverageContinuously by option("--do-not-send-coverage-continuously", help = "Do not send coverage during execution.")
128127
.flag(default = false)
129128

130-
private val coverageOutputFormat by option("--coverage-output-format", help = "Use Lines, Instructions or TopFrameInstructions.")
131-
.choice("Instructions", "Lines", "TopFrameInstructions")
132-
.default("Instructions")
129+
private val coverageOutputFormat by option("--coverage-output-format", help = "Use LINES, INSTRUCTIONS or TOPFRAMEINSTRUCTIONS.")
130+
.choice("INSTRUCTIONS", "LINES", "TOPFRAMEINSTRUCTIONS")
131+
.default("LINES")
133132

134133
private val testFramework: TestFramework
135134
get() =
@@ -267,9 +266,9 @@ class PythonGenerateTestsCommand : CliktCommand(
267266
withMinimization = !doNotMinimize,
268267
isCanceled = { false },
269268
runtimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.valueOf(runtimeExceptionTestsBehaviour),
270-
coverageMeasureMode = coverageMeasureMode.toPythonCoverageMode() ?: PythonCoverageMode.Instructions,
269+
coverageMeasureMode = PythonCoverageMode.parse(coverageMeasureMode),
271270
sendCoverageContinuously = !doNotSendCoverageContinuously,
272-
coverageOutputFormat = CoverageOutputFormat.valueOf(coverageOutputFormat),
271+
coverageOutputFormat = CoverageOutputFormat.parse(coverageOutputFormat),
273272
)
274273

275274
val processor = PythonCliProcessor(

utbot-python-executor/src/main/python/utbot_executor/utbot_executor/ut_tracer.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,19 @@ def __init__(
7777
self.ignore_dirs = ignore_dirs
7878
self.sender = sender
7979
self.mode = mode
80-
self.depth = 0
80+
self.global_offset = 0
81+
self.local_offset = 0
82+
self.offsets = {}
8183

8284
def runfunc(self, func, /, *args, **kw):
8385
result = None
84-
self.depth = 0
86+
self.global_offset = 0
8587
sys.settrace(self.globaltrace)
8688
try:
8789
result = func(*args, **kw)
8890
finally:
8991
sys.settrace(None)
90-
self.depth = 0
92+
self.global_offset = 0
9193
return result
9294

9395
def coverage(self, filename: str) -> typing.List[int]:
@@ -98,10 +100,12 @@ def localtrace_count(self, frame, why, arg):
98100
filename = frame.f_code.co_filename
99101
lineno = frame.f_lineno
100102
if pathlib.Path(filename) == self.tested_file and lineno is not None:
101-
offset = lineno * 2
103+
offset = 0
102104
if why == "opcode":
103105
offset = frame.f_lasti
104-
key = UtInstruction(lineno, offset, self.depth)
106+
self.local_offset = offset
107+
key = UtInstruction(lineno, offset, self.global_offset)
108+
print(key)
105109
if key not in self.counts:
106110
message = key.serialize()
107111
try:
@@ -112,7 +116,11 @@ def localtrace_count(self, frame, why, arg):
112116
return self.localtrace
113117

114118
def globaltrace_lt(self, frame, why, arg):
115-
self.depth += 1
119+
print("Global", frame, id(frame), frame.f_lasti, self.global_offset)
120+
if frame not in self.offsets:
121+
self.offsets[frame] = self.global_offset + self.local_offset
122+
self.global_offset = self.offsets[frame]
123+
116124
if why == 'call':
117125
if self.mode == TraceMode.Instructions:
118126
frame.f_trace_opcodes = True

utbot-python-executor/src/main/python/utbot_executor/utbot_executor/utils.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ class TraceMode(enum.Enum):
1616
class UtInstruction:
1717
line: int
1818
offset: int
19-
depth: int
19+
global_offset: int
2020

2121
def serialize(self) -> str:
22-
return ":".join(map(str, [self.line, self.offset, self.depth]))
22+
return ":".join(map(str, [self.line, self.offset, self.global_offset]))
2323

2424
def __hash__(self):
25-
return hash((self.line, self.offset, self.depth))
25+
return hash((self.line, self.offset, self.global_offset))
2626

2727

2828
@contextmanager
@@ -37,13 +37,13 @@ def suppress_stdout():
3737

3838

3939
def get_instructions(obj: object, start_line: int) -> typing.Iterator[UtInstruction]:
40-
def inner_get_instructions(x, current_line, depth):
40+
def inner_get_instructions(x, current_line, offset):
4141
for i, el in enumerate(dis.get_instructions(x)):
4242
if el.starts_line is not None:
4343
current_line = el.starts_line
44-
yield UtInstruction(current_line, el.offset, depth)
44+
yield UtInstruction(current_line, el.offset, el.offset + offset)
4545
if any(t in str(type(el.argval)) for t in ["<class 'code'>"]):
46-
inner_get_instructions(el.argval, current_line, depth + 1)
46+
inner_get_instructions(el.argval, current_line, el.offset + offset)
4747
return inner_get_instructions(obj, start_line, 0)
4848

4949

@@ -52,8 +52,5 @@ def filter_instructions(
5252
mode: TraceMode = TraceMode.Instructions,
5353
) -> list[UtInstruction]:
5454
if mode == TraceMode.Lines:
55-
unique_line_instructions: set[UtInstruction] = set()
56-
for it in instructions:
57-
unique_line_instructions.add(UtInstruction(it.line, it.line * 2, it.depth))
58-
return list({UtInstruction(it.line, 2 * it.line, it.depth) for it in instructions})
55+
return list({UtInstruction(it.line, 0, 0) for it in instructions})
5956
return list(instructions)

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -286,19 +286,21 @@ abstract class PythonTestGenerationProcessor {
286286
}
287287
}
288288

289-
data class InstructionIdCoverage(val line: Int, val id: Long) : CoverageFormat() {
289+
data class InstructionIdCoverage(val line: Int, val offset: Long, val globalOffset: Long) : CoverageFormat() {
290290
override fun equals(other: Any?): Boolean {
291-
if (other is InstructionCoverage) {
292-
return line == other.line && id == other.offset
291+
if (other is InstructionIdCoverage) {
292+
return line == other.line && offset == other.offset && globalOffset == other.globalOffset
293293
}
294294
return false
295295
}
296296

297297
override fun hashCode(): Int {
298298
var result = line
299-
result = 31 * result + id.hashCode()
299+
result = 31 * result + offset.hashCode()
300+
result = 31 * result + globalOffset.hashCode()
300301
return result
301302
}
303+
302304
}
303305

304306
data class CoverageInfo<T: CoverageFormat>(
@@ -324,7 +326,7 @@ abstract class PythonTestGenerationProcessor {
324326
missed.filterNot { missedInstruction -> covered.any { it.start <= missedInstruction.lineNumber && missedInstruction.lineNumber <= it.end } }
325327

326328
private fun getInstructionsListWithId(instructions: Collection<PyInstruction>): List<CoverageFormat> =
327-
instructions.map { InstructionIdCoverage(it.lineNumber, it.id) }.toSet().toList()
329+
instructions.map { InstructionIdCoverage(it.lineNumber, it.offset, it.globalOffset) }.toSet().toList()
328330

329331
private fun getInstructionsListWithOffset(instructions: Collection<PyInstruction>): List<CoverageFormat> =
330332
instructions.map { InstructionCoverage(it.lineNumber, it.offset) }.toSet().toList()
@@ -349,8 +351,8 @@ abstract class PythonTestGenerationProcessor {
349351
}
350352
CoverageOutputFormat.Instructions -> CoverageInfo(getInstructionsListWithId(covered), getInstructionsListWithId(missed))
351353
CoverageOutputFormat.TopFrameInstructions -> {
352-
val filteredCovered = covered.filter { it.id.toPair().second == 0L }
353-
val filteredMissed = missed.filter { it.id.toPair().second == 0L }
354+
val filteredCovered = covered.filter { it.id.toPair().first == it.id.toPair().second }
355+
val filteredMissed = missed.filter { it.id.toPair().first == it.id.toPair().second }
354356

355357
val coveredInstructions = getInstructionsListWithOffset(filteredCovered)
356358
val missedInstructions = getInstructionsListWithOffset(filteredMissed)
@@ -371,7 +373,7 @@ abstract class PythonTestGenerationProcessor {
371373
return when (coverageFormat) {
372374
is LineCoverage -> "{\"start\": ${coverageFormat.start}, \"end\": ${coverageFormat.end}}"
373375
is InstructionCoverage -> "{\"line\": ${coverageFormat.line}, \"offset\": ${coverageFormat.offset}}"
374-
is InstructionIdCoverage -> "{\"line\": ${coverageFormat.line}, \"id\": ${coverageFormat.id}}"
376+
is InstructionIdCoverage -> "{\"line\": ${coverageFormat.line}, \"offset\": ${coverageFormat.offset}, \"globalOffset\": ${coverageFormat.globalOffset}}"
375377
}
376378
}
377379

utbot-python/src/main/kotlin/org/utbot/python/evaluation/coverage/CoverageApi.kt

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,28 @@ enum class PythonCoverageMode {
1313

1414
Instructions {
1515
override fun toString() = "instructions"
16-
}
17-
}
16+
};
1817

19-
fun String.toPythonCoverageMode(): PythonCoverageMode? {
20-
return when (this.lowercase()) {
21-
"lines" -> PythonCoverageMode.Lines
22-
"instructions" -> PythonCoverageMode.Instructions
23-
else -> null
18+
companion object {
19+
fun parse(name: String): PythonCoverageMode {
20+
return PythonCoverageMode.values().first {
21+
it.name.lowercase() == name.lowercase()
22+
}
23+
}
2424
}
2525
}
2626

2727
data class PyInstruction(
2828
val lineNumber: Int,
2929
val offset: Long,
30-
val depth: Int,
30+
val globalOffset: Long,
3131
) {
32-
override fun toString(): String = listOf(lineNumber, offset, depth).joinToString(":")
32+
override fun toString(): String = listOf(lineNumber, offset, globalOffset).joinToString(":")
3333

34-
val id: Long = (offset to depth.toLong()).toCoverageId()
34+
val id: Long = (offset to globalOffset).toCoverageId()
3535

3636
constructor(lineNumber: Int) : this(lineNumber, lineNumber.toLong(), 0)
37-
constructor(lineNumber: Int, id: Long) : this(lineNumber, id.toPair().first, id.toPair().second.toInt())
37+
constructor(lineNumber: Int, id: Long) : this(lineNumber, id.toPair().first, id.toPair().second)
3838
}
3939

4040
fun String.toPyInstruction(): PyInstruction? {
@@ -43,8 +43,8 @@ fun String.toPyInstruction(): PyInstruction? {
4343
3 -> {
4444
val line = data[0].toInt()
4545
val offset = data[1].toLong()
46-
val depth = data[2].toInt()
47-
return PyInstruction(line, offset, depth)
46+
val globalOffset = data[2].toLong()
47+
return PyInstruction(line, offset, globalOffset)
4848
}
4949
2 -> {
5050
val line = data[0].toInt()
@@ -106,4 +106,12 @@ enum class CoverageOutputFormat {
106106
Lines,
107107
Instructions,
108108
TopFrameInstructions;
109-
}
109+
110+
companion object {
111+
fun parse(name: String): CoverageOutputFormat {
112+
return CoverageOutputFormat.values().first {
113+
it.name.lowercase() == name.lowercase()
114+
}
115+
}
116+
}
117+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.utbot.python.evaluation.coverage
2+
3+
import kotlin.math.ceil
4+
import kotlin.math.max
5+
import kotlin.math.min
6+
import kotlin.math.sqrt
7+
8+
fun Long.toPair(): Pair<Long, Long> {
9+
val n = ceil(sqrt(this + 2.0)).toLong() - 1
10+
val k = this - (n * n - 1)
11+
return if (k <= n + 1) {
12+
n + 1 to k
13+
} else {
14+
k to n + 1
15+
}
16+
}
17+
18+
fun Pair<Long, Long>.toCoverageId(): Long {
19+
val n = max(this.first, this.second) - 1
20+
val k = min(this.first, this.second)
21+
return (n * n - 1) + k
22+
}

0 commit comments

Comments
 (0)