[dv/dvsim] Group failures per test in buckets Within each bucket collect failures per unqualified test name. Cap the failures shown to 5 unqualified test names and qualified tests 2 for each. Show the number of failures per bucket and per test. Use a trailing backslash to separate the line and log info from the qualified test name. Signed-off-by: Guillermo Maturana <maturana@google.com>
diff --git a/util/dvsim/SimCfg.py b/util/dvsim/SimCfg.py index 56c75f7..41826fe 100644 --- a/util/dvsim/SimCfg.py +++ b/util/dvsim/SimCfg.py
@@ -5,6 +5,7 @@ Class describing simulation configuration object """ +import collections import logging as log import os import shutil @@ -21,7 +22,10 @@ from testplanner.testplan_utils import parse_testplan from utils import VERBOSE, rm_path -_MAX_TESTS_PER_BUCKET = 10 + +# This affects the bucketizer failure report. +_MAX_UNIQUE_TESTS = 5 +_MAX_TEST_RESEEDS = 2 def pick_wave_format(fmts): @@ -539,20 +543,67 @@ is enabled, then the summary coverage report is also generated. The final result is in markdown format. ''' + def indent_by(level): + return " " * (4 * level) + def create_failure_message(test, line, context): - spaces = " " * 12 - message = [f" * {test.qual_name}", ""] + message = [f"{indent_by(2)}* {test.qual_name}\\"] if line: message.append( - f"{spaces}Line {line}, in log {test.get_log_path()}") + f"{indent_by(2)} Line {line}, in log " + + test.get_log_path()) else: - message.append(f"{spaces}Log {test.get_log_path()}") + message.append(f"{indent_by(2)} Log {test.get_log_path()}") if context: - lines = [f"{spaces}{c.rstrip()}" for c in context] + message.append("") + lines = [f"{indent_by(4)}{c.rstrip()}" for c in context] message.extend(lines) message.append("") return message + def create_bucket_report(buckets): + """Creates a report based on the given buckets. + + The buckets are sorted by descending number of failures. Within + buckets this also group tests by unqualified name, and just a few + failures are shown per unqualified name. + + Args: + buckets: A dictionary by bucket containing triples + (test, line, context). + + Returns: + A list of text lines for the report. + """ + by_tests = sorted(buckets.items(), + key=lambda i: len(i[1]), + reverse=True) + fail_msgs = ["\n## Failure Buckets", ""] + for bucket, tests in by_tests: + fail_msgs.append(f"* `{bucket}` has {len(tests)} failures:") + unique_tests = collections.defaultdict(list) + for (test, line, context) in tests: + unique_tests[test.name].append((test, line, context)) + for name, test_reseeds in list(unique_tests.items())[ + :_MAX_UNIQUE_TESTS]: + fail_msgs.append(f"{indent_by(1)}* Test {name} has " + f"{len(test_reseeds)} failures.") + for test, line, context in test_reseeds[:_MAX_TEST_RESEEDS]: + fail_msgs.extend( + create_failure_message(test, line, context)) + if len(test_reseeds) > _MAX_TEST_RESEEDS: + fail_msgs.append( + f"{indent_by(2)}* ... and " + f"{len(test_reseeds) - _MAX_TEST_RESEEDS} " + "more failures.") + if len(unique_tests) > _MAX_UNIQUE_TESTS: + fail_msgs.append( + f"{indent_by(1)}* ... and " + f"{len(unique_tests) - _MAX_UNIQUE_TESTS} more tests.") + + fail_msgs.append("") + return fail_msgs + deployed_items = self.deploy results = SimResults(deployed_items, run_results) @@ -612,26 +663,9 @@ self.results_summary["Name"] = self._get_results_page_link( self.results_summary["Name"]) - # Append bucketized failures for triage, sorted by descending number - # of failures. if results.buckets: self.errors_seen = True - by_tests = sorted(results.buckets.items(), - key=lambda i: len(i[1]), - reverse=True) - fail_msgs = ["\n## Failure Buckets", ""] - for bucket, tests in by_tests: - fail_msgs.append(f"* ```{bucket}```:") - # The tests could be sorted by ascending wall clock time. - for count, (test, line, context) in enumerate(tests): - if count == _MAX_TESTS_PER_BUCKET: - fail_msgs.append( - f" * ... {len(tests) - count} more tests.") - break - fail_msgs.extend( - create_failure_message(test, line, context)) - fail_msgs.append("") - results_str += "\n".join(fail_msgs) + results_str += "\n".join(create_bucket_report(results.buckets)) self.results_md = results_str