From 7a0d454d1dd4586e8446b8300e58b88fff375684 Mon Sep 17 00:00:00 2001 From: TannazVhdBMWExt Date: Fri, 27 Sep 2024 12:10:24 +0200 Subject: [PATCH] lobster-report allow optional up-linking in tracing policies Resolves https://github.com/bmw-software-engineering/lobster/issues/10 --- lobster/config/parser.py | 96 ++++++++++++++++++---------------------- lobster/items.py | 40 ++++++++++------- 2 files changed, 67 insertions(+), 69 deletions(-) diff --git a/lobster/config/parser.py b/lobster/config/parser.py index 4b83af00..160d28df 100644 --- a/lobster/config/parser.py +++ b/lobster/config/parser.py @@ -102,13 +102,11 @@ def parse_level_declaration(self): "duplicate declaration") item = { - "name" : level_name, - "kind" : level_kind, - "traces" : [], - "source" : [], - "needs_tracing_up" : False, - "needs_tracing_down" : False, - "raw_trace_requirements" : [] + "name" : level_name, + "kind" : level_kind, + "source" : [], + "trace_to" : [], + "trace_from" : [] } self.levels[level_name] = item @@ -175,39 +173,40 @@ def parse_level_declaration(self): elif self.peek("KEYWORD", "trace"): self.match("KEYWORD", "trace") - self.match("KEYWORD", "to") - self.match("COLON") - self.match("STRING") - if self.ct.value() == level_name: - self.error(self.ct.loc, - "cannot trace to yourself") - elif self.ct.value() not in self.levels: - self.error(self.ct.loc, - "unknown item %s" % self.ct.value()) - else: - self.levels[self.ct.value()]["needs_tracing_down"] = True - item["traces"].append(self.ct.value()) - item["needs_tracing_up"] = True - self.match("SEMI") + if self.peek("KEYWORD", "to"): + self.match("KEYWORD", "to") + self.match("COLON") - elif self.peek("KEYWORD", "requires"): - self.match("KEYWORD", "requires") - self.match("COLON") + req_list = [] + self.match("STRING") + req_list.append(self.ct.value()) - req_list = [] + while self.peek("KEYWORD", "or"): + self.match("KEYWORD", "or") + self.match("STRING") + req_list.append(self.ct.value()) - self.match("STRING") - req_list.append(self.ct) + self.match("SEMI") + + item["trace_to"].append(req_list) - while self.peek("KEYWORD", "or"): - self.match("KEYWORD", "or") + elif self.peek("KEYWORD", "from"): + self.match("KEYWORD", "from") + self.match("COLON") + + req_list = [] self.match("STRING") - req_list.append(self.ct) + req_list.append(self.ct.value()) - self.match("SEMI") + while self.peek("KEYWORD", "or"): + self.match("KEYWORD", "or") + self.match("STRING") + req_list.append(self.ct.value()) - item["raw_trace_requirements"].append(req_list) + self.match("SEMI") + + item["trace_from"].append(req_list) else: self.error(self.nt.loc, @@ -219,28 +218,19 @@ def parse_level_declaration(self): def load(mh, file_name): parser = Parser(mh, file_name) ast = parser.parse() - - # Resolve requires links now + item_names = list(ast.keys()) for item in ast.values(): - item["breakdown_requirements"] = [] - if len(item["raw_trace_requirements"]) > 0: - for chain in item["raw_trace_requirements"]: - new_chain = [] - for tok in chain: - if tok.value() not in ast: - mh.error(tok.loc, "unknown level %s" % tok.value()) - if item["name"] not in ast[tok.value()]["traces"]: - mh.error(tok.loc, - "%s cannot trace to %s items" % - (tok.value(), - item["name"])) - new_chain.append(tok.value()) - item["breakdown_requirements"].append(new_chain) - else: - for src in ast.values(): - if item["name"] in src["traces"]: - item["breakdown_requirements"].append([src["name"]]) - del item["raw_trace_requirements"] + if len(item["trace_to"]) > 0: + for trace_to in item["trace_to"]: + if not set(trace_to).issubset(item_names): + mh.error(set(trace_to).issubset(item_names), + "cannot trace to %s items" % ",".join(trace_to)) + + if len(item["trace_from"]) > 0: + for trace_from in item["trace_from"]: + if not set(trace_from).issubset(item_names): + mh.error(set(trace_to).issubset(item_names), + "cannot trace from %s items" % ",".join(trace_from)) return ast diff --git a/lobster/items.py b/lobster/items.py index 7fb1774a..8185e75d 100644 --- a/lobster/items.py +++ b/lobster/items.py @@ -134,31 +134,41 @@ def determine_status(self, config, stab): level = config[self.level] has_up_ref = len(self.ref_up) > 0 + has_down_ref = len(self.ref_down) > 0 has_just_up = len(self.just_up) > 0 or len(self.just_global) > 0 has_just_down = len(self.just_down) > 0 or len(self.just_global) > 0 has_init_errors = len(self.messages) > 0 # Check up references ok_up = True - if level["needs_tracing_up"]: + if level["trace_to"]: if not has_up_ref and not has_just_up: ok_up = False self.messages.append("missing up reference") + for trace_to in level["trace_to"]: + # and + if not ok_up: + break + + # or + refs_levels = [stab[ref.key()].level for ref in self.ref_up] + ok_up = len(set(refs_levels).intersection(set(trace_to))) > 0 + # Check set of down references ok_down = True - if level["needs_tracing_down"]: - has_trace = {name : False - for name in config - if self.level in config[name]["traces"]} - for ref in self.ref_down: - has_trace[stab[ref.key()].level] = True - for chain in level["breakdown_requirements"]: - if not any(has_trace[src] for src in chain) and \ - not has_just_down: - ok_down = False - self.messages.append("missing reference to %s" % - " or ".join(sorted(chain))) + if level["trace_from"]: + if not has_down_ref and not has_just_down: + ok_down = False + self.messages.append("missing down reference") + + for trace_from in level["trace_from"]: + # and + if not ok_down: + break + # or + refs_levels = [stab[ref.key()].level for ref in self.ref_down] + ok_down = len(set(refs_levels).intersection(set(trace_from))) > 0 # Set status if self.has_error: @@ -168,9 +178,7 @@ def determine_status(self, config, stab): self.tracing_status = Tracing_Status.JUSTIFIED else: self.tracing_status = Tracing_Status.OK - elif (ok_up or ok_down) and \ - level["needs_tracing_up"] and \ - level["needs_tracing_down"]: + elif ok_up or ok_down: self.tracing_status = Tracing_Status.PARTIAL else: self.tracing_status = Tracing_Status.MISSING