[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