[dv regr tool] Generate testplan annotated results
- Updated script to generate a testplan annotated results table
- Additional minor fixes to support the above change
Note the table is generated using a thirld party python library called
'tabulate'. This needs to be installed separately by running
```
pip3 install --user tabulate
```
It has been added to the `python-requirements.txt` file.
Signed-off-by: Srikrishna Iyer <sriyer@google.com>
diff --git a/util/dvsim/SimCfg.py b/util/dvsim/SimCfg.py
index cd460ef..0dfea27 100644
--- a/util/dvsim/SimCfg.py
+++ b/util/dvsim/SimCfg.py
@@ -9,12 +9,13 @@
import datetime
import logging as log
import pprint
-import random
import re
import sys
import hjson
+from testplanner import class_defs, testplan_utils
+
from .Deploy import *
from .Modes import *
from .utils import *
@@ -26,6 +27,11 @@
A simulation configuration class holds key information required for building a DV
regression framework.
"""
+
+ # Static variables - indicate timestamp.
+ ts_format_long = "%A %B %d %Y %I:%M:%S%p %Z"
+ ts_format = '+%a.%m.%d.%y__%I.%M.%S%p'
+
def __str__(self):
return pprint.pformat(self.__dict__)
@@ -34,7 +40,6 @@
def __init__(self, proj_root, args):
# Options set from command line
- self.flow = args.flow
self.cfg_files = []
self.cfg_files.append(args.cfg)
self.items = args.items
@@ -50,7 +55,6 @@
self.build_unique = args.build_unique
self.build_only = args.build_only
self.run_only = args.run_only
- self.seeds = args.seeds
self.reseed_ovrd = args.reseed
self.reseed_multiplier = args.reseed_multiplier
self.waves = args.waves
@@ -58,7 +62,6 @@
self.max_waves = args.max_waves
self.cov = args.cov
self.profile = args.profile
- self.max_odirs = args.max_odirs
self.no_rerun = args.no_rerun
self.verbosity = "{" + args.verbosity + "}"
self.email = args.email
@@ -66,8 +69,6 @@
self.dry_run = args.dry_run
self.skip_ral = args.skip_ral
self.job_prefix = args.job_prefix
- self.print_interval = args.print_interval
- self.max_parallel = args.max_parallel
# Set default sim modes for unpacking
if self.waves is True: self.en_build_modes.append("waves")
@@ -76,6 +77,7 @@
# Options built from cfg_file files
self.project = ""
+ self.flow = ""
self.flow_makefile = ""
self.scratch_path = ""
self.build_dir = ""
@@ -103,13 +105,18 @@
self.dump_file = ""
self.exports = []
- # Current timestamp
- self.ts_format_long = "%A %B %d %Y %I:%M:%S%p %Z"
- self.ts_format = '+%a.%m.%d.%y__%I.%M.%S%p'
+ # Register the seeds from command line with RunTest class.
+ RunTest.seeds = args.seeds
+ # Register the common deploy settings.
+ Deploy.print_interval = args.print_interval
+ Deploy.max_parallel = args.max_parallel
+ Deploy.max_odirs = args.max_odirs
+
+ # Current timestamp
curr_ts = datetime.datetime.now()
- self.timestamp_long = curr_ts.strftime(self.ts_format_long)
- self.timestamp = curr_ts.strftime(self.ts_format)
+ self.timestamp_long = curr_ts.strftime(SimCfg.ts_format_long)
+ self.timestamp = curr_ts.strftime(SimCfg.ts_format)
# Parse the cfg_file file tree
self.parse_sim_cfg(args.cfg)
@@ -153,20 +160,11 @@
self.run_list = []
self.create_build_and_run_list()
- # Process reseed override
- for test in self.run_list:
- # Override reseed if available.
- if self.reseed_ovrd != -1:
- test.reseed = self.reseed_ovrd
-
- # Apply reseed multiplier if set on the command line.
- test.reseed *= self.reseed_multiplier
-
# Create deploy objects
self.create_deploy_objects()
- # Deploy the builds and runs
- Deploy.deploy(self.deploy)
+ # Print info
+ log.info("Scratch path: %s", self.scratch_path)
def process_exports(self):
# Convert 'exports' to dict
@@ -208,7 +206,8 @@
# Case 1: key value in class and hjson_dict differ - error!
if type(hjson_dict_val) != type(self_val):
- log.error("Coflicting key types: %s {%s, %s}", key,
+ log.error("Coflicting key types: \"%s\" {\"%s, \"%s\"}",
+ key,
type(hjson_dict_val).__name__,
type(self_val).__name__)
sys.exit(1)
@@ -224,7 +223,7 @@
rm_hjson_dict_keys.append(key)
elif not self_val in defaults and not hjson_dict_val in defaults:
log.error(
- "Coflicting values {%s, %s} encountered for key %s",
+ "Coflicting values {\"%s\", \"%s\"} encountered for key \"%s\"",
str(self_val), str(hjson_dict_val), key)
sys.exit(1)
@@ -262,7 +261,8 @@
cfg_file = subst_wildcards(cfg_file, self.__dict__)
self.parse_sim_cfg(cfg_file)
else:
- log.error("Sim cfg file %s has already been parsed", cfg_file)
+ log.error("Sim cfg file \"%s\" has already been parsed",
+ cfg_file)
def create_objects(self):
# Create build and run modes objects
@@ -378,9 +378,17 @@
"to see a list of available tests / regressions for run", items_list)
sys.exit(1)
- # Create the build_list
+ # Process reseed override and create the build_list
build_list_names = []
for test in self.run_list:
+ # Override reseed if available.
+ if self.reseed_ovrd != -1:
+ test.reseed = self.reseed_ovrd
+
+ # Apply reseed multiplier if set on the command line.
+ test.reseed *= self.reseed_multiplier
+
+ # Create the unique set of builds needed.
if test.build_mode.name not in build_list_names:
self.build_list.append(test.build_mode)
build_list_names.append(test.build_mode.name)
@@ -388,7 +396,6 @@
def create_deploy_objects(self):
builds = []
build_map = {}
- Deploy.initialize(self)
for build in self.build_list:
item = CompileSim(build, self)
builds.append(item)
@@ -407,14 +414,41 @@
else:
self.deploy = builds
- def get_seed(self):
- if self.seeds == []:
- try:
- # Pre-populate 1000 seeds at a time
- self.seeds = run_cmd(
- "od -vAn -N4000 -tu < /dev/random | xargs").split()
- random.shuffle(self.seeds)
- except:
- log.error("Faild to generate a list of 1000 random seeds")
- sys.exit(1)
- return self.seeds.pop(-1)
+ def gen_results(self, fmt="md"):
+ '''
+ The function is called after the regression has completed. It collates the status of
+ all run targets and generates a dict. It parses the testplan and maps the generated
+ result to the testplan entries to generate a final table (list). It uses the fmt arg
+ to dump the final result as a markdown of html.
+ '''
+
+ # TODO: add support for html
+ def retrieve_result(name, results):
+ for item in results:
+ if name == item["name"]: return item
+ return None
+
+ def gen_results_sub(items, results):
+ if items == []: return results
+ for item in items:
+ # Only generate results table for runs.
+ if item.target == "run":
+ result = retrieve_result(item.name, results)
+ if result is None:
+ result = {"name": item.name, "passing": 0, "total": 0}
+ results.append(result)
+ if item.status == "P": result["passing"] += 1
+ result["total"] += 1
+ results = gen_results_sub(item.sub, results)
+ return results
+
+ # Generate results table for runs.
+ regr_results = {}
+ regr_results["timestamp"] = self.timestamp_long
+ regr_results["test_results"] = gen_results_sub(self.deploy, [])
+ results_str = "# " + self.name.upper() + " Regression Results\n"
+ results_str += " Run on " + regr_results["timestamp"] + "\n"
+ results_str += "\n## Test Results\n"
+ testplan = testplan_utils.parse_testplan(self.testplan)
+ results_str += testplan.results_table(regr_results["test_results"])
+ print(results_str)