From cefb7483c04a981f628f44f428712944e0a7280c Mon Sep 17 00:00:00 2001 From: Eyck Jentzsch Date: Mon, 11 Jul 2022 09:44:05 +0200 Subject: [PATCH 1/9] adds union/non-union merge aggregation --- src/ucis/report/coverage_report_builder.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ucis/report/coverage_report_builder.py b/src/ucis/report/coverage_report_builder.py index f009643..ee3f8ae 100644 --- a/src/ucis/report/coverage_report_builder.py +++ b/src/ucis/report/coverage_report_builder.py @@ -72,18 +72,22 @@ def build_covergroup(self, cg_n)->CoverageReport.Covergroup: coverage = 0.0 div = 0 + non_union_merge = True for cp in cg_r.coverpoints: + non_union_merge = False if cp.weight > 0: coverage += cp.coverage * cp.weight div += cp.weight for cr in cg_r.crosses: + non_union_merge = False coverage += cr.coverage * cr.weight div += cr.weight -# for cg in cg_r.covergroups: -# coverage += cg.coverage * cg.weight -# div += cg.weight + if non_union_merge: + for cg in cg_r.covergroups: + coverage += cg.coverage * cg.weight + div += cg.weight if div > 0: coverage /= div From 68080e8300f3dba06074b4abd3adff0fd3f0e12f Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Fri, 8 Jul 2022 20:16:38 -0700 Subject: [PATCH 2/9] Add initial merge implementation and build out conversion infrastructure Signed-off-by: Matthew Ballance --- .vscode/settings.json | 13 + src/ucis/__init__.py | 10 +- src/ucis/__main__.py | 41 ++- src/ucis/cmd/cmd_convert.py | 36 +++ src/ucis/cmd/cmd_merge.py | 46 ++- src/ucis/cmd/cmd_report.py | 17 ++ src/ucis/lib/lib_scope.py | 14 +- src/ucis/mem/__init__.py | 1 + src/ucis/mem/mem_covergroup.py | 6 +- src/ucis/mem/mem_history_node.py | 2 +- src/ucis/mem/mem_scope.py | 15 +- src/ucis/merge/__init__.py | 1 + src/ucis/merge/db_merger.py | 260 ++++++++++------- src/ucis/report/coverage_report_builder.py | 5 +- src/ucis/xml/db_format_if_xml.py | 3 +- src/ucis/xml/xml_ucis.py | 12 + src/ucis/xml/xml_writer.py | 8 +- src/ucis/yaml/db_format_if_yaml.py | 11 +- src/ucis/yaml/yaml_reader.py | 114 ++++---- src/ucis/yaml/yaml_ucis.py | 7 + ve/unit/runtest.sh | 9 + ve/unit/test_merge.py | 317 +++++++++++++++++++-- ve/unit/test_yaml_reader.py | 64 +++-- 23 files changed, 745 insertions(+), 267 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/ucis/cmd/cmd_convert.py create mode 100644 src/ucis/xml/xml_ucis.py create mode 100644 src/ucis/yaml/yaml_ucis.py create mode 100755 ve/unit/runtest.sh diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9e7e4d7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "python.defaultInterpreterPath": "${workspaceFolder}/packages/python/bin/python3", + "python.testing.unittestArgs": [ + "-v", + "-s", + "./ve/unit", + "-p", + "test_**.py" + ], + "python.envFile": "${workspaceFolder}/.env", + "python.testing.pytestEnabled": false, + "python.testing.unittestEnabled": true +} \ No newline at end of file diff --git a/src/ucis/__init__.py b/src/ucis/__init__.py index 7d50dca..a8860a2 100644 --- a/src/ucis/__init__.py +++ b/src/ucis/__init__.py @@ -23,11 +23,11 @@ from .ucis import UCIS from .scope import Scope -from ucis.handle_property import HandleProperty -from ucis.history_node import HistoryNode -from ucis.history_node_kind import HistoryNodeKind -from ucis.int_property import IntProperty -from ucis.obj import Obj +from .handle_property import HandleProperty +from .history_node import HistoryNode +from .history_node_kind import HistoryNodeKind +from .int_property import IntProperty +from .obj import Obj from ucis.real_property import RealProperty from ucis.source_info import SourceInfo from ucis.str_property import StrProperty diff --git a/src/ucis/__main__.py b/src/ucis/__main__.py index bb0bedc..6964d35 100644 --- a/src/ucis/__main__.py +++ b/src/ucis/__main__.py @@ -6,27 +6,39 @@ import argparse from ucis.cmd import cmd_list_db_formats from ucis.cmd import cmd_list_report_formats -from .cmd import cmd_report +from ucis.cmd import cmd_report, cmd_merge, cmd_convert import sys +import traceback def get_parser(): parser = argparse.ArgumentParser() subparser = parser.add_subparsers() subparser.required = True + + convert = subparser.add_parser("convert") + convert.add_argument("--out", "-o", + help="Specifies the output of the conversion", + required=True) + convert.add_argument("--input-format", "-if", + help="Specifies the format of the input database. Defaults to 'xml'") + convert.add_argument("--output-format", "-of", + help="Specifies the format of the output database. Defaults to 'xml'") + convert.add_argument("input", help="Source database to convert") + convert.set_defaults(func=cmd_convert.convert) - # merge = subparser.add_parser("merge") - # merge.add_argument("--out", "-o", - # help="Specifies the output of the merge", - # required=True) - # merge.add_argument("--input-format", "-if", - # help="Specifies the format of the input databases. Defaults to 'xml'") - # merge.add_argument("--output-format", "-of", - # help="Specifies the format of the input databases. Defaults to 'xml'") - # merge.add_argument("--libucis", "-l", - # help="Specifies the name/path of the UCIS shared library") - # merge.add_argument("db", action="append") - # merge.set_defaults(func=cmd.cmd_merge.merge) + merge = subparser.add_parser("merge") + merge.add_argument("--out", "-o", + help="Specifies the output of the merge", + required=True) + merge.add_argument("--input-format", "-if", + help="Specifies the format of the input databases. Defaults to 'xml'") + merge.add_argument("--output-format", "-of", + help="Specifies the format of the input databases. Defaults to 'xml'") + merge.add_argument("--libucis", "-l", + help="Specifies the name/path of the UCIS shared library") + merge.add_argument("db", action="append") + merge.set_defaults(func=cmd_merge.merge) list_db_formats = subparser.add_parser("list-db-formats", help="Shows available database formats") @@ -67,8 +79,9 @@ def main(): try: args.func(args) except Exception as e: + traceback.print_exc() print("Error: %s" % "{0}".format(e)) if __name__ == "__main__": main() - \ No newline at end of file + diff --git a/src/ucis/cmd/cmd_convert.py b/src/ucis/cmd/cmd_convert.py new file mode 100644 index 0000000..54aa992 --- /dev/null +++ b/src/ucis/cmd/cmd_convert.py @@ -0,0 +1,36 @@ + +from ucis.rgy import FormatRgy +from ucis.rgy import FormatDescDb, FormatIfDb +from ucis.merge import DbMerger + +def convert(args): + if args.input_format is None: + args.input_format = "xml" + if args.output_format is None: + args.output_format = "xml" + + rgy = FormatRgy.inst() + if not rgy.hasDatabaseFormat(args.input_format): + raise Exception("Input format %s not recognized" % args.input_format) + if not rgy.hasDatabaseFormat(args.output_format): + raise Exception("Output format %s not recognized" % args.output_format) + + input_desc : FormatDescDb = rgy.getDatabaseDesc(args.input_format) + output_desc : FormatDescDb = rgy.getDatabaseDesc(args.output_format) + + input_if = input_desc.fmt_if() + output_if = output_desc.fmt_if() + + try: + in_db = input_if.read(args.input) + except Exception as e: + raise Exception("Failed to read file %s ; %s" % (args.input, str(e))) + + out_db = output_if.create() + + # For now, we treat a merge like a poor-man's copy + merger = DbMerger() + merger.merge(out_db, [in_db]) + + out_db.write(args.out) + in_db.close() diff --git a/src/ucis/cmd/cmd_merge.py b/src/ucis/cmd/cmd_merge.py index 9e13d37..a8a48c5 100644 --- a/src/ucis/cmd/cmd_merge.py +++ b/src/ucis/cmd/cmd_merge.py @@ -4,6 +4,50 @@ @author: mballance ''' +from typing import List +from ucis.merge.db_merger import DbMerger +from ucis.rgy import FormatRgy +from ucis.rgy.format_if_db import FormatDescDb, FormatIfDb +from ucis.ucis import UCIS + def merge(args): - pass + if args.input_format is None: + args.input_format = "xml" + if args.output_format is None: + args.output_format = "xml" + + rgy = FormatRgy.inst() + if not rgy.hasDatabaseFormat(args.input_format): + raise Exception("Input format %s not recognized" % args.input_format) + if not rgy.hasDatabaseFormat(args.output_format): + raise Exception("Output format %s not recognized" % args.output_format) + + input_desc : FormatDescDb = rgy.getDatabaseDesc(args.input_format) + output_desc : FormatDescDb = rgy.getDatabaseDesc(args.output_format) + + db_l : List[UCIS] = [] + for input in args.db: + db_if : FormatIfDb = input_desc.fmt_if() + try: + db = db_if.read(input) + db_l.append(db) + except Exception as e: + raise Exception("Failed to read input file %s: %s" % ( + input, + str(e) + )) + + out_if = output_desc.fmt_if() + out_db : UCIS = out_if.create() + + merger = DbMerger() + try: + merger.merge(out_db, db_l) + except Exception as e: + raise Exception("Merge operation failed: %s" % str(e)) + + out_db.write(args.out) + for db in db_l: + db.close() + \ No newline at end of file diff --git a/src/ucis/cmd/cmd_report.py b/src/ucis/cmd/cmd_report.py index ccdcd9e..04f6e39 100644 --- a/src/ucis/cmd/cmd_report.py +++ b/src/ucis/cmd/cmd_report.py @@ -3,6 +3,8 @@ @author: mballance ''' +from ucis.rgy.format_if_db import FormatIfDb +from ucis.rgy.format_if_rpt import FormatIfRpt from ucis.rgy.format_rgy import FormatRgy def report(args): @@ -17,6 +19,21 @@ def report(args): if not rgy.hasDatabaseFormat(args.input_format): raise Exception("Unknown input format %s ; supported=%s" % ( args.input_format, str(rgy.getDatabaseFormats()))) + if not rgy.hasReportFormat(args.output_format): + raise Exception("Unknown report format %s ; supported=%s" % ( + args.output_format, str(rgy.getReportFormats()))) + + input_desc = rgy.getDatabaseDesc(args.input_format) + input_if : FormatIfDb = input_desc.fmt_if() + output_desc = rgy.getReportDesc(args.output_format) + output_if : FormatIfRpt = output_desc.fmt_if() + + in_db = input_if.read(args.db) + + with open(args.out, "w") as fp: + output_if.report(in_db, fp, []) + + in_db.close() # in_desc = rgy. diff --git a/src/ucis/lib/lib_scope.py b/src/ucis/lib/lib_scope.py index 2c3f395..028a540 100644 --- a/src/ucis/lib/lib_scope.py +++ b/src/ucis/lib/lib_scope.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. import logging +from ucis.int_property import IntProperty from ucis.lib.lib_cover_index import LibCoverIndex from ucis.lib.lib_scope_iterator import LibScopeIterator from typing import Iterator @@ -27,9 +28,6 @@ from _ctypes import byref, pointer from ucis.scope import Scope from ucis.unimpl_error import UnimplError -from ucis import UCIS_COVERGROUP, UCIS_INT_SCOPE_GOAL, UCIS_INT_CVG_STROBE,\ - UCIS_INT_CVG_MERGEINSTANCES, UCIS_STR_COMMENT, UCIS_INT_SCOPE_WEIGHT - from ucis.cover_data import CoverData from ucis.flags_t import FlagsT from ucis.lib.lib_cover_data import LibCoverData @@ -52,16 +50,16 @@ def __init__(self, db, obj): logging.debug("LibScope::init - db=" + str(self.db) + " " + str(self.obj)) def getGoal(self)->int: - return self.getIntProperty(-1, UCIS_INT_SCOPE_GOAL) + return self.getIntProperty(-1, IntProperty.SCOPE_GOAL) def setGoal(self,goal)->int: - self.setIntProperty(-1, UCIS_INT_SCOPE_GOAL, goal) + self.setIntProperty(-1, IntProperty.SCOPE_GOAL, goal) # def getWeight(self): -# return self.getIntProperty(-1, UCIS_INT_SCOPE_WEIGHT) +# return self.getIntProperty(-1, IntProperty.SCOPE_WEIGHT) # # def setWeight(self, w): -# self.setIntProperty(-1, UCIS_INT_SCOPE_WEIGHT, w) +# self.setIntProperty(-1, IntProperty.SCOPE_WEIGHT, w) def createScope(self, name:str, @@ -131,7 +129,7 @@ def createCovergroup(self, srcinfo_p, weight, source, - UCIS_COVERGROUP, + ScopeTypeT.COVERGROUP, 0) return LibCovergroup(self.db, cg_obj) diff --git a/src/ucis/mem/__init__.py b/src/ucis/mem/__init__.py index ba33a4d..0db80f6 100644 --- a/src/ucis/mem/__init__.py +++ b/src/ucis/mem/__init__.py @@ -16,3 +16,4 @@ # specific language governing permissions and limitations # under the License. +from .mem_ucis import * diff --git a/src/ucis/mem/mem_covergroup.py b/src/ucis/mem/mem_covergroup.py index 0b38254..76f3c8d 100644 --- a/src/ucis/mem/mem_covergroup.py +++ b/src/ucis/mem/mem_covergroup.py @@ -4,10 +4,10 @@ @author: ballance ''' from typing import List -from ucis import UCIS_COVERGROUP, UCIS_COVERINSTANCE from ucis.cover_type import CoverType from ucis.covergroup import Covergroup from ucis.mem.mem_cvg_scope import MemCvgScope +from ucis.scope_type_t import ScopeTypeT from ucis.source_t import SourceT from ucis.source_info import SourceInfo @@ -20,7 +20,7 @@ def __init__(self, weight, source): MemCvgScope.__init__(self, parent, name, srcinfo, weight, source, - UCIS_COVERGROUP, 0) + ScopeTypeT.COVERGROUP, 0) Covergroup.__init__(self) self.at_least = 0 self.auto_bin_max @@ -83,7 +83,7 @@ def createCoverInstance( srcinfo, weight, source, - UCIS_COVERINSTANCE, + ScopeTypeT.COVERINSTANCE, 0) return ci_obj diff --git a/src/ucis/mem/mem_history_node.py b/src/ucis/mem/mem_history_node.py index 5989a70..11fb9f9 100644 --- a/src/ucis/mem/mem_history_node.py +++ b/src/ucis/mem/mem_history_node.py @@ -25,7 +25,7 @@ from ucis.history_node import HistoryNode from datetime import datetime from ucis.test_data import TestData -from ucis import IntProperty +from ucis.int_property import IntProperty class MemHistoryNode(HistoryNode): diff --git a/src/ucis/mem/mem_scope.py b/src/ucis/mem/mem_scope.py index e78ce5a..7dde27e 100644 --- a/src/ucis/mem/mem_scope.py +++ b/src/ucis/mem/mem_scope.py @@ -21,8 +21,7 @@ ''' from typing import Iterator, List -from ucis import IntProperty, UCIS_COVERGROUP, UCIS_COVERINSTANCE,\ - UCIS_COVERPOINT, UCIS_CROSS +from ucis.int_property import IntProperty from ucis.cover_data import CoverData from ucis.cover_index import CoverIndex from ucis.cover_type_t import CoverTypeT @@ -155,17 +154,17 @@ def createScope(self, if ScopeTypeT.DU_ANY(type): ret = MemScope(self, name, srcinfo, weight, source, type, flags) - elif type == UCIS_COVERGROUP: + elif type == ScopeTypeT.COVERGROUP: from .mem_covergroup import MemCovergroup ret = MemCovergroup(self, name, srcinfo, weight,source) - elif type == UCIS_COVERINSTANCE: + elif type == ScopeTypeT.COVERINSTANCE: from .mem_covergroup import MemCovergroup ret = MemCovergroup(self, name, srcinfo, weight,source) - ret.m_type = UCIS_COVERINSTANCE - elif type == UCIS_COVERPOINT: + ret.m_type = ScopeTypeT.COVERINSTANCE + elif type == ScopeTypeT.COVERPOINT: from .mem_coverpoint import MemCoverpoint ret = MemCoverpoint(self, name, srcinfo, weight, source) -# elif type == UCIS_CROSS: +# elif type == ScopeTypeT.CROSS: # from .mem_cross import MemCross # ret = MemCross(self, name, srcinfo, weight, source) else: @@ -203,4 +202,4 @@ def scopes(self, mask)->Iterator['Scope']: def coverItems(self, mask : CoverTypeT) -> Iterator[CoverIndex]: return MemCoverIndexIterator(self.m_cover_items, mask) - \ No newline at end of file + diff --git a/src/ucis/merge/__init__.py b/src/ucis/merge/__init__.py index ba33a4d..02b9a51 100644 --- a/src/ucis/merge/__init__.py +++ b/src/ucis/merge/__init__.py @@ -16,3 +16,4 @@ # specific language governing permissions and limitations # under the License. +from .db_merger import * diff --git a/src/ucis/merge/db_merger.py b/src/ucis/merge/db_merger.py index 6e9c18a..aa4f4e1 100644 --- a/src/ucis/merge/db_merger.py +++ b/src/ucis/merge/db_merger.py @@ -3,9 +3,11 @@ @author: mballance ''' +from typing import Dict, Tuple, List + from ucis import UCIS_OTHER, UCIS_INSTANCE, UCIS_DU_MODULE, UCIS_ENABLED_STMT, \ UCIS_ENABLED_BRANCH, UCIS_ENABLED_COND, UCIS_ENABLED_EXPR, UCIS_ENABLED_FSM, \ - UCIS_ENABLED_TOGGLE, UCIS_INST_ONCE, UCIS_SCOPE_UNDER_DU, UCIS_CVGBIN,\ + UCIS_ENABLED_TOGGLE, UCIS_INST_ONCE, UCIS_SCOPE_UNDER_DU, UCIS_CVGBIN, \ UCIS_IGNOREBIN, UCIS_ILLEGALBIN, coverpoint from ucis.cover_type_t import CoverTypeT from ucis.report.coverage_report import CoverageReport @@ -16,122 +18,176 @@ class DbMerger(object): - def __init__(self, dst_db): - self.dst_db = dst_db + def __init__(self): + self.dst_db = None - def merge(self, src_db): - for src_iscope in src_db.scopes(ScopeTypeT.INSTANCE): - # Search for relevant scope in dst_db - # - If exists validate that it is the same as the source scope - # - If it doesn't exist, copy src->dst - print("src_name: %s" % src_iscope.getScopeName()) - dst_iscope = None - for dst_iscope_t in self.dst_db.scopes(ScopeTypeT.INSTANCE): - if dst_iscope_t.getScopeName() == src_iscope.getScopeName(): - print("found scope") - dst_iscope = dst_iscope_t - break - - if dst_iscope is not None: - # TODO: validate - print("TODO: validate") - pass - else: - src_du = src_iscope.getInstanceDu() - dst_du = self.dst_db.createScope( - src_du.getScopeName(), - None, - 1, - UCIS_OTHER, # TODO: must query SourceType - UCIS_DU_MODULE, # TODO: must query GetScopeType - UCIS_ENABLED_STMT | UCIS_ENABLED_BRANCH - | UCIS_ENABLED_COND | UCIS_ENABLED_EXPR - | UCIS_ENABLED_FSM | UCIS_ENABLED_TOGGLE - | UCIS_INST_ONCE | UCIS_SCOPE_UNDER_DU) # TODO: GetScopeFlags - - dst_iscope = self.dst_db.createInstance( - src_iscope.getScopeName(), - None, - 1, # weight - UCIS_OTHER, # query SourceType - UCIS_INSTANCE, - dst_du, - UCIS_INST_ONCE) + def merge(self, dst_db, src_db_l : List[UCIS]): + # There are three possible actions for each instance scope + # in the two databases: + # - Only exists in DB1 -> Copy from DB1 + # - Only exists in DB2 -> Copy from DB2 + # - Exists in both -> Copy from one (DB1?) and merge + + self.dst_db = dst_db + + iscope_m : Dict[str, List[object]] = {} + iscope_name_l = [] - self._merge_covergroups(dst_iscope, src_iscope) + for i,db in enumerate(src_db_l): + for src_iscope in db.scopes(ScopeTypeT.INSTANCE): + name = src_iscope.getScopeName() + if not name in iscope_m.keys(): + scope_l = [None]*len(src_db_l) + scope_l[i] = src_iscope + iscope_m[name] = scope_l + iscope_name_l.append(name) + else: + iscope_m[name][i] = src_iscope - def _merge_covergroups(self, dst_scope, src_scope): + for name in iscope_name_l: + # We'll create the scope using the first src database + # that it was in + src_scopes = list(filter(lambda e: e is not None, iscope_m[name])) + + src_iscope = src_scopes[0] + + # Create a representation of the scope in the destination + # database + src_du = src_iscope.getInstanceDu() + dst_du = self.dst_db.createScope( + src_du.getScopeName(), + None, + 1, + UCIS_OTHER, # TODO: must query SourceType + UCIS_DU_MODULE, # TODO: must query GetScopeType + UCIS_ENABLED_STMT | UCIS_ENABLED_BRANCH + | UCIS_ENABLED_COND | UCIS_ENABLED_EXPR + | UCIS_ENABLED_FSM | UCIS_ENABLED_TOGGLE + | UCIS_INST_ONCE | UCIS_SCOPE_UNDER_DU) # TODO: GetScopeFlags + + dst_iscope = self.dst_db.createInstance( + src_iscope.getScopeName(), + None, + 1, # weight + UCIS_OTHER, # query SourceType + UCIS_INSTANCE, + dst_du, + UCIS_INST_ONCE) - for src_cg in src_scope.scopes(ScopeTypeT.COVERGROUP): - dst_cg = None + self._merge_covergroups(dst_iscope, src_scopes) - for dst_cg_t in dst_scope.scopes(ScopeTypeT.COVERGROUP): - if dst_cg_t.getScopeName() == src_cg.getScopeName(): - print("Found") - dst_cg = dst_cg_t - break + def _merge_covergroups(self, dst_scope, src_scopes): + + cg_name_m : Dict[str,List] = {} + cg_name_l = [] + + for i,src_scope in enumerate(src_scopes): + for src_cg in src_scope.scopes(ScopeTypeT.COVERGROUP): + name = src_cg.getScopeName() - if dst_cg is not None: - print("TODO: compare covergroups") - else: - dst_cg = dst_scope.createCovergroup( - src_cg.getScopeName(), - None, # location - 1, # weight - UCIS_OTHER) - self._clone_coverpoints(dst_cg, src_cg) - - self._clone_coverinsts(dst_cg, src_cg) - + if name not in cg_name_m.keys(): + scope_l = [None]*len(src_scopes) + cg_name_m[name] = scope_l + cg_name_l.append(name) + cg_name_m[name][i] = src_cg - def _clone_coverpoints(self, dst_cg, src_cg): - for cp in src_cg.scopes(ScopeTypeT.COVERPOINT): - self._clone_coverpoint(dst_cg, cp) - pass + for name in cg_name_l: + src_cg_l = list(filter(lambda cg: cg is not None, cg_name_m[name])) + + # Create the destination using the first covergroup + dst_cg = dst_scope.createCovergroup( + src_cg_l[0].getScopeName(), + None, # location + 1, # weight + UCIS_OTHER) + self._merge_covergroup(dst_cg, src_cg_l) + + def _merge_covergroup(self, dst_cg, src_cg_l): - for cp in src_cg.scopes(ScopeTypeT.CROSS): - self._clone_cross(dst_cg, cp) - pass + dst_cp_m = self._merge_coverpoints(dst_cg, src_cg_l) + + self._merge_coverinsts(dst_cg, src_cg_l) pass - def _clone_coverpoint(self, dst_cg, src_cp): - cp = dst_cg.createCoverpoint( - src_cp.getScopeName(), - None, # location - 1, # weight - UCIS_OTHER) # SourceType + def _merge_coverinsts(self, dst_cg, src_cg_l): + + cg_i_m = {} + cg_n_l = [] - for ci_n in src_cp.coverItems(CoverTypeT.CVGBIN): - cvg_data = ci_n.getCoverData() - cvg_data_c = cp.createBin( - ci_n.getName(), - None, # Location - cvg_data.at_least, - cvg_data.data, - ci_n.getName(), - UCIS_CVGBIN) + for i,src_cg in enumerate(src_cg_l): - for ci_n in src_cp.coverItems(CoverTypeT.IGNOREBIN): - cvg_data = ci_n.getCoverData() - cvg_data_c = cp.createBin( - ci_n.getName(), - None, # Location - cvg_data.at_least, - cvg_data.data, - ci_n.getName(), - UCIS_IGNOREBIN) + for src_cg_i in src_cg.scopes(ScopeTypeT.COVERINSTANCE): + name = src_cg_i.getScopeName() + + if name not in cg_i_m.keys(): + cg_i_m[name] = [None]*len(src_cg_l) + cg_n_l.append(name) + cg_i_m[name][i] = src_cg_i + + for name in cg_n_l: + src_cg_i_l = list(filter(lambda cg : cg is not None, cg_i_m[name])) - for ci_n in src_cp.coverItems(CoverTypeT.ILLEGALBIN): - cvg_data = ci_n.getCoverData() - cvg_data_c = cp.createBin( - ci_n.getName(), - None, # Location - cvg_data.at_least, - cvg_data.data, - ci_n.getName(), - UCIS_ILLEGALBIN) + dst_cg_i = dst_cg.createCoverInstance( + name, + None, # location + 1, # weight + UCIS_OTHER) + self._merge_coverpoints(dst_cg_i, src_cg_i_l) + + def _merge_coverpoints(self, dst_cg, src_cg_l) -> Dict[str,object]: + dst_cp_m : Dict[str, object] = {} + cp_name_m : Dict[str,List] = {} + cp_name_l = [] + + for i,src_cg in enumerate(src_cg_l): + for cp in src_cg.scopes(ScopeTypeT.COVERPOINT): + name = cp.getScopeName() + + if name not in cp_name_m.keys(): + scope_l = [None]*len(src_cg_l) + cp_name_m[name] = scope_l + cp_name_l.append(name) + cp_name_m[name][i] = cp + + for name in cp_name_l: + src_cp_l = list(filter(lambda cp : cp is not None, cp_name_m[name])) + dst_cp = dst_cg.createCoverpoint( + src_cp_l[0].getScopeName(), + None, # location + 1, # weight + UCIS_OTHER) # SourceType + dst_cp_m[name] = dst_cp + + self._merge_coverpoint_bins(dst_cp, src_cp_l) + + return dst_cp_m + + def _merge_coverpoint_bins(self, dst_cp, src_cp_l): + + for bin_t in (CoverTypeT.CVGBIN,CoverTypeT.IGNOREBIN,CoverTypeT.ILLEGALBIN): + bin_name_m : Dict[str,List[int,int]] = {} + bin_name_l = [] + + for src_cp in src_cp_l: + for ci_n in src_cp.coverItems(bin_t): + cvg_data = ci_n.getCoverData() + name = ci_n.getName() + if name not in bin_name_m.keys(): + bin_name_m[name] = [0, cvg_data.at_least] + bin_name_l.append(name) + bin_name_m[name][0] += cvg_data.data + + for name in bin_name_l: + dst_cp.createBin( + name, + None, # Location + bin_name_m[name][1], + bin_name_m[name][0], + name, + bin_t) + def _clone_cross(self, dst_cg, cp): coverpoint_l = [] for i in range(cp.getNumCrossedCoverpoints()): diff --git a/src/ucis/report/coverage_report_builder.py b/src/ucis/report/coverage_report_builder.py index ee3f8ae..4b78324 100644 --- a/src/ucis/report/coverage_report_builder.py +++ b/src/ucis/report/coverage_report_builder.py @@ -131,7 +131,10 @@ def build_coverpoint(self, cp_n : Coverpoint): cvg_data.at_least, cvg_data.data)) - cp_r.coverage = (100*num_hit)/total + if total > 0: + cp_r.coverage = (100*num_hit)/total + else: + cp_r.coverage = 0 return cp_r diff --git a/src/ucis/xml/db_format_if_xml.py b/src/ucis/xml/db_format_if_xml.py index 1a66b9c..a4874fc 100644 --- a/src/ucis/xml/db_format_if_xml.py +++ b/src/ucis/xml/db_format_if_xml.py @@ -4,6 +4,7 @@ @author: mballance ''' from ucis.rgy.format_if_db import FormatIfDb, FormatDescDb, FormatDbFlags +from .xml_ucis import XmlUCIS class DbFormatIfXml(FormatIfDb): @@ -11,7 +12,7 @@ def init(self, options): raise Exception("Options %s not accepted by the XML format" % str(options)) def create(self): - raise Exception("The XML format can only be read and written, not created") + return XmlUCIS() def read(self, file_or_filename) -> 'UCIS': from ucis.xml.xml_factory import XmlFactory diff --git a/src/ucis/xml/xml_ucis.py b/src/ucis/xml/xml_ucis.py new file mode 100644 index 0000000..46eb10e --- /dev/null +++ b/src/ucis/xml/xml_ucis.py @@ -0,0 +1,12 @@ + +from ucis.mem import MemUCIS +from .xml_writer import XmlWriter + +class XmlUCIS(MemUCIS): + + def write(self, file, scope=None, recurse=True, covertype=-1): + + writer = XmlWriter() + + with open(file, "w") as fp: + writer.write(fp, self) diff --git a/src/ucis/xml/xml_writer.py b/src/ucis/xml/xml_writer.py index 5e5bcad..721dfa9 100644 --- a/src/ucis/xml/xml_writer.py +++ b/src/ucis/xml/xml_writer.py @@ -14,7 +14,6 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -from ucis import UCIS_CVGBIN, UCIS_IGNOREBIN, UCIS_ILLEGALBIN ''' Created on Jan 5, 2020 @@ -34,7 +33,6 @@ from ucis.scope import Scope from ucis.scope_type_t import ScopeTypeT from ucis.statement_id import StatementId -from ucis.ucis import UCIS from lxml import etree as et from lxml.etree import QName, tounicode, SubElement @@ -204,11 +202,11 @@ def write_coverpoint_bins(self, cpElem, coveritems : Iterator[CoverIndex]): cpBinElem = self.mkElem(cpElem, "coverpointBin") self.setAttr(cpBinElem, "name", cp_bin.getName()) - if cp_bin.data.type == UCIS_CVGBIN: + if cp_bin.data.type == CoverTypeT.CVGBIN: self.setAttr(cpBinElem, "type", "default") - elif cp_bin.data.type == UCIS_IGNOREBIN: + elif cp_bin.data.type == CoverTypeT.IGNOREBIN: self.setAttr(cpBinElem, "type", "ignore") - elif cp_bin.data.type == UCIS_ILLEGALBIN: + elif cp_bin.data.type == CoverTypeT.ILLEGALBIN: self.setAttr(cpBinElem, "type", "illegal") else: raise Exception("Unknown bin type %s" % str(cp_bin.type)) diff --git a/src/ucis/yaml/db_format_if_yaml.py b/src/ucis/yaml/db_format_if_yaml.py index 4414924..9d24e0c 100644 --- a/src/ucis/yaml/db_format_if_yaml.py +++ b/src/ucis/yaml/db_format_if_yaml.py @@ -4,6 +4,8 @@ @author: mballance ''' from ucis.rgy.format_if_db import FormatIfDb, FormatDescDb, FormatDbFlags +from ucis.yaml.yaml_ucis import YamlUCIS +from .yaml_reader import YamlReader class DbFormatIfYaml(FormatIfDb): @@ -13,12 +15,15 @@ def init(self, options): raise NotImplementedError("DbFormatIf.init not implemented by %s" % str(type(self))) def create(self) -> 'UCIS': - raise NotImplementedError("DbFormatIf.create not implemented by %s" % str(type(self))) + return YamlUCIS() def read(self, filename) -> 'UCIS': + reader = YamlReader() - - raise NotImplementedError("DbFormatIf.read not implemented by %s" % str(type(self))) + with open(filename, "r") as fp: + db = reader.load(fp) + + return db def write(self, db : 'UCIS', file_or_filename): raise NotImplementedError("DbFormatIf.write not implemented by %s" % str(type(self))) diff --git a/src/ucis/yaml/yaml_reader.py b/src/ucis/yaml/yaml_reader.py index ce2f8e4..aba3b0f 100644 --- a/src/ucis/yaml/yaml_reader.py +++ b/src/ucis/yaml/yaml_reader.py @@ -5,12 +5,13 @@ ''' import yaml -from ucis import UCIS_HISTORYNODE_TEST, UCIS_OTHER, UCIS_CVGBIN, UCIS_IGNOREBIN, \ - UCIS_ILLEGALBIN, UCIS_DU_MODULE, UCIS_ENABLED_STMT, UCIS_ENABLED_BRANCH, \ - UCIS_ENABLED_COND, UCIS_ENABLED_EXPR, UCIS_ENABLED_FSM, UCIS_ENABLED_TOGGLE, \ - UCIS_INST_ONCE, UCIS_SCOPE_UNDER_DU, UCIS_INSTANCE +from io import StringIO +from ucis.cover_type_t import CoverTypeT +from ucis.flags_t import FlagsT from ucis.mem.mem_ucis import MemUCIS from ucis.scope import Scope +from ucis.scope_type_t import ScopeTypeT +from ucis.source_t import SourceT from ucis.ucis import UCIS import os @@ -54,11 +55,16 @@ def getCoverageSchema(cls): def loads(self, s) -> UCIS: + fp = StringIO(s) + + return self.load(fp) + + def load(self, fp) -> UCIS: self.cvg_ns = YamlReader.getCoverageNS() schema = YamlReader.getCoverageSchema() - cov_yml = yaml.load(s, Loader=yaml.FullLoader) + cov_yml = yaml.load(fp, Loader=yaml.FullLoader) jsonschema.validate(instance=cov_yml, schema=schema) @@ -74,21 +80,21 @@ def loads(self, s) -> UCIS: self.cg_default_du_name, du_src_info, 1, # weight - UCIS_OTHER, # source language - UCIS_DU_MODULE, - UCIS_ENABLED_STMT | UCIS_ENABLED_BRANCH - | UCIS_ENABLED_COND | UCIS_ENABLED_EXPR - | UCIS_ENABLED_FSM | UCIS_ENABLED_TOGGLE - | UCIS_INST_ONCE | UCIS_SCOPE_UNDER_DU) + SourceT.OTHER, # source language + ScopeTypeT.DU_MODULE, + FlagsT.ENABLED_STMT | FlagsT.ENABLED_BRANCH + | FlagsT.ENABLED_COND | FlagsT.ENABLED_EXPR + | FlagsT.ENABLED_FSM | FlagsT.ENABLED_TOGGLE + | FlagsT.INST_ONCE | FlagsT.SCOPE_UNDER_DU) self.cg_default_inst = self.db.createInstance( self.cg_default_inst_name, None, # sourceinfo 1, # weight - UCIS_OTHER, # source language - UCIS_INSTANCE, + SourceT.OTHER, # source language + ScopeTypeT.INSTANCE, self.cg_default_du, - UCIS_INST_ONCE) + FlagsT.INST_ONCE) coverage = coverage.coverage @@ -117,13 +123,13 @@ def process_covergroup(self, cg_t): for cg_i in cg_t.instances: if cg_i.coverpoints is not None: for cp in cg_i.coverpoints: - if cp.name not in cp_t_m.keys(): + if str(cp.name) not in cp_t_m.keys(): cp_t = self.cvg_ns.Coverpoint() - cp_t.name = cp.name - cp_t_m[cp.name] = cp_t + cp_t.name = str(cp.name) + cp_t_m[cp_t.name] = cp_t cp_t_l.append(cp_t) else: - cp_t = cp_t_m[cp.name] + cp_t = cp_t_m[str(cp.name)] # Now, do bins... if cp.bins is not None: @@ -138,7 +144,7 @@ def process_covergroup(self, cg_t): break if t_b is None: t_b = self.cvg_ns.CoverageBin() - t_b.name = b.name + t_b.name = str(b.name) t_b.count = 0 cp_t.bins.append(t_b) t_b.count += b.count @@ -155,7 +161,7 @@ def process_covergroup(self, cg_t): break if t_b is None: t_b = self.cvg_ns.CoverageBin() - t_b.name = b.name + t_b.name = str(b.name) t_b.count = 0 cp_t.bins.append(t_b) t_b.count += b.count @@ -172,7 +178,7 @@ def process_covergroup(self, cg_t): break if t_b is None: t_b = self.cvg_ns.CoverageBin() - t_b.name = b.name + t_b.name = str(b.name) t_b.count = 0 cp_t.bins.append(t_b) t_b.count += b.count @@ -181,50 +187,50 @@ def process_covergroup(self, cg_t): for cr in cg_i.crosses: if cr.coverpoints is None: raise Exception("Cross %s in covergroup instance %s doesn't specify coverpoints" % ( - cr.name, cg_i.name)) - if cr.name not in cr_t_m.keys(): + str(cr.name), str(cg_i.name))) + if str(cr.name) not in cr_t_m.keys(): cr_t = self.cvg_ns.Cross() - cr_t.name = cr.name + cr_t.name = str(cr.name) cr_t.coverpoints = [] for cr_cp in cr.coverpoints: cr_t.coverpoints.append(cr_cp) cr_t_bins_m = {} - cr_t_m[cr.name] = (cr_t, cr_t_bins_m) + cr_t_m[str(cr.name)] = (cr_t, cr_t_bins_m) cr_t_l.append(cr_t) if cr.bins is not None: cr_t.bins = [] else: - cr_t = cr_t_m[cr.name][0] - cr_t_bins_m = cr_t_m[cr.name][1] + cr_t = cr_t_m[str(cr.name)][0] + cr_t_bins_m = cr_t_m[str(cr.name)][1] # Now, proceed to aggregate bins if cr.bins is not None: for b in cr.bins: if b.name in cr_t_bins_m.keys(): - cr_t_bins_m[b.name].count += b.count + cr_t_bins_m[str(b.name)].count += b.count else: b_t = self.cvg_ns.CoverageBin() - b_t.name = b.name + b_t.name = str(b.name) b_t.count = b.count cr_t.bins.append(b_t) - cr_t_bins_m[b.name] = b_t + cr_t_bins_m[str(b.name)] = b_t else: - print("Warning: Covergroup type %s has no instances" % cg_t.name) + print("Warning: Covergroup type %s has no instances" % str(cg_t.name)) # Now, convert to UCIS cg_t_scope = self.cg_default_inst.createCovergroup( - cg_t.name, + str(cg_t.name), cg_location, weight, - UCIS_OTHER) + SourceT.OTHER) cp_n_scope_m = {} # Create type coverpoints and crosses for cp in cp_t_l: - cp_n_scope_m[cp.name] = self.record_coverpoint(cg_t_scope, cp) + cp_n_scope_m[str(cp.name)] = self.record_coverpoint(cg_t_scope, cp) for cr in cr_t_l: cp_l = [] @@ -234,7 +240,7 @@ def process_covergroup(self, cg_t): cp_l.append(cp_n_scope_m[cp]) else: raise Exception("Coverpoint %s referenced in cross %s is not defined" % ( - cp, cr.name)) + cp, str(cr.name))) self.record_cross(cg_t_scope, cp_l, cr) @@ -242,14 +248,14 @@ def process_covergroup(self, cg_t): cp_n_scope_m = {} for i in cg_t.instances: cg_i_scope = cg_t_scope.createCoverInstance( - i.name, + str(i.name), cg_location, weight, - UCIS_OTHER) + SourceT.OTHER) if i.coverpoints is not None: for cp in i.coverpoints: - cp_n_scope_m[cp.name] = self.record_coverpoint(cg_i_scope, cp) + cp_n_scope_m[str(cp.name)] = self.record_coverpoint(cg_i_scope, cp) if i.crosses is not None: for cr in i.crosses: @@ -260,7 +266,7 @@ def process_covergroup(self, cg_t): cp_l.append(cp_n_scope_m[cp]) else: raise Exception("Coverpoint %s referenced in cross %s is not defined" % ( - cp, cr.name)) + cp, str(cr.name))) self.record_cross(cg_i_scope, cp_l, cr) @@ -277,40 +283,40 @@ def record_coverpoint(self, cp_parent_s, cp): at_least = cp.atleast cp_scope = cp_parent_s.createCoverpoint( - cp.name, + str(cp.name), cp_location, weight, - UCIS_OTHER) + SourceT.OTHER) if cp.bins is not None: for b in cp.bins: cp_scope.createBin( - b.name, + str(b.name), cp_location, at_least, b.count, - b.name, - UCIS_CVGBIN) + str(b.name), + CoverTypeT.CVGBIN) if cp.ignorebins is not None: for b in cp.ignorebins: cp_scope.createBin( - b.name, + str(b.name), cp_location, at_least, b.count, - b.name, - UCIS_IGNOREBIN) + str(b.name), + CoverTypeT.IGNOREBIN) if cp.illegalbins is not None: for b in cp.illegalbins: cp_scope.createBin( - b.name, + str(b.name), cp_location, at_least, b.count, - b.name, - UCIS_ILLEGALBIN) + str(b.name), + CoverTypeT.ILLEGALBIN) return cp_scope @@ -326,19 +332,19 @@ def record_cross(self, cp_parent_s, cp_l, cr): at_least = cr.atleast cr_scope = cp_parent_s.createCross( - cr.name, + str(cr.name), cr_location, weight, - UCIS_OTHER, + SourceT.OTHER, cp_l) if cr.bins is not None: for b in cr.bins: cr_scope.createBin( - b.name, + str(b.name), cr_location, at_least, b.count, - b.name) + str(b.name)) return cr_scope diff --git a/src/ucis/yaml/yaml_ucis.py b/src/ucis/yaml/yaml_ucis.py new file mode 100644 index 0000000..04fc463 --- /dev/null +++ b/src/ucis/yaml/yaml_ucis.py @@ -0,0 +1,7 @@ + +from ucis.mem import MemUCIS + +class YamlUCIS(MemUCIS): + + def write(self, file, scope=None, recurse=True, covertype=-1): + raise Exception("YamlUCIS.write not implemented") \ No newline at end of file diff --git a/ve/unit/runtest.sh b/ve/unit/runtest.sh new file mode 100755 index 0000000..5c567b6 --- /dev/null +++ b/ve/unit/runtest.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +ROOTDIR=../.. + +PYTHON=${ROOTDIR}/packages/python/bin/python3 + +export PYTHONPATH=${ROOTDIR}/src + +${PYTHON} -m unittest diff --git a/ve/unit/test_merge.py b/ve/unit/test_merge.py index c6deb9a..115432c 100644 --- a/ve/unit/test_merge.py +++ b/ve/unit/test_merge.py @@ -3,7 +3,7 @@ @author: mballance ''' -from _io import StringIO +from io import StringIO from unittest.case import TestCase from ucis.mem.mem_factory import MemFactory @@ -19,18 +19,19 @@ def test_null_type_cg_1_cp(self): text = """ coverage: covergroups: - - type-name: cvg - - coverpoints: - - name: cp1 - bins: - - name: b0 - count: 1 - - name: b1 - count: 0 + - name: cvg + instances: + - name: i1 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 1 + - name: b1 + count: 0 """ - src_db = YamlReader().loads(StringIO(text)) + src_db = YamlReader().loads(text) rpt = CoverageReportBuilder.build(src_db) self.assertEqual(len(rpt.covergroups), 1) @@ -41,8 +42,8 @@ def test_null_type_cg_1_cp(self): dst_db = MemFactory.create() - merger = DbMerger(dst_db) - merger.merge(src_db) + merger = DbMerger() + merger.merge(dst_db, [src_db]) rpt = CoverageReportBuilder.build(dst_db) @@ -56,18 +57,10 @@ def test_null_inst_cg_1_cp(self): text = """ coverage: covergroups: - - type-name: cvg - - coverpoints: - - name: cp1 - bins: - - name: b0 - count: 1 - - name: b1 - count: 1 - covergroups: - - type-name: cvg - inst-name: inst1 + - name: cvg + + instances: + - name: inst1 coverpoints: - name: cp1 @@ -77,8 +70,7 @@ def test_null_inst_cg_1_cp(self): - name: b1 count: 1 - - type-name: cvg - inst-name: inst2 + - name: inst2 coverpoints: - name: cp1 @@ -89,7 +81,7 @@ def test_null_inst_cg_1_cp(self): count: 0 """ - src_db = YamlReader().loads(StringIO(text)) + src_db = YamlReader().loads(text) rpt = CoverageReportBuilder.build(src_db) self.assertEqual(len(rpt.covergroups), 1) @@ -111,8 +103,8 @@ def test_null_inst_cg_1_cp(self): dst_db = MemFactory.create() - merger = DbMerger(dst_db) - merger.merge(src_db) + merger = DbMerger() + merger.merge(dst_db, [src_db]) rpt = CoverageReportBuilder.build(dst_db) @@ -132,6 +124,271 @@ def test_null_inst_cg_1_cp(self): self.assertEqual(rpt.covergroups[0].covergroups[1].coverpoints[0].coverage, 50.0) + def test_2db_1t_2i(self): + cvg1 = """ + coverage: + covergroups: + - name: cvg + + instances: + - name: inst1 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 0 + - name: b1 + count: 1 + + """ + + cvg2 = """ + coverage: + covergroups: + - name: cvg + + instances: + - name: inst2 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 1 + - name: b1 + count: 0 + """ + + src1_db = YamlReader().loads(cvg1) + src2_db = YamlReader().loads(cvg2) + + dst_db = MemFactory.create() + merger = DbMerger() + merger.merge(dst_db, [src1_db, src2_db]) + + rpt = CoverageReportBuilder.build(dst_db) + + self.assertEqual(len(rpt.covergroups), 1) + self.assertEqual(rpt.covergroups[0].name, "cvg") + self.assertEqual(len(rpt.covergroups[0].coverpoints), 1) + self.assertEqual(len(rpt.covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].coverpoints[0].coverage, 100.0) + self.assertEqual(len(rpt.covergroups[0].covergroups), 2) +# self.assertEqual(rpt.covergroups[0].name, "cvg") + self.assertEqual(len(rpt.covergroups[0].covergroups[0].coverpoints), 1) + self.assertEqual(len(rpt.covergroups[0].covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].covergroups[0].coverpoints[0].coverage, 50.0) + + self.assertEqual(len(rpt.covergroups[0].covergroups[1].coverpoints), 1) + self.assertEqual(len(rpt.covergroups[0].covergroups[1].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].covergroups[1].coverpoints[0].coverage, 50.0) + def test_2db_1t_1i(self): + cvg1 = """ + coverage: + covergroups: + - name: cvg + + instances: + - name: inst1 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 0 + - name: b1 + count: 1 + + """ + + cvg2 = """ + coverage: + covergroups: + - name: cvg + + instances: + - name: inst1 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 1 + - name: b1 + count: 0 + """ + + src1_db = YamlReader().loads(cvg1) + src2_db = YamlReader().loads(cvg2) + + dst_db = MemFactory.create() + + merger = DbMerger() + merger.merge(dst_db, [src1_db, src2_db]) + + rpt = CoverageReportBuilder.build(dst_db) + + self.assertEqual(len(rpt.covergroups), 1) + self.assertEqual(rpt.covergroups[0].name, "cvg") + self.assertEqual(len(rpt.covergroups[0].coverpoints), 1) + self.assertEqual(len(rpt.covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].coverpoints[0].coverage, 100.0) + self.assertEqual(len(rpt.covergroups[0].covergroups), 1) + + self.assertEqual(len(rpt.covergroups[0].covergroups[0].coverpoints), 1) + self.assertEqual(len(rpt.covergroups[0].covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].covergroups[0].coverpoints[0].coverage, 100.0) + + def test_3db_1t_2i(self): + cvg1 = """ + coverage: + covergroups: + - name: cvg + + instances: + - name: inst1 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 0 + - name: b1 + count: 1 + + """ + + cvg2 = """ + coverage: + covergroups: + - name: cvg + + instances: + - name: inst1 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 1 + - name: b1 + count: 0 + """ + + cvg3 = """ + coverage: + covergroups: + - name: cvg + + instances: + - name: inst2 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 1 + - name: b1 + count: 0 + """ + + src1_db = YamlReader().loads(cvg1) + src2_db = YamlReader().loads(cvg2) + src3_db = YamlReader().loads(cvg3) + + dst_db = MemFactory.create() + + merger = DbMerger() + merger.merge(dst_db, [src1_db, src2_db, src3_db]) + + rpt = CoverageReportBuilder.build(dst_db) + + self.assertEqual(len(rpt.covergroups), 1) + self.assertEqual(rpt.covergroups[0].name, "cvg") + self.assertEqual(len(rpt.covergroups[0].coverpoints), 1) + self.assertEqual(len(rpt.covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].coverpoints[0].coverage, 100.0) + self.assertEqual(len(rpt.covergroups[0].covergroups), 2) + + self.assertEqual(len(rpt.covergroups[0].covergroups[0].coverpoints), 1) + self.assertEqual(len(rpt.covergroups[0].covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].covergroups[0].coverpoints[0].coverage, 100.0) + + self.assertEqual(len(rpt.covergroups[0].covergroups[1].coverpoints), 1) + self.assertEqual(len(rpt.covergroups[0].covergroups[1].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].covergroups[1].coverpoints[0].coverage, 50.0) + + def test_3db_2t_1i(self): + cvg1 = """ + coverage: + covergroups: + - name: cvg + + instances: + - name: inst1 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 0 + - name: b1 + count: 1 + + """ + + cvg2 = """ + coverage: + covergroups: + - name: cvg + + instances: + - name: inst1 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 1 + - name: b1 + count: 0 + """ + + cvg3 = """ + coverage: + covergroups: + - name: cvg2 + + instances: + - name: inst1 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 1 + - name: b1 + count: 0 + """ + + src1_db = YamlReader().loads(cvg1) + src2_db = YamlReader().loads(cvg2) + src3_db = YamlReader().loads(cvg3) + + dst_db = MemFactory.create() + + merger = DbMerger() + merger.merge(dst_db, [src1_db, src2_db, src3_db]) + + rpt = CoverageReportBuilder.build(dst_db) + + self.assertEqual(len(rpt.covergroups), 2) + self.assertEqual(rpt.covergroups[0].name, "cvg") + self.assertEqual(len(rpt.covergroups[0].coverpoints), 1) + self.assertEqual(len(rpt.covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].coverpoints[0].coverage, 100.0) + self.assertEqual(len(rpt.covergroups[0].covergroups), 1) + + self.assertEqual(len(rpt.covergroups[0].covergroups[0].coverpoints), 1) + self.assertEqual(len(rpt.covergroups[0].covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].covergroups[0].coverpoints[0].coverage, 100.0) + + self.assertEqual(rpt.covergroups[1].name, "cvg2") + self.assertEqual(len(rpt.covergroups[1].covergroups), 1) + self.assertEqual(len(rpt.covergroups[1].covergroups[0].coverpoints), 1) + self.assertEqual(len(rpt.covergroups[1].covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[1].covergroups[0].coverpoints[0].coverage, 50.0) \ No newline at end of file diff --git a/ve/unit/test_yaml_reader.py b/ve/unit/test_yaml_reader.py index 4753c32..7926bde 100644 --- a/ve/unit/test_yaml_reader.py +++ b/ve/unit/test_yaml_reader.py @@ -42,7 +42,7 @@ def test_smoke(self): """ - db = YamlReader().loads(StringIO(text)) + db = YamlReader().loads(text) rpt = CoverageReportBuilder.build(db) print("rpt=%s" % str(rpt)) @@ -51,7 +51,7 @@ def test_smoke(self): renderer.details = True renderer.report() - self.assertEqual(round(rpt.covergroups[0].coverage, 2), 66.67) + self.assertEqual(round(rpt.covergroups[0].coverage, 2), 100.0) self.assertEqual(round(rpt.covergroups[0].covergroups[0].coverage, 2), 50.0) self.assertEqual(round(rpt.covergroups[0].covergroups[1].coverage, 2), 50.0) @@ -91,7 +91,7 @@ def test_cross_smoke(self): - name: count: 0 """ - db = YamlReader().loads(StringIO(text)) + db = YamlReader().loads(text) rpt = CoverageReportBuilder.build(db) print("rpt=%s" % str(rpt)) @@ -108,19 +108,20 @@ def test_cross_smoke(self): def test_type_cvg_cvp(self): text = """ coverage: - covergroups: - - type-name: cvg - - coverpoints: - - name: cp1 - bins: - - name: b0 - count: 1 - - name: b1 - count: 0 + covergroups: + - name: cvg + instances: + - name: i1 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 1 + - name: b1 + count: 0 """ - db = YamlReader().loads(StringIO(text)) + db = YamlReader().loads(text) rpt = CoverageReportBuilder.build(db) self.assertEqual(len(rpt.covergroups), 1) @@ -132,25 +133,26 @@ def test_type_cvg_cvp(self): def test_type_cvg_2_cvp(self): text = """ coverage: - covergroups: - - type-name: cvg - - coverpoints: - - name: cp1 - bins: - - name: b0 - count: 1 - - name: b1 - count: 0 - - name: cp2 - bins: - - name: b0 - count: 1 - - name: b1 - count: 1 + covergroups: + - name: cvg + instances: + - name: i1 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 1 + - name: b1 + count: 0 + - name: cp2 + bins: + - name: b0 + count: 1 + - name: b1 + count: 1 """ - db = YamlReader().loads(StringIO(text)) + db = YamlReader().loads(text) rpt = CoverageReportBuilder.build(db) self.assertEqual(len(rpt.covergroups), 1) From 1fd91e767310209e90f4da37e88a8c59f6d6ab76 Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Sat, 9 Jul 2022 09:58:43 -0700 Subject: [PATCH 3/9] Update unit-test run process Signed-off-by: Matthew Ballance --- .github/workflows/ci.yml | 10 ++------ ve/unit/runtest.sh | 3 ++- ve/unit/test_lib_examples.py | 39 ----------------------------- ve/unit/test_sqlite.py | 48 ------------------------------------ 4 files changed, 4 insertions(+), 96 deletions(-) delete mode 100644 ve/unit/test_lib_examples.py delete mode 100644 ve/unit/test_sqlite.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6f8de5..5e73a6b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,14 +23,8 @@ jobs: ./packages/python/bin/python3 setup.py bdist_wheel --universal - name: Run Tests run: | - export PYTHONPATH=`pwd`/src - tests="$tests ve/unit/test_coverage_report.py" - tests="$tests ve/unit/test_report.py" - tests="$tests ve/unit/test_simple_create.py" - tests="$tests ve/unit/test_xml_examples.py" - tests="$tests ve/unit/test_xml_output.py" - tests="$tests ve/unit/test_xml_reader.py" - ./packages/python/bin/python3 -m pytest --no-cov -v $tests + cd ve/unit + /bin/bash runtest.sh - name: Build Docs run: | make html diff --git a/ve/unit/runtest.sh b/ve/unit/runtest.sh index 5c567b6..3f0ad5c 100755 --- a/ve/unit/runtest.sh +++ b/ve/unit/runtest.sh @@ -6,4 +6,5 @@ PYTHON=${ROOTDIR}/packages/python/bin/python3 export PYTHONPATH=${ROOTDIR}/src -${PYTHON} -m unittest +${PYTHON} -m unittest -v + diff --git a/ve/unit/test_lib_examples.py b/ve/unit/test_lib_examples.py deleted file mode 100644 index 468fce8..0000000 --- a/ve/unit/test_lib_examples.py +++ /dev/null @@ -1,39 +0,0 @@ - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - - -import os -from unittest.case import TestCase -from ucis.mem.mem_factory import MemFactory -from ucis.source_info import SourceInfo -from ucis.scope import Scope -from ucis.test_data import TestData -from ucis import * -from ucis.lib.LibFactory import LibFactory -import example_create_ucis - -class TestUcisExamples(TestCase): - - def setUp(self): - LibFactory.load_ucis_library("libucis.so") - - def disabled_test_create_ucis(self): - db = LibFactory.create() - example_create_ucis.create_ucis(db) - db.write("file.ucis", None, True, -1) - db.close() diff --git a/ve/unit/test_sqlite.py b/ve/unit/test_sqlite.py deleted file mode 100644 index 9541625..0000000 --- a/ve/unit/test_sqlite.py +++ /dev/null @@ -1,48 +0,0 @@ -''' -Created on Mar 24, 2020 - -@author: ballance -''' -from unittest.case import TestCase - -class TestSQLite(TestCase): - - def disabled_test_smoke(self): - import sqlite3 - - conn = sqlite3.connect("my_db.scdb") - - print("conn=" + str(conn)) - - - with conn: - c = conn.cursor() - c.execute(""" - CREATE TABLE IF NOT EXISTS scopes ( - id integer PRIMARY KEY, - parent integer, - name text NOT NULL, - type integer, - flags integer - ); - """) - - c.execute(""" - INSERT INTO scopes(parent,name,type,flags) - VALUES(?,?,?,?) - """, (-1, "foo", 1, 0)) - - c.execute(""" - INSERT INTO scopes(parent,name,type,flags) - VALUES(-1,"bar",1, 0) - """) - - c.execute(""" - INSERT INTO scopes(parent,name,type,flags) - VALUES(?,?,?,?)""", (1, "baz", 1, 0)) - - # Retrieve all top-level scopes - c.execute("""SELECT * from scopes where parent=-1""") - print("rows=" + str(c.fetchall())) - - \ No newline at end of file From 7e62d7a83bb4915d14b1b55c522f88dfaf9532be Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Sun, 10 Jul 2022 09:40:48 -0700 Subject: [PATCH 4/9] Add env file Signed-off-by: Matthew Ballance --- .env | 1 + .gitignore | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..c80eeee --- /dev/null +++ b/.env @@ -0,0 +1 @@ +PYTHONPATH=./src \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7647bdc..31ccf5d 100644 --- a/.gitignore +++ b/.gitignore @@ -100,7 +100,6 @@ celerybeat.pid *.sage.py # Environments -.env .venv env/ venv/ From 728e7035bd333fd93e0cbd7d3baf5c3ede715b51 Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Sun, 10 Jul 2022 19:34:41 -0700 Subject: [PATCH 5/9] Add support or merging crosses Signed-off-by: Matthew Ballance --- doc/source/commands.rst | 12 ++ doc/source/index.rst | 1 + src/ucis/__main__.py | 10 +- src/ucis/merge/db_merger.py | 65 ++++++++- ve/unit/test_merge.py | 254 +++++++++++++++++++++++++++++++++++- 5 files changed, 338 insertions(+), 4 deletions(-) create mode 100644 doc/source/commands.rst diff --git a/doc/source/commands.rst b/doc/source/commands.rst new file mode 100644 index 0000000..8c8d77a --- /dev/null +++ b/doc/source/commands.rst @@ -0,0 +1,12 @@ +######## +Commands +######## + +The `ucis` root command accepts one of several sub-commands that operate on +coverage data. + +.. argparse:: + :module: ucis.__main__ + :func: get_parser + :prog: ucis + diff --git a/doc/source/index.rst b/doc/source/index.rst index d1b6dda..c3c0dfe 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -7,6 +7,7 @@ Contents: :maxdepth: 2 introduction + commands reference/reference diff --git a/src/ucis/__main__.py b/src/ucis/__main__.py index 6964d35..7b8c48f 100644 --- a/src/ucis/__main__.py +++ b/src/ucis/__main__.py @@ -16,7 +16,10 @@ def get_parser(): subparser = parser.add_subparsers() subparser.required = True - convert = subparser.add_parser("convert") + convert = subparser.add_parser("convert", + help=""" + Converts coverage data from one format to another + """) convert.add_argument("--out", "-o", help="Specifies the output of the conversion", required=True) @@ -27,7 +30,10 @@ def get_parser(): convert.add_argument("input", help="Source database to convert") convert.set_defaults(func=cmd_convert.convert) - merge = subparser.add_parser("merge") + merge = subparser.add_parser("merge", + help=""" + Merges coverage data from two or more databases into a single merged database + """) merge.add_argument("--out", "-o", help="Specifies the output of the merge", required=True) diff --git a/src/ucis/merge/db_merger.py b/src/ucis/merge/db_merger.py index aa4f4e1..1472090 100644 --- a/src/ucis/merge/db_merger.py +++ b/src/ucis/merge/db_merger.py @@ -105,6 +105,8 @@ def _merge_covergroups(self, dst_scope, src_scopes): def _merge_covergroup(self, dst_cg, src_cg_l): dst_cp_m = self._merge_coverpoints(dst_cg, src_cg_l) + + self._merge_crosses(dst_cg, dst_cp_m, src_cg_l) self._merge_coverinsts(dst_cg, src_cg_l) @@ -133,7 +135,9 @@ def _merge_coverinsts(self, dst_cg, src_cg_l): None, # location 1, # weight UCIS_OTHER) - self._merge_coverpoints(dst_cg_i, src_cg_i_l) + dst_cp_m = self._merge_coverpoints(dst_cg_i, src_cg_i_l) + + self._merge_crosses(dst_cg_i, dst_cp_m, src_cg_i_l) def _merge_coverpoints(self, dst_cg, src_cg_l) -> Dict[str,object]: dst_cp_m : Dict[str, object] = {} @@ -187,6 +191,65 @@ def _merge_coverpoint_bins(self, dst_cp, src_cp_l): bin_name_m[name][0], name, bin_t) + + def _merge_crosses(self, dst_cg, dst_cp_m, src_cg_l): + + cross_m = {} + cross_name_l = [] + + for i,src_cg in enumerate(src_cg_l): + for cr in src_cg.scopes(ScopeTypeT.CROSS): + name = cr.getScopeName() + if name not in cross_m.keys(): + cross_m[name] = [] + cross_name_l.append(name) + cross_m[name].append(cr) + + for name in cross_name_l: + src_cr_l = cross_m[name] + + # Create the destination cross + coverpoint_l = [] + for i in range(src_cr_l[0].getNumCrossedCoverpoints()): + src_cp = src_cr_l[0].getIthCrossedCoverpoint(i) + if src_cp.getScopeName() in dst_cp_m.keys(): + coverpoint_l.append(dst_cp_m[src_cp.getScopeName()]) + else: + raise Exception("Cannot find coverpoint %s when creating cross %s" % ( + src_cp.getName(), name)) + + dst_cr = dst_cg.createCross( + name, + None, + 1, # weight + UCIS_OTHER, + coverpoint_l) + + self._merge_cross(dst_cr, src_cr_l) + + def _merge_cross(self, dst_cr, src_cr_l): + + for cvg_t in (CoverTypeT.CVGBIN,CoverTypeT.IGNOREBIN,CoverTypeT.ILLEGALBIN): + bin_name_m = {} + bin_name_l = [] + + for src_cr in src_cr_l: + for ci in src_cr.coverItems(cvg_t): + bin_n = ci.getName() + cvg_data = ci.getCoverData() + if bin_n not in bin_name_m.keys(): + bin_name_m[bin_n] = [0, cvg_data.at_least] + bin_name_l.append(bin_n) + bin_name_m[bin_n][0] += cvg_data.data + + for bin_n in bin_name_l: + dst_cr.createBin( + bin_n, + None, # Location + bin_name_m[bin_n][1], # at_least + bin_name_m[bin_n][0], # count + bin_n, + cvg_t) def _clone_cross(self, dst_cg, cp): coverpoint_l = [] diff --git a/ve/unit/test_merge.py b/ve/unit/test_merge.py index 115432c..02b22af 100644 --- a/ve/unit/test_merge.py +++ b/ve/unit/test_merge.py @@ -3,12 +3,14 @@ @author: mballance ''' +import sys from io import StringIO from unittest.case import TestCase from ucis.mem.mem_factory import MemFactory from ucis.mem.mem_ucis import MemUCIS from ucis.report.coverage_report_builder import CoverageReportBuilder +from ucis.report.text_coverage_report_formatter import TextCoverageReportFormatter from ucis.yaml.yaml_reader import YamlReader from ucis.merge.db_merger import DbMerger @@ -391,4 +393,254 @@ def test_3db_2t_1i(self): self.assertEqual(len(rpt.covergroups[1].covergroups[0].coverpoints), 1) self.assertEqual(len(rpt.covergroups[1].covergroups[0].coverpoints[0].bins), 2) self.assertEqual(rpt.covergroups[1].covergroups[0].coverpoints[0].coverage, 50.0) - \ No newline at end of file + + def test_1db_2ci_2cp_1cr(self): + text = """ + coverage: + covergroups: + - name: cvg + + instances: + - name: inst1 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 0 + - name: b1 + count: 1 + - name: cp2 + bins: + - name: b0 + count: 0 + - name: b1 + count: 1 + crosses: + - name: cp1xc2 + coverpoints: ["cp1", "cp2"] + bins: + - name: "" + count: 0 + - name: "" + count: 0 + - name: "" + count: 0 + - name: "" + count: 1 + + - name: inst2 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 1 + - name: b1 + count: 0 + - name: cp2 + bins: + - name: b0 + count: 1 + - name: b1 + count: 0 + crosses: + - name: cp1xc2 + coverpoints: ["cp1", "cp2"] + bins: + - name: "" + count: 1 + - name: "" + count: 0 + - name: "" + count: 0 + - name: "" + count: 0 + """ + + src_db = YamlReader().loads(text) + rpt = CoverageReportBuilder.build(src_db) + + self.assertEqual(len(rpt.covergroups), 1) + self.assertEqual(rpt.covergroups[0].name, "cvg") + self.assertEqual(len(rpt.covergroups[0].coverpoints), 2) + self.assertEqual(len(rpt.covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].coverpoints[0].coverage, 100.0) + self.assertEqual(len(rpt.covergroups[0].crosses), 1) + self.assertEqual(len(rpt.covergroups[0].crosses[0].bins), 4) + self.assertEqual(rpt.covergroups[0].crosses[0].coverage, 50.0) + + self.assertEqual(len(rpt.covergroups[0].covergroups), 2) +# self.assertEqual(rpt.covergroups[0].name, "cvg") + self.assertEqual(len(rpt.covergroups[0].covergroups[0].coverpoints), 2) + self.assertEqual(len(rpt.covergroups[0].covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].covergroups[0].coverpoints[0].coverage, 50.0) + self.assertEqual(len(rpt.covergroups[0].covergroups[0].coverpoints[1].bins), 2) + self.assertEqual(rpt.covergroups[0].covergroups[0].coverpoints[1].coverage, 50.0) + self.assertEqual(len(rpt.covergroups[0].covergroups[0].crosses), 1) + self.assertEqual(len(rpt.covergroups[0].covergroups[0].crosses[0].bins), 4) + self.assertEqual(round(rpt.covergroups[0].covergroups[0].crosses[0].coverage,2), 25.0) + +# self.assertEqual(rpt.covergroups[0].name, "cvg") + self.assertEqual(len(rpt.covergroups[0].covergroups[1].coverpoints), 2) + self.assertEqual(len(rpt.covergroups[0].covergroups[1].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].covergroups[1].coverpoints[0].coverage, 50.0) + + dst_db = MemFactory.create() + + merger = DbMerger() + merger.merge(dst_db, [src_db]) + + rpt = CoverageReportBuilder.build(dst_db) + + self.assertEqual(len(rpt.covergroups), 1) + self.assertEqual(rpt.covergroups[0].name, "cvg") + self.assertEqual(len(rpt.covergroups[0].coverpoints), 2) + self.assertEqual(len(rpt.covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].coverpoints[0].coverage, 100.0) + self.assertEqual(len(rpt.covergroups[0].covergroups), 2) +# self.assertEqual(rpt.covergroups[0].name, "cvg") + self.assertEqual(len(rpt.covergroups[0].covergroups[0].coverpoints), 2) + self.assertEqual(len(rpt.covergroups[0].covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].covergroups[0].coverpoints[0].coverage, 50.0) + + self.assertEqual(len(rpt.covergroups[0].covergroups[1].coverpoints), 2) + self.assertEqual(len(rpt.covergroups[0].covergroups[1].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].covergroups[1].coverpoints[0].coverage, 50.0) + + + def test_2db_2ci_2cp_1cr(self): + db1_src = """ + coverage: + covergroups: + - name: cvg + instances: + - name: inst1 + coverpoints: + - name: cp1 + bins: + - { name: b0, count: 1 } + - { name: b1, count: 0 } + - name: cp2 + bins: + - { name: b0, count: 1 } + - { name: b1, count: 0 } + crosses: + - name: cp1xc2 + coverpoints: ["cp1", "cp2"] + bins: + - { name: "", count: 1 } + - { name: "", count: 0 } + - { name: "", count: 0 } + - { name: "", count: 0 } + + - name: inst2 + coverpoints: + - name: cp1 + bins: + - name: b0 + count: 1 + - name: b1 + count: 0 + - name: cp2 + bins: + - name: b0 + count: 0 + - name: b1 + count: 1 + crosses: + - name: cp1xc2 + coverpoints: ["cp1", "cp2"] + bins: + - { name: "", count: 0 } + - { name: "", count: 1 } + - { name: "", count: 0 } + - { name: "", count: 0 } + """ + + db2_src = """ + coverage: + covergroups: + - name: cvg + instances: + - name: inst1 + coverpoints: + - name: cp1 + bins: + - { name: b0, count: 0 } + - { name: b1, count: 1 } + - name: cp2 + bins: + - { name: b0, count: 1 } + - { name: b1, count: 0 } + crosses: + - name: cp1xc2 + coverpoints: ["cp1", "cp2"] + bins: + - { name: "", count: 0 } + - { name: "", count: 0 } + - { name: "", count: 1 } + - { name: "", count: 0 } + + - name: inst2 + coverpoints: + - name: cp1 + bins: + - { name: b0, count: 0 } + - { name: b1, count: 1 } + - name: cp2 + bins: + - { name: b0, count: 0 } + - { name: b1, count: 1 } + crosses: + - name: cp1xc2 + coverpoints: ["cp1", "cp2"] + bins: + - { name: "", count: 0 } + - { name: "", count: 0 } + - { name: "", count: 0 } + - { name: "", count: 1 } + """ + + src_db = [] + src_db.append(YamlReader().loads(db1_src)) + src_db.append(YamlReader().loads(db2_src)) + + rpt = CoverageReportBuilder.build(src_db[0]) + self.assertEqual(len(rpt.covergroups), 1) + self.assertEqual(rpt.covergroups[0].name, "cvg") + self.assertEqual(len(rpt.covergroups[0].coverpoints), 2) + self.assertEqual(len(rpt.covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].coverpoints[0].coverage, 50.0) + self.assertEqual(len(rpt.covergroups[0].crosses), 1) + + dst_db = MemFactory.create() + + merger = DbMerger() + merger.merge(dst_db, src_db) + + rpt = CoverageReportBuilder.build(dst_db) +# formatter = TextCoverageReportFormatter(rpt, sys.stdout) +# formatter.details = True +# formatter.report() + + self.assertEqual(len(rpt.covergroups), 1) + self.assertEqual(rpt.covergroups[0].name, "cvg") + self.assertEqual(len(rpt.covergroups[0].coverpoints), 2) + self.assertEqual(len(rpt.covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].coverpoints[0].coverage, 100.0) + self.assertEqual(len(rpt.covergroups[0].crosses), 1) + self.assertEqual(rpt.covergroups[0].crosses[0].coverage, 100.0) + + # inst1 + self.assertEqual(len(rpt.covergroups[0].covergroups), 2) + self.assertEqual(rpt.covergroups[0].covergroups[0].name, "inst1") + self.assertEqual(len(rpt.covergroups[0].covergroups[0].coverpoints), 2) + self.assertEqual(len(rpt.covergroups[0].covergroups[0].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].covergroups[0].coverpoints[0].coverage, 100.0) + self.assertEqual(rpt.covergroups[0].covergroups[0].coverpoints[1].coverage, 50.0) + self.assertEqual(len(rpt.covergroups[0].covergroups[0].crosses), 1) + self.assertEqual(rpt.covergroups[0].covergroups[0].crosses[0].coverage, 50.0) + + self.assertEqual(len(rpt.covergroups[0].covergroups[1].coverpoints), 2) + self.assertEqual(len(rpt.covergroups[0].covergroups[1].coverpoints[0].bins), 2) + self.assertEqual(rpt.covergroups[0].covergroups[1].coverpoints[0].coverage, 100.0) + From 548adae039df4a3708e890c19109301e92c53062 Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Sun, 10 Jul 2022 21:45:35 -0700 Subject: [PATCH 6/9] Propagate weights and source info in merge process Signed-off-by: Matthew Ballance --- src/ucis/merge/db_merger.py | 62 +++++++------------------------------ 1 file changed, 11 insertions(+), 51 deletions(-) diff --git a/src/ucis/merge/db_merger.py b/src/ucis/merge/db_merger.py index 1472090..57cf7fd 100644 --- a/src/ucis/merge/db_merger.py +++ b/src/ucis/merge/db_merger.py @@ -56,8 +56,8 @@ def merge(self, dst_db, src_db_l : List[UCIS]): src_du = src_iscope.getInstanceDu() dst_du = self.dst_db.createScope( src_du.getScopeName(), - None, - 1, + src_du.getSourceInfo(), + src_du.getWeight(), # weight UCIS_OTHER, # TODO: must query SourceType UCIS_DU_MODULE, # TODO: must query GetScopeType UCIS_ENABLED_STMT | UCIS_ENABLED_BRANCH @@ -67,7 +67,7 @@ def merge(self, dst_db, src_db_l : List[UCIS]): dst_iscope = self.dst_db.createInstance( src_iscope.getScopeName(), - None, + src_iscope.getSourceInfo(), 1, # weight UCIS_OTHER, # query SourceType UCIS_INSTANCE, @@ -97,8 +97,8 @@ def _merge_covergroups(self, dst_scope, src_scopes): # Create the destination using the first covergroup dst_cg = dst_scope.createCovergroup( src_cg_l[0].getScopeName(), - None, # location - 1, # weight + src_cg_l[0].getSourceInfo(), # location + src_cg_l[0].getWeight(), # weight UCIS_OTHER) self._merge_covergroup(dst_cg, src_cg_l) @@ -132,8 +132,8 @@ def _merge_coverinsts(self, dst_cg, src_cg_l): dst_cg_i = dst_cg.createCoverInstance( name, - None, # location - 1, # weight + src_cg_i_l[0].getSourceInfo(), # location + src_cg_i_l[0].getWeight(), # weight UCIS_OTHER) dst_cp_m = self._merge_coverpoints(dst_cg_i, src_cg_i_l) @@ -159,8 +159,8 @@ def _merge_coverpoints(self, dst_cg, src_cg_l) -> Dict[str,object]: dst_cp = dst_cg.createCoverpoint( src_cp_l[0].getScopeName(), - None, # location - 1, # weight + src_cp_l[0].getSourceInfo(), # location + src_cp_l[0].getWeight(), # weight UCIS_OTHER) # SourceType dst_cp_m[name] = dst_cp @@ -220,8 +220,8 @@ def _merge_crosses(self, dst_cg, dst_cp_m, src_cg_l): dst_cr = dst_cg.createCross( name, - None, - 1, # weight + src_cr_l[0].getSourceInfo(), + src_cr_l[0].getWeight(), # weight UCIS_OTHER, coverpoint_l) @@ -251,44 +251,4 @@ def _merge_cross(self, dst_cr, src_cr_l): bin_n, cvg_t) - def _clone_cross(self, dst_cg, cp): - coverpoint_l = [] - for i in range(cp.getNumCrossedCoverpoints()): - src_cp = cp.getIthCrossedCoverpoint(i) - - dst_cp = None - for dst_cp_t in dst_cg.scopes(ScopeTypeT.COVERPOINT): - if dst_cp_t.getName() == src_cp.getName(): - dst_cp = dst_cp_t - break - coverpoint_l.append(dst_cp) - - cp_c = dst_cg.createCross( - cp.getName(), - None, - 1, # weight - UCIS_OTHER, # TODO: query - coverpoint_l) - - for ci_n in cp.coverItems(CoverTypeT.CVGBIN): - cvg_data = ci_n.getCoverData() - cvg_data_c = cp.createBin( - ci_n.getName(), - None, # Location - cvg_data.at_least, - cvg_data.data, - ci_n.getName(), - UCIS_CVGBIN) - - - def _clone_coverinsts(self, dst_cg, src_cg): - for src_cg_i in src_cg.scopes(ScopeTypeT.COVERINSTANCE): - dst_cg_i = dst_cg.createCoverInstance( - src_cg_i.getScopeName(), - None, # location - 1, # weight - UCIS_OTHER) - self._clone_coverpoints(dst_cg_i, src_cg_i) - self._clone_coverinsts(dst_cg_i, src_cg_i) - From 26f5a330b7e5457deca0745de516a8f4dfa012f1 Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Tue, 12 Jul 2022 21:05:04 -0700 Subject: [PATCH 7/9] XX Signed-off-by: Matthew Ballance --- doc/scdb_schema.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/scdb_schema.md b/doc/scdb_schema.md index 32ffc18..ab63e8d 100644 --- a/doc/scdb_schema.md +++ b/doc/scdb_schema.md @@ -1,8 +1,22 @@ # Tables +Scope + - scopeId + - name + - scopeType + - parentId (references scopeId in Scopes) + - fileId or NULL + - lineNo or NULL + +File + - path + - fileId + - files (?) - testhistory - scopes +- coverInstances +- covergroups - coveritems From d0980a712a4dc16bb44010c0b776dfaf84e1880a Mon Sep 17 00:00:00 2001 From: Eyck Jentzsch Date: Wed, 13 Jul 2022 09:27:40 +0200 Subject: [PATCH 8/9] changes handling of cgInstance options --- src/ucis/xml/xml_reader.py | 103 ++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 59 deletions(-) diff --git a/src/ucis/xml/xml_reader.py b/src/ucis/xml/xml_reader.py index fb21318..435d0f1 100644 --- a/src/ucis/xml/xml_reader.py +++ b/src/ucis/xml/xml_reader.py @@ -68,8 +68,8 @@ def read(self, file) -> UCIS: for histN in tree.iter("historyNodes"): self.readHistoryNode(histN) - for instN in tree.iter("instanceCoverages"): - self.readInstanceCoverage(instN) + for instX in tree.iter("instanceCoverages"): + self.readInstanceCoverage(instX) return self.db @@ -114,17 +114,14 @@ def readHistoryNode(self, histN): return ret - def readInstanceCoverage(self, instN): - name = instN.attrib["name"] - stmt_id = None - for stmt_idN in instN.iter("id"): - stmt_id = self.readStatementId(stmt_idN) - + def readInstanceCoverage(self, instX): + name = instX.attrib["name"] + stmt_id = self.readStatementId(instX.find('id')) srcinfo = None # TODO: Creating a coverage instance depends on # having a du_type - module_scope_name = self.getAttr(instN, "moduleName", "default") + module_scope_name = self.getAttr(instX, "moduleName", "default") type_scope = self.getScope( module_scope_name, @@ -137,58 +134,46 @@ def readInstanceCoverage(self, instN): UCIS_OTHER, type_scope) - for cg in instN.iter("covergroupCoverage"): - self.readCovergroup(cg, inst_scope, module_scope_name) + for cgX in instX.iter("covergroupCoverage"): + self.readCovergroup(cgX, inst_scope, module_scope_name) # self.setIntIfEx(instN, ret.setAli, name) - def readCovergroup(self, cg, inst_scope, module_scope_name): - # This entry is for a given covergroup type - - cg_typescope = None - covergroup_scope = None - - instances = [i for i in cg.iter("cgInstance")] - if len(instances) == 1: - cg_typescope = inst_scope.createCovergroup( - self.getAttr(instances[0], "name", "default"), - None, - 1, - UCIS_OTHER) - else: - cg_typescope = inst_scope.createCovergroup( - module_scope_name, None, 1, UCIS_OTHER) + def readCovergroup(self, cgX, inst_scope, module_scope_name): + + cg = inst_scope.createCovergroup( + module_scope_name, None, 1, UCIS_OTHER) - for cgN in instances: + for ciX in cgX.iter("cgInstance"): srcinfo = None - if len(instances) == 1: - covergroup_scope = cg_typescope - else: - covergroup_scope = cg_typescope.createCoverInstance( - self.getAttr(cgN, "name", "default"), - srcinfo, - 1, - UCIS_OTHER) - + ci = cg.createCoverInstance( + self.getAttr(ciX, "name", "default"), + srcinfo, + 1, + UCIS_OTHER) + ciX_options = ciX.find("options") + ci.m_per_instance = self.getAttrBool(ciX_options, 'per_instance') + ci.m_merge_instances = self.getAttrBool(ciX_options, 'merge_instances') + cp_m = {} - for cpN in cgN.iter("coverpoint"): - cp = self.readCoverpoint(cpN, covergroup_scope) - cp_m[self.getAttr(cpN, "name", "default")] = cp + for cpX in ciX.iter("coverpoint"): + cp = self.readCoverpoint(cpX, ci) + cp_m[self.getAttr(cpX, "name", "default")] = cp - for crN in cgN.iter("cross"): - self.readCross(crN, cp_m, covergroup_scope) + for crX in ciX.iter("cross"): + self.readCross(crX, cp_m, ci) - def readCoverpoint(self, cpN, covergroup_scope): + def readCoverpoint(self, cpX, ci): srcinfo = None - cp = covergroup_scope.createCoverpoint( - self.getAttr(cpN, "name", "default"), + cp = ci.createCoverpoint( + self.getAttr(cpX, "name", "default"), srcinfo, 1, # weight UCIS_OTHER) - for cpBin in cpN.iter("coverpointBin"): + for cpBin in cpX.iter("coverpointBin"): self.readCoverpointBin(cpBin, cp) return cp @@ -226,9 +211,9 @@ def readCoverpointBin(self, cpBin : Element, cp): self.getAttr(cpBin, "name", "default"), kind) - def readCross(self, crN, cp_m, covergroup_scope): - crossExpr = next(crN.iter("crossExpr")) - name = self.getAttr(crN, "name", "default") + def readCross(self, crX, cp_m, ci): + crossExpr = next(crX.iter("crossExpr")) + name = self.getAttr(crX, "name", "default") cp_l = [] for cp_n in crossExpr.text.split(','): @@ -240,22 +225,22 @@ def readCross(self, crN, cp_m, covergroup_scope): srcinfo = None - cr = covergroup_scope.createCross( + cr = ci.createCross( name, srcinfo, 1, # weight UCIS_OTHER, cp_l) - for crB in crN.iter("crossBin"): - self.readCrossBin(crB, cr) + for crbX in crX.iter("crossBin"): + self.readCrossBin(crbX, cr) return cr - def readCrossBin(self, crB, cr): - name = self.getAttr(crB, "name", "default") + def readCrossBin(self, crbX, cr): + name = self.getAttr(crbX, "name", "default") srcinfo = None - contentsN = next(crB.iter("contents")) + contentsN = next(crbX.iter("contents")) cr.createBin( name, @@ -265,10 +250,10 @@ def readCrossBin(self, crB, cr): "") # TODO: - def readStatementId(self, stmt_idN): - file_id = int(stmt_idN.attrib["file"]) - line = int(stmt_idN.attrib["line"]) - item = int(stmt_idN.attrib["inlineCount"]) + def readStatementId(self, stmt_idX): + file_id = int(stmt_idX.attrib["file"]) + line = int(stmt_idX.attrib["line"]) + item = int(stmt_idX.attrib["inlineCount"]) file = self.file_m[file_id] return StatementId(file, line, item) From 1c94de502c7fa701074d77bef33fc6150dec4515 Mon Sep 17 00:00:00 2001 From: Eyck Jentzsch Date: Wed, 13 Jul 2022 21:29:42 +0200 Subject: [PATCH 9/9] adds (non-)union merging and reporting --- src/ucis/report/coverage_report_builder.py | 102 +++++++++++++++++++-- 1 file changed, 92 insertions(+), 10 deletions(-) diff --git a/src/ucis/report/coverage_report_builder.py b/src/ucis/report/coverage_report_builder.py index df8fe06..f162ba0 100644 --- a/src/ucis/report/coverage_report_builder.py +++ b/src/ucis/report/coverage_report_builder.py @@ -6,9 +6,12 @@ from ucis.scope_type_t import ScopeTypeT from ucis.report.coverage_report import CoverageReport from ucis.coverpoint import Coverpoint +from ucis.cover_data import CoverData from ucis.cover_type_t import CoverTypeT from math import ceil from ucis.cross import Cross +from ucis.mem.mem_cover_index import MemCoverIndex +from collections import defaultdict class CoverageReportBuilder(object): @@ -32,11 +35,57 @@ def build(db : 'UCIS') ->'CoverageReport': def _build(self)->'CoverageReport': + self.update_coverage() for iscope in self.db.scopes(ScopeTypeT.INSTANCE): self.build_covergroups(iscope) return self.report + def update_coverage(self): + import copy + for inst in self.db.scopes(ScopeTypeT.INSTANCE): + for cg in inst.scopes(ScopeTypeT.COVERGROUP): + cps = dict() + crs = dict() + merged_cps = defaultdict(lambda: defaultdict(lambda: CoverData(CoverTypeT.CVGBIN, 0))) + merged_crs = defaultdict(lambda: defaultdict(lambda: CoverData(CoverTypeT.CVGBIN, 0))) + not_initialized = True + for ci in cg.scopes(ScopeTypeT.COVERINSTANCE): + if ci.getMergeInstances(): + if not_initialized: + for cp in ci.scopes(ScopeTypeT.COVERPOINT): + cp_m = cg.createCoverpoint( + cp.m_name, cp.m_srcinfo, cp.m_weight, cp.m_source) + cps[cp.m_name] = cp_m + for ci_n in cp.coverItems(CoverTypeT.CVGBIN): + cp_m.createNextCover(ci_n.name, copy.copy(ci_n.data), ci_n.srcinfo) + merged_cps[cp.m_name][ci_n.name] = copy.copy(ci_n.getCoverData()) + merged_cps[cp.m_name][ci_n.name].data = 0 + for cr in ci.scopes(ScopeTypeT.CROSS): + cr_m = cg.createCross( + cr.m_name, cr.m_srcinfo, cr.m_weight, cr.m_source, + copy.copy(cr.coverItems)) + crs[cr.m_name] = cr_m + for ci_n in cr.coverItems(CoverTypeT.CVGBIN): + cr_m.createNextCover(ci_n.name, copy.copy(ci_n.data), ci_n.srcinfo) + merged_cps[cr.m_name][ci_n.name] = copy.copy(ci_n.getCoverData()) + merged_cps[cr.m_name][ci_n.name].data = 0 + not_initialized = False + for cp in ci.scopes(ScopeTypeT.COVERPOINT): + for ci_n in cp.coverItems(CoverTypeT.CVGBIN): + merged_cps[cp.m_name][ci_n.name].data+= ci_n.getCoverData().data + for cr in ci.scopes(ScopeTypeT.CROSS): + for ci_n in cr.coverItems(CoverTypeT.CVGBIN): + merged_crs[cr.m_name][ci_n.name].data+= ci_n.getCoverData().data + for cp in cps.values(): + for ci_n in cp.coverItems(CoverTypeT.CVGBIN): + ci_n.data.data = merged_cps[cp.m_name][ci_n.name].data + cp.m_name += " (merged)" + for cr in crs.values(): + for ci_n in cr.coverItems(CoverTypeT.CVGBIN): + ci_n.data.data = merged_crs[cr.m_name][ci_n.name].data + cr.m_name += " (merged)" + def build_covergroups(self, iscope): @@ -65,30 +114,63 @@ def build_covergroup(self, cg_n)->CoverageReport.Covergroup: for cr_in in cg_n.scopes(ScopeTypeT.CROSS): cg_r.crosses.append(self.build_cross(cr_in)) + unmerged_ci = list() for cg_in in cg_n.scopes(ScopeTypeT.COVERINSTANCE): - cg_r.covergroups.append(self.build_covergroup(cg_in)) - + ci = self.build_coverinstance(cg_in) + if cg_in.getPerInstance(): + cg_r.covergroups.append(ci) + if not cg_in.getMergeInstances(): + unmerged_ci.append(ci) # Determine the covergroup coverage coverage = 0.0 - div = 0 - non_union_merge = True + for cp in cg_r.coverpoints: - non_union_merge = False if cp.weight > 0: coverage += cp.coverage * cp.weight div += cp.weight for cr in cg_r.crosses: - non_union_merge = False - coverage += cr.coverage * cr.weight + if cr.weight > 0: + coverage += cr.coverage * cr.weight div += cr.weight - if non_union_merge: - for cg in cg_r.covergroups: + for cg in unmerged_ci: + if cg.weight > 0: coverage += cg.coverage * cg.weight - div += cg.weight + div += cg.weight + + if div > 0: coverage /= div + + cg_r.coverage = coverage + + return cg_r + + def build_coverinstance(self, cg_i)->CoverageReport.Covergroup: + cg_r = CoverageReport.Covergroup( + cg_i.getScopeName(), + cg_i.getScopeName()) + + cg_r.weight = cg_i.getWeight() + + for cp_in in cg_i.scopes(ScopeTypeT.COVERPOINT): + cg_r.coverpoints.append(self.build_coverpoint(cp_in)) + + for cr_in in cg_i.scopes(ScopeTypeT.CROSS): + cg_r.crosses.append(self.build_cross(cr_in)) + coverage = 0.0 + + div = 0 + for cp in cg_r.coverpoints: + if cp.weight > 0: + coverage += cp.coverage * cp.weight + div += cp.weight + + for cr in cg_r.crosses: + coverage += cr.coverage * cr.weight + div += cr.weight + if div > 0: coverage /= div cg_r.coverage = coverage