Skip to content

Commit e62a180

Browse files
committed
instruction coverage update and fix bugs
1 parent dc62e03 commit e62a180

File tree

7 files changed

+79
-53
lines changed

7 files changed

+79
-53
lines changed

utbot-python-executor/src/main/python/utbot_executor/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "utbot-executor"
3-
version = "1.8.0"
3+
version = "1.8.0.dev4"
44
description = ""
55
authors = ["Vyacheslav Tamarin <vyacheslav.tamarin@yandex.ru>"]
66
readme = "README.md"

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Python code executor for UnitTestBot"""
22
import copy
33
import importlib
4+
import inspect
45
import logging
56
import pathlib
67
import sys
@@ -173,8 +174,8 @@ def _run_calculate_function_value(
173174

174175
__is_exception = False
175176

176-
__all_code_lines = filter_instructions(get_instructions(function), tracer.mode)
177-
__start = min([op[0] for op in __all_code_lines])
177+
_, __start = inspect.getsourcelines(function)
178+
__all_code_lines = filter_instructions(get_instructions(function, __start), tracer.mode)
178179

179180
__tracer = tracer
180181

@@ -189,7 +190,7 @@ def _run_calculate_function_value(
189190
logging.debug("Coverage: %s", __tracer.counts)
190191
logging.debug("Fullpath: %s", fullpath)
191192
__stmts = [x for x in __tracer.counts]
192-
__stmts_with_def = [(1, 0)] + __stmts
193+
__stmts_with_def = [(__start, 0)] + __stmts
193194
__missed_filtered = [x for x in __all_code_lines if x not in __stmts_with_def]
194195
logging.debug("Covered lines: %s", __stmts_with_def)
195196
logging.debug("Missed lines: %s", __missed_filtered)

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

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ def coverage(self, filename: str) -> typing.List[int]:
8484

8585
def localtrace_count(self, frame, why, arg):
8686
filename = frame.f_code.co_filename
87-
if pathlib.Path(filename) == self.tested_file:
88-
lineno = frame.f_lineno
87+
lineno = frame.f_lineno
88+
if pathlib.Path(filename) == self.tested_file and lineno is not None:
8989
offset = lineno * 2
9090
if why == "opcode":
9191
offset = frame.f_lasti
@@ -119,18 +119,3 @@ def __init__(self):
119119

120120
def runfunc(self, func, /, *args, **kw):
121121
return func(*args, **kw)
122-
123-
124-
def f(x):
125-
if 0 < x < 10 and x % 2 == 0:
126-
return 1
127-
else:
128-
return [100,
129-
x**2,
130-
x + 1
131-
]
132-
133-
134-
if __name__ in "__main__":
135-
tracer = UtTracer(pathlib.Path(__file__), [], UtCoverageSender("1", "localhost", 0, use_thread=False), mode=TraceMode.Lines)
136-
tracer.runfunc(f, 6)

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import dis
22
import enum
3-
import inspect
43
import os
54
import sys
65
import typing
@@ -23,15 +22,15 @@ def suppress_stdout():
2322
sys.stdout = old_stdout
2423

2524

26-
def get_instructions(obj: object) -> typing.Iterator[tuple[int, int]]:
25+
def get_instructions(obj: object, start_line: int) -> typing.Iterator[tuple[int, int]]:
2726
def inner_get_instructions(x, current_line):
2827
for i, el in enumerate(dis.get_instructions(x)):
2928
if el.starts_line is not None:
3029
current_line = el.starts_line
3130
yield current_line, el.offset
32-
if "<class 'code'>" in str(type(el.argval)):
31+
if any(t in str(type(el.argval)) for t in ["<class 'code'>"]):
3332
inner_get_instructions(el.argval, current_line)
34-
return inner_get_instructions(obj, None)
33+
return inner_get_instructions(obj, start_line)
3534

3635

3736
def filter_instructions(
@@ -41,4 +40,3 @@ def filter_instructions(
4140
if mode == TraceMode.Lines:
4241
return [(it, it) for it in {line for line, op in instructions}]
4342
return list(instructions)
44-

utbot-python/samples/run_tests.py

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@
77
-c <path to UTBotJava/utbot-python/samples>
88
"""
99
import argparse
10+
import contextlib
1011
import json
1112
import os
1213
import shutil
14+
import sys
1315
import typing
16+
import tqdm
17+
from tqdm.contrib import DummyTqdmFile
1418
import pathlib
1519

1620

@@ -38,11 +42,41 @@ def parse_arguments():
3842
return parser.parse_args()
3943

4044

45+
def inner_zip(collection: dict[str, typing.Iterable], keys: list[str]) -> typing.Iterator[list[typing.Any]]:
46+
key, inner_keys = keys[0], keys[1:]
47+
if len(inner_keys) == 0:
48+
yield [collection[key]]
49+
return
50+
for inner_collection in collection[key]:
51+
for group in inner_zip(inner_collection, inner_keys):
52+
yield [collection[key]] + group
53+
54+
55+
def test_inner_zip():
56+
data = {"1": [{"2": [1, 2, 3]}, {"2": [4, 5, 6]}]}
57+
actual = inner_zip(data, ["1", "2"])
58+
assert list(actual) == [[data["1"], data["1"][0]["2"]], [data["1"], data["1"][1]["2"]]]
59+
60+
4161
def parse_config(config_path: str):
4262
with open(config_path, "r") as fin:
4363
return json.loads(fin.read())
4464

4565

66+
@contextlib.contextmanager
67+
def std_out_err_redirect_tqdm():
68+
orig_out_err = sys.stdout, sys.stderr
69+
try:
70+
sys.stdout, sys.stderr = map(DummyTqdmFile, orig_out_err)
71+
yield orig_out_err[0]
72+
# Relay exceptions
73+
except Exception as exc:
74+
raise exc
75+
# Always restore sys.stdout/err if necessary
76+
finally:
77+
sys.stdout, sys.stderr = orig_out_err
78+
79+
4680
def generate_tests(
4781
java: str,
4882
jar_path: str,
@@ -60,7 +94,7 @@ def generate_tests(
6094
command += f" -c {','.join(class_names)}"
6195
if method_names is not None:
6296
command += f" -m {','.join(method_names)}"
63-
print(command)
97+
tqdm.tqdm.write(command)
6498
code = os.system(command)
6599
return code
66100

@@ -71,7 +105,7 @@ def run_tests(
71105
samples_dir: str,
72106
):
73107
command = f'{python_path} -m coverage run --source={samples_dir} -m unittest discover -p "utbot_*" {tests_dir}'
74-
print(command)
108+
tqdm.tqdm.write(command)
75109
code = os.system(command)
76110
return code
77111

@@ -119,25 +153,27 @@ def main_test_generation(args):
119153
config = parse_config(args.config_file)
120154
if pathlib.Path(args.coverage_output_dir).exists():
121155
shutil.rmtree(args.coverage_output_dir)
122-
for part in config['parts']:
123-
for file in part['files']:
124-
for group in file['groups']:
125-
full_name = pathlib.PurePath(args.path_to_test_dir, part['path'], file['name'])
126-
output_file = pathlib.PurePath(args.output_dir, f"utbot_tests_{part['path'].replace('/', '_')}_{file['name']}.py")
127-
coverage_output_file = pathlib.PurePath(args.coverage_output_dir, f"coverage_{part['path'].replace('/', '_')}_{file['name']}.json")
128-
generate_tests(
129-
args.java,
130-
args.jar,
131-
[args.path_to_test_dir],
132-
args.python_path,
133-
str(full_name),
134-
group['timeout'],
135-
str(output_file),
136-
str(coverage_output_file),
137-
group['classes'],
138-
group['methods']
139-
)
140-
156+
with std_out_err_redirect_tqdm() as orig_stdout:
157+
# for (part, file, group) in tqdm.tqdm(inner_zip(config, ["parts", "files", "groups"]), file=orig_stdout, dynamic_ncols=True):
158+
for part in tqdm.tqdm(config["parts"], file=orig_stdout, dynamic_ncols=True):
159+
for file in tqdm.tqdm(part["files"], file=orig_stdout, dynamic_ncols=True):
160+
for group in tqdm.tqdm(file["groups"], file=orig_stdout, dynamic_ncols=True):
161+
full_name = pathlib.PurePath(args.path_to_test_dir, part['path'], file['name'])
162+
output_file = pathlib.PurePath(args.output_dir, f"utbot_tests_{part['path'].replace('/', '_')}_{file['name']}.py")
163+
coverage_output_file = pathlib.PurePath(args.coverage_output_dir, f"coverage_{part['path'].replace('/', '_')}_{file['name']}.json")
164+
generate_tests(
165+
args.java,
166+
args.jar,
167+
[args.path_to_test_dir],
168+
args.python_path,
169+
str(full_name),
170+
group['timeout'],
171+
str(output_file),
172+
str(coverage_output_file),
173+
group['classes'],
174+
group['methods']
175+
)
176+
141177

142178
if __name__ == '__main__':
143179
arguments = parse_arguments()

utbot-python/samples/test_configuration.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"classes": null,
1111
"methods": null,
1212
"timeout": 10,
13-
"coverage": 100
13+
"coverage": 93
1414
}
1515
]
1616
},
@@ -233,8 +233,8 @@
233233
{
234234
"classes": null,
235235
"methods": null,
236-
"timeout": 30,
237-
"coverage": 72
236+
"timeout": 180,
237+
"coverage": 94
238238
}
239239
]
240240
},
@@ -256,7 +256,7 @@
256256
"classes": null,
257257
"methods": null,
258258
"timeout": 15,
259-
"coverage": 100
259+
"coverage": 93
260260
}
261261
]
262262
}
@@ -531,7 +531,7 @@
531531
{
532532
"classes": ["Matrix"],
533533
"methods": null,
534-
"timeout": 240,
534+
"timeout": 180,
535535
"coverage": 100
536536
}
537537
]

utbot-python/src/main/kotlin/org/utbot/python/newtyping/inference/baseline/BaselineAlgorithm.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class BaselineAlgorithm(
5252
private val openedStates: MutableMap<UtType, Pair<BaselineAlgorithmState, BaselineAlgorithmState>> = mutableMapOf()
5353
private val statistic: MutableMap<UtType, Int> = mutableMapOf()
5454

55+
private val checkedSignatures: MutableSet<UtType> = mutableSetOf()
56+
5557
private fun getRandomType(): UtType? {
5658
val weights = states.map { 1.0 / (it.anyNodes.size * it.anyNodes.size + 1) }
5759
val state = weightedRandom(states, weights, random)
@@ -92,15 +94,19 @@ class BaselineAlgorithm(
9294
val state = chooseState(states)
9395
val newState = expandState(state, storage)
9496
if (newState != null) {
95-
logger.info("Checking ${newState.signature.pythonTypeRepresentation()}")
97+
logger.info("Checking new state ${newState.signature.pythonTypeRepresentation()}")
9698
if (checkSignature(newState.signature as FunctionType, fileForMypyRuns, configFile)) {
9799
logger.debug("Found new state!")
98100
openedStates[newState.signature] = newState to state
99101
return newState.signature
100102
}
101103
} else if (state.anyNodes.isEmpty()) {
104+
if (state.signature in checkedSignatures) {
105+
return state.signature
106+
}
102107
logger.info("Checking ${state.signature.pythonTypeRepresentation()}")
103108
if (checkSignature(state.signature as FunctionType, fileForMypyRuns, configFile)) {
109+
checkedSignatures.add(state.signature)
104110
return state.signature
105111
} else {
106112
states.remove(state)

0 commit comments

Comments
 (0)