Skip to content

Commit 5141b8a

Browse files
committed
add description for timeout mutants
1 parent b7a81a9 commit 5141b8a

File tree

1 file changed

+44
-30
lines changed

1 file changed

+44
-30
lines changed

mutmut/__main__.py

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -312,33 +312,35 @@ def __init__(self, *, path):
312312
self.estimated_time_of_tests_by_mutant = {}
313313
self.path = path
314314
self.meta_path = Path('mutants') / (str(path) + '.meta')
315-
self.meta = None
316315
self.key_by_pid = {}
317316
self.exit_code_by_key = {}
317+
self.durations_by_key = {}
318318
self.hash_by_function_name = {}
319319
self.start_time_by_pid = {}
320-
self.estimated_time_of_tests_by_pid = {}
321320

322321
def load(self):
323322
try:
324323
with open(self.meta_path) as f:
325-
self.meta = json.load(f)
324+
meta = json.load(f)
326325
except FileNotFoundError:
327326
return
328327

329-
self.exit_code_by_key = self.meta.pop('exit_code_by_key')
330-
self.hash_by_function_name = self.meta.pop('hash_by_function_name')
331-
assert not self.meta, self.meta # We should read all the data!
328+
self.exit_code_by_key = meta.pop('exit_code_by_key')
329+
self.hash_by_function_name = meta.pop('hash_by_function_name')
330+
self.durations_by_key = meta.pop('durations_by_key')
331+
self.estimated_time_of_tests_by_mutant = meta.pop('estimated_durations_by_key')
332+
assert not meta, f'Meta file {self.meta_path} constains unexpected keys: {set(meta.keys())}'
332333

333-
def register_pid(self, *, pid, key, estimated_time_of_tests):
334+
def register_pid(self, *, pid, key):
334335
self.key_by_pid[pid] = key
335336
with START_TIMES_BY_PID_LOCK:
336337
self.start_time_by_pid[pid] = datetime.now()
337-
self.estimated_time_of_tests_by_pid[pid] = estimated_time_of_tests
338338

339339
def register_result(self, *, pid, exit_code):
340340
assert self.key_by_pid[pid] in self.exit_code_by_key
341-
self.exit_code_by_key[self.key_by_pid[pid]] = exit_code
341+
key = self.key_by_pid[pid]
342+
self.exit_code_by_key[key] = exit_code
343+
self.durations_by_key[key] = (datetime.now() - self.start_time_by_pid[pid]).total_seconds()
342344
# TODO: maybe rate limit this? Saving on each result can slow down mutation testing a lot if the test run is fast.
343345
del self.key_by_pid[pid]
344346
with START_TIMES_BY_PID_LOCK:
@@ -354,6 +356,8 @@ def save(self):
354356
json.dump(dict(
355357
exit_code_by_key=self.exit_code_by_key,
356358
hash_by_function_name=self.hash_by_function_name,
359+
durations_by_key=self.durations_by_key,
360+
estimated_durations_by_key=self.estimated_time_of_tests_by_mutant,
357361
), f, indent=4)
358362

359363

@@ -1110,7 +1114,7 @@ def read_one_child_exit_status():
11101114
else:
11111115
# in the parent
11121116
source_file_mutation_data_by_pid[pid] = m
1113-
m.register_pid(pid=pid, key=mutant_name, estimated_time_of_tests=estimated_time_of_tests)
1117+
m.register_pid(pid=pid, key=mutant_name)
11141118
running_children += 1
11151119

11161120
if running_children >= max_children:
@@ -1297,8 +1301,6 @@ def apply_mutant(mutant_name):
12971301
f.write(new_module.code)
12981302

12991303

1300-
# TODO: junitxml, html commands
1301-
13021304
@cli.command()
13031305
@click.option("--show-killed", is_flag=True, default=False, help="Display killed mutants.")
13041306
def browse(show_killed):
@@ -1331,13 +1333,14 @@ class ResultBrowser(App):
13311333
]
13321334

13331335
cursor_type = 'row'
1334-
source_file_mutation_data_and_stat_by_path = None
1336+
source_file_mutation_data_and_stat_by_path: dict[str, tuple[SourceFileMutationData, Stat]] = {}
13351337

13361338
def compose(self):
13371339
with Container(classes='container'):
13381340
yield DataTable(id='files')
13391341
yield DataTable(id='mutants')
13401342
with Widget(id="diff_view_widget"):
1343+
yield Static(id='description')
13411344
yield Static(id='diff_view')
13421345
yield Footer()
13431346

@@ -1405,25 +1408,36 @@ def on_data_table_row_highlighted(self, event):
14051408
else:
14061409
assert event.data_table.id == 'mutants'
14071410
# noinspection PyTypeChecker
1408-
diff_view: Static = self.query_one('#diff_view')
1409-
if event.row_key.value is None:
1410-
diff_view.update('')
1411+
description: Static = self.query_one('#description')
1412+
mutant_name = event.row_key.value
1413+
self.loading_id = mutant_name
1414+
path = self.path_by_name.get(mutant_name)
1415+
source_file_mutation_data, stat = self.source_file_mutation_data_and_stat_by_path[str(path)]
1416+
1417+
exit_code = source_file_mutation_data.exit_code_by_key[mutant_name]
1418+
status = status_by_exit_code[exit_code]
1419+
estimated_duration = source_file_mutation_data.estimated_time_of_tests_by_mutant.get(mutant_name, '?')
1420+
duration = source_file_mutation_data.durations_by_key.get(mutant_name, '?')
1421+
1422+
if status == 'timeout':
1423+
description.update(f'Timed out because tests did not finish within {duration:.3f} seconds. Tests without mutation took {estimated_duration:.3f} seconds.\n')
14111424
else:
1412-
diff_view.update('<loading...>')
1413-
self.loading_id = event.row_key.value
1414-
path = self.path_by_name.get(event.row_key.value)
1425+
description.update('')
14151426

1416-
def load_thread():
1417-
ensure_config_loaded()
1418-
try:
1419-
d = get_diff_for_mutant(event.row_key.value, path=path)
1420-
if event.row_key.value == self.loading_id:
1421-
diff_view.update(Syntax(d, "diff"))
1422-
except Exception as e:
1423-
diff_view.update(f"<{type(e)} {e}>")
1424-
1425-
t = Thread(target=load_thread)
1426-
t.start()
1427+
diff_view: Static = self.query_one('#diff_view')
1428+
diff_view.update('<loading code diff...>')
1429+
1430+
def load_thread():
1431+
ensure_config_loaded()
1432+
try:
1433+
d = get_diff_for_mutant(event.row_key.value, path=path)
1434+
if event.row_key.value == self.loading_id:
1435+
diff_view.update(Syntax(d, "diff"))
1436+
except Exception as e:
1437+
diff_view.update(f"<{type(e)} {e}>")
1438+
1439+
t = Thread(target=load_thread)
1440+
t.start()
14271441

14281442
def retest(self, pattern):
14291443
with self.suspend():

0 commit comments

Comments
 (0)