Skip to content

Commit

Permalink
lobster-report allow optional up-linking in tracing policies
Browse files Browse the repository at this point in the history
  • Loading branch information
TannazVhdBMWExt committed Oct 9, 2024
1 parent 77e74a1 commit 6719212
Show file tree
Hide file tree
Showing 9 changed files with 294 additions and 69 deletions.
96 changes: 43 additions & 53 deletions lobster/config/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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,
Expand All @@ -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("cannot trace from %s items" %
",".join(trace_from))

return ast

Expand Down
41 changes: 25 additions & 16 deletions lobster/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,31 +134,42 @@ 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:
Expand All @@ -168,9 +179,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
Expand Down
27 changes: 27 additions & 0 deletions test-unit/lobster-core/data/data1.lobster
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"data": [
{
"tag": "req 17045@11",
"location": {
"kind": "codebeamer",
"cb_root": "https://codebeamer.company.net/",
"tracker": 1111,
"item": 1111,
"version": 11,
"name": "test1"
},
"name": "test1",
"messages": [],
"just_up": [],
"just_down": [],
"just_global": [],
"framework": "codebeamer",
"kind": "Technical Requirement",
"text": null,
"status": "Valid"
}
],
"generator": "lobster_codebeamer",
"schema": "lobster-req-trace",
"version": 4
}
27 changes: 27 additions & 0 deletions test-unit/lobster-core/data/data2.lobster
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"data": [
{
"tag": "req 17044@22",
"location": {
"kind": "codebeamer",
"cb_root": "https://codebeamer.company.net/",
"tracker": 2222,
"item": 2222,
"version": 22,
"name": "test"
},
"name": "test",
"messages": [],
"just_up": [],
"just_down": [],
"just_global": [],
"framework": "codebeamer",
"kind": "Technical Requirement",
"text": null,
"status": "Valid"
}
],
"generator": "lobster_codebeamer",
"schema": "lobster-req-trace",
"version": 4
}
50 changes: 50 additions & 0 deletions test-unit/lobster-core/data/data3.lobster
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"data": [
{
"tag": "req test3.trlc",
"location": {
"kind": "file",
"file": "./main.trlc",
"line": 3,
"column": 13
},
"name": "test3.trlc",
"messages": [],
"just_up": [],
"just_down": [],
"just_global": [],
"ref_down": [
"req 17044@22"
],
"ref_up": [
"req 17045@11",
"req test33.lobster"
],
"framework": "TRLC",
"kind": "Requirement",
"text": "Treat Requirements Like Code",
"status": null
},
{
"tag": "req test33.lobster",
"location": {
"kind": "file",
"file": "./main.trlc",
"line": 7,
"column": 13
},
"name": "test33.lobster",
"messages": [],
"just_up": [],
"just_down": [],
"just_global": [],
"framework": "TRLC",
"kind": "Requirement",
"text": "test",
"status": null
}
],
"generator": "lobster-trlc",
"schema": "lobster-req-trace",
"version": 4
}
25 changes: 25 additions & 0 deletions test-unit/lobster-core/data/data4.lobster
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"data": [
{
"tag": "req test4.trlc",
"location": {
"kind": "file",
"file": "./main.trlc",
"line": 4,
"column": 44
},
"name": "test4.trlc",
"messages": [],
"just_up": [],
"just_down": [],
"just_global": [],
"framework": "TRLC",
"kind": "Requirement",
"text": "Treat Requirements Like Code",
"status": null
}
],
"generator": "lobster-trlc",
"schema": "lobster-req-trace",
"version": 4
}
25 changes: 25 additions & 0 deletions test-unit/lobster-core/data/data5.lobster
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"data": [
{
"tag": "req test5.trlc",
"location": {
"kind": "file",
"file": "./main.trlc",
"line": 4,
"column": 44
},
"name": "test5.trlc",
"messages": [],
"just_up": [],
"just_down": [],
"just_global": [],
"framework": "TRLC",
"kind": "Requirement",
"text": "Treat Requirements Like Code",
"status": null
}
],
"generator": "lobster-trlc",
"schema": "lobster-req-trace",
"version": 4
}
39 changes: 39 additions & 0 deletions test-unit/lobster-core/data/lobster1.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
requirements "Laws" {
source: "data/data4.lobster";
}

requirements "System Requirements" {
source: "data/data3.lobster";
trace to: "Implementation" or "System Requirements";
trace from: "Software Requirements";
}

requirements "Software Requirements" {
source: "data/data2.lobster";
trace to: "Software Requirements" or "System Requirements";
trace to: "Laws";
trace from: "Software Requirements" or "Implementation";
trace from: "Component Test";
}

requirements "Another Test" {
source: "data/data5.lobster";
trace to: "Software Requirements" or "Another Test";
trace to: "Software Requirements";
}

requirements "Implementation" {
source: "data/data1.lobster";
trace to: "Unit Test";
}

requirements "Unit Test" {
trace to: "Component Test";
trace to: "Software Requirements";
}

requirements "Component Test" {
trace to: "Software Requirements";
}

requirements "Empty" {}
Loading

0 comments on commit 6719212

Please sign in to comment.