Skip to content

Commit 09c730e

Browse files
committed
Remove inner frames logic
1 parent 99c66ea commit 09c730e

File tree

7 files changed

+45
-84
lines changed

7 files changed

+45
-84
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
@@ -126,8 +126,8 @@ class PythonGenerateTestsCommand : CliktCommand(
126126
private val doNotSendCoverageContinuously by option("--do-not-send-coverage-continuously", help = "Do not send coverage during execution.")
127127
.flag(default = false)
128128

129-
private val coverageOutputFormat by option("--coverage-output-format", help = "Use LINES, INSTRUCTIONS or TOPFRAMEINSTRUCTIONS.")
130-
.choice("INSTRUCTIONS", "LINES", "TOPFRAMEINSTRUCTIONS")
129+
private val coverageOutputFormat by option("--coverage-output-format", help = "Use LINES, INSTRUCTIONS (only from function frame).")
130+
.choice("INSTRUCTIONS", "LINES")
131131
.default("LINES")
132132

133133
private val testFramework: TestFramework

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import pathlib
77
import sys
88
import traceback
9+
import types
910
from typing import Any, Callable, Dict, Iterable, List, Tuple
1011

1112
from utbot_executor.deep_serialization.deep_serialization import serialize_memory_dump, \
@@ -96,7 +97,7 @@ def run_function(self, request: ExecutionRequest) -> ExecutionResponse:
9697
importlib.import_module(request.function_module),
9798
request.function_name
9899
)
99-
if not callable(function):
100+
if not isinstance(function, types.FunctionType):
100101
return ExecutionFailResponse(
101102
"fail",
102103
f"Invalid function path {request.function_module}.{request.function_name}"
@@ -165,7 +166,7 @@ def _serialize_state(
165166

166167

167168
def _run_calculate_function_value(
168-
function: Callable,
169+
function: types.FunctionType,
169170
args: List[Any],
170171
kwargs: Dict[str, Any],
171172
fullpath: str,
@@ -181,7 +182,7 @@ def _run_calculate_function_value(
181182
__is_exception = False
182183

183184
_, __start = inspect.getsourcelines(function)
184-
__all_code_stmts = filter_instructions(get_instructions(function, __start), tracer.mode)
185+
__all_code_stmts = filter_instructions(get_instructions(function.__code__), tracer.mode)
185186

186187
__tracer = tracer
187188

@@ -195,7 +196,7 @@ def _run_calculate_function_value(
195196

196197
logging.debug("Coverage: %s", __tracer.counts)
197198
logging.debug("Fullpath: %s", fullpath)
198-
__stmts_with_def = [UtInstruction(__start, 0, 0)] + list(__tracer.counts.keys())
199+
__stmts_with_def = [UtInstruction(__start, 0, True)] + list(__tracer.counts.keys())
199200
__missed_filtered = [x for x in __all_code_stmts if x not in __stmts_with_def]
200201
logging.debug("Covered lines: %s", __stmts_with_def)
201202
logging.debug("Missed lines: %s", __missed_filtered)

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

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

8481
def runfunc(self, func, /, *args, **kw):
8582
result = None
86-
self.global_offset = 0
8783
sys.settrace(self.globaltrace)
84+
self.f_code = func.__code__
8885
try:
8986
result = func(*args, **kw)
9087
finally:
9188
sys.settrace(None)
92-
self.global_offset = 0
9389
return result
9490

9591
def coverage(self, filename: str) -> typing.List[int]:
@@ -100,12 +96,11 @@ def localtrace_count(self, frame, why, arg):
10096
filename = frame.f_code.co_filename
10197
lineno = frame.f_lineno
10298
if pathlib.Path(filename) == self.tested_file and lineno is not None:
103-
offset = 0
104-
if why == "opcode":
99+
if self.mode == TraceMode.Instructions and frame.f_lasti is not None:
105100
offset = frame.f_lasti
106-
self.local_offset = offset
107-
key = UtInstruction(lineno, offset, self.global_offset)
108-
print(key)
101+
else:
102+
offset = 0
103+
key = UtInstruction(lineno, offset, frame.f_code == self.f_code)
109104
if key not in self.counts:
110105
message = key.serialize()
111106
try:
@@ -116,16 +111,11 @@ def localtrace_count(self, frame, why, arg):
116111
return self.localtrace
117112

118113
def globaltrace_lt(self, frame, why, arg):
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-
124114
if why == 'call':
125115
if self.mode == TraceMode.Instructions:
126116
frame.f_trace_opcodes = True
127117
frame.f_trace_lines = False
128-
filename = frame.f_code.co_filename
118+
filename = frame.f_globals.get('__file__', None)
129119
if filename and all(not filename.startswith(d + os.sep) for d in self.ignore_dirs):
130120
modulename = _modname(filename)
131121
if modulename is not None:
@@ -142,13 +132,18 @@ def runfunc(self, func, /, *args, **kw):
142132
return func(*args, **kw)
143133

144134

135+
def g1(x):
136+
return x * 2
137+
138+
145139
def f(x):
146140
def g(x):
147141
xs = [[j for j in range(i)] for i in range(10)]
148142
return x * 2
149-
return x * g(x) + 2
143+
return g1(x) * g(x) + 2
150144

151145

152146
if __name__ == "__main__":
153147
tracer = UtTracer(pathlib.Path(__file__), [], PureSender())
154148
tracer.runfunc(f, 2)
149+
print(tracer.counts)
Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
from __future__ import annotations
12
import dataclasses
2-
import dis
33
import enum
44
import os
55
import sys
66
import typing
77
from contextlib import contextmanager
8+
from types import CodeType
89

910

1011
class TraceMode(enum.Enum):
@@ -16,13 +17,13 @@ class TraceMode(enum.Enum):
1617
class UtInstruction:
1718
line: int
1819
offset: int
19-
global_offset: int
20+
from_main_frame: bool
2021

2122
def serialize(self) -> str:
22-
return ":".join(map(str, [self.line, self.offset, self.global_offset]))
23+
return ":".join(map(str, [self.line, self.offset, int(self.from_main_frame)]))
2324

2425
def __hash__(self):
25-
return hash((self.line, self.offset, self.global_offset))
26+
return hash((self.line, self.offset, self.from_main_frame))
2627

2728

2829
@contextmanager
@@ -36,21 +37,18 @@ def suppress_stdout():
3637
sys.stdout = old_stdout
3738

3839

39-
def get_instructions(obj: object, start_line: int) -> typing.Iterator[UtInstruction]:
40-
def inner_get_instructions(x, current_line, offset):
41-
for i, el in enumerate(dis.get_instructions(x)):
42-
if el.starts_line is not None:
43-
current_line = el.starts_line
44-
yield UtInstruction(current_line, el.offset, el.offset + offset)
45-
if any(t in str(type(el.argval)) for t in ["<class 'code'>"]):
46-
inner_get_instructions(el.argval, current_line, el.offset + offset)
47-
return inner_get_instructions(obj, start_line, 0)
40+
def get_instructions(obj: CodeType) -> list[UtInstruction]:
41+
return [UtInstruction(line, start_offset, True) for start_offset, _, line in obj.co_lines() if None not in {start_offset, line}]
4842

4943

5044
def filter_instructions(
5145
instructions: typing.Iterable[UtInstruction],
5246
mode: TraceMode = TraceMode.Instructions,
5347
) -> list[UtInstruction]:
5448
if mode == TraceMode.Lines:
55-
return list({UtInstruction(it.line, 0, 0) for it in instructions})
49+
return list({UtInstruction(it.line, 0, True) for it in instructions})
5650
return list(instructions)
51+
52+
53+
def get_lines(instructions: typing.Iterable[UtInstruction]) -> list[int]:
54+
return [instruction.line for instruction in filter_instructions(instructions)]

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

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import org.utbot.framework.plugin.api.util.withUtContext
1212
import org.utbot.python.code.PythonCode
1313
import org.utbot.python.evaluation.coverage.CoverageOutputFormat
1414
import org.utbot.python.evaluation.coverage.PyInstruction
15-
import org.utbot.python.evaluation.coverage.toPair
1615
import org.utbot.python.framework.api.python.PythonClassId
1716
import org.utbot.python.framework.api.python.PythonMethodId
1817
import org.utbot.python.framework.api.python.PythonModel
@@ -286,23 +285,6 @@ abstract class PythonTestGenerationProcessor {
286285
}
287286
}
288287

289-
data class InstructionIdCoverage(val line: Int, val offset: Long, val globalOffset: Long) : CoverageFormat() {
290-
override fun equals(other: Any?): Boolean {
291-
if (other is InstructionIdCoverage) {
292-
return line == other.line && offset == other.offset && globalOffset == other.globalOffset
293-
}
294-
return false
295-
}
296-
297-
override fun hashCode(): Int {
298-
var result = line
299-
result = 31 * result + offset.hashCode()
300-
result = 31 * result + globalOffset.hashCode()
301-
return result
302-
}
303-
304-
}
305-
306288
data class CoverageInfo<T: CoverageFormat>(
307289
val covered: List<T>,
308290
val notCovered: List<T>,
@@ -325,10 +307,7 @@ abstract class PythonTestGenerationProcessor {
325307
private fun filterMissedLines(covered: Collection<LineCoverage>, missed: Collection<PyInstruction>): List<PyInstruction> =
326308
missed.filterNot { missedInstruction -> covered.any { it.start <= missedInstruction.lineNumber && missedInstruction.lineNumber <= it.end } }
327309

328-
private fun getInstructionsListWithId(instructions: Collection<PyInstruction>): List<CoverageFormat> =
329-
instructions.map { InstructionIdCoverage(it.lineNumber, it.offset, it.globalOffset) }.toSet().toList()
330-
331-
private fun getInstructionsListWithOffset(instructions: Collection<PyInstruction>): List<CoverageFormat> =
310+
private fun getInstructionsList(instructions: Collection<PyInstruction>): List<CoverageFormat> =
332311
instructions.map { InstructionCoverage(it.lineNumber, it.offset) }.toSet().toList()
333312

334313
private fun getCoverageInfo(testSets: List<PythonTestSet>): CoverageInfo<CoverageFormat> {
@@ -349,16 +328,7 @@ abstract class PythonTestGenerationProcessor {
349328
val missedLines = getLinesList(filteredMissed)
350329
CoverageInfo(coveredLines, missedLines)
351330
}
352-
CoverageOutputFormat.Instructions -> CoverageInfo(getInstructionsListWithId(covered), getInstructionsListWithId(missed))
353-
CoverageOutputFormat.TopFrameInstructions -> {
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 }
356-
357-
val coveredInstructions = getInstructionsListWithOffset(filteredCovered)
358-
val missedInstructions = getInstructionsListWithOffset(filteredMissed)
359-
360-
CoverageInfo(coveredInstructions, (missedInstructions.toSet() - coveredInstructions.toSet()).toList())
361-
}
331+
CoverageOutputFormat.Instructions -> CoverageInfo(getInstructionsList(covered), getInstructionsList(missed))
362332
}
363333
return CoverageInfo(info.covered.toSet().toList(), info.notCovered.toSet().toList())
364334
}
@@ -373,7 +343,6 @@ abstract class PythonTestGenerationProcessor {
373343
return when (coverageFormat) {
374344
is LineCoverage -> "{\"start\": ${coverageFormat.start}, \"end\": ${coverageFormat.end}}"
375345
is InstructionCoverage -> "{\"line\": ${coverageFormat.line}, \"offset\": ${coverageFormat.offset}}"
376-
is InstructionIdCoverage -> "{\"line\": ${coverageFormat.line}, \"offset\": ${coverageFormat.offset}, \"globalOffset\": ${coverageFormat.globalOffset}}"
377346
}
378347
}
379348

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

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ enum class PythonCoverageMode {
2727
data class PyInstruction(
2828
val lineNumber: Int,
2929
val offset: Long,
30-
val globalOffset: Long,
30+
val fromMainFrame: Boolean,
3131
) {
32-
override fun toString(): String = listOf(lineNumber, offset, globalOffset).joinToString(":")
32+
override fun toString(): String = listOf(lineNumber, offset, fromMainFrame).joinToString(":")
3333

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

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

4040
fun String.toPyInstruction(): PyInstruction? {
@@ -43,13 +43,13 @@ fun String.toPyInstruction(): PyInstruction? {
4343
3 -> {
4444
val line = data[0].toInt()
4545
val offset = data[1].toLong()
46-
val globalOffset = data[2].toLong()
47-
return PyInstruction(line, offset, globalOffset)
46+
val fromMainFrame = data[2].toInt() != 0
47+
return PyInstruction(line, offset, fromMainFrame)
4848
}
4949
2 -> {
5050
val line = data[0].toInt()
5151
val offset = data[1].toLong()
52-
return PyInstruction(line, offset, 0)
52+
return PyInstruction(line, offset, true)
5353
}
5454
1 -> {
5555
val line = data[0].toInt()
@@ -80,7 +80,7 @@ fun calculateCoverage(coverage: PyCoverage, method: PythonMethod): Coverage {
8080
}
8181

8282
fun calculateCoverage(statements: List<PyInstruction>, missedStatements: List<PyInstruction>, method: PythonMethod): Coverage {
83-
val covered = statements.filter { it !in missedStatements }
83+
val covered = statements.filter { it !in missedStatements && it.fromMainFrame }
8484
return Coverage(
8585
coveredInstructions=covered.map {
8686
Instruction(
@@ -104,8 +104,7 @@ fun calculateCoverage(statements: List<PyInstruction>, missedStatements: List<Py
104104

105105
enum class CoverageOutputFormat {
106106
Lines,
107-
Instructions,
108-
TopFrameInstructions;
107+
Instructions;
109108

110109
companion object {
111110
fun parse(name: String): CoverageOutputFormat {

utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/DictValueProvider.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,9 @@ object DictValueProvider : ValueProvider<UtType, PythonFuzzedValue, PythonMethod
3939
}
4040
})
4141
yield(Seed.Recursive(
42-
construct = Routine.Create(params) { v ->
43-
val items = mapOf(v[0].tree to v[1].tree).toMutableMap()
42+
construct = Routine.Create(emptyList()) { v ->
4443
PythonFuzzedValue(
45-
PythonTree.DictNode(items),
44+
PythonTree.DictNode(mutableMapOf()),
4645
"%var% = ${type.pythonTypeRepresentation()}"
4746
)
4847
},

0 commit comments

Comments
 (0)