From baddc9aa9f9fd99c80c3ea713b935f2609b1a7c7 Mon Sep 17 00:00:00 2001 From: Paul Boege <160138461+paulboege@users.noreply.github.com> Date: Thu, 29 Feb 2024 14:31:42 +0100 Subject: [PATCH] Implement lobster source filters kind and prefix (#22) - add integration test at integration-tests/projects/filter - add source filter description to docs/config_files.md --- Makefile | 1 + docs/config_files.md | 10 +- integration-tests/projects/filter/.gitignore | 5 + integration-tests/projects/filter/BUILD | 12 + integration-tests/projects/filter/Makefile | 31 ++ integration-tests/projects/filter/example.rsl | 20 + integration-tests/projects/filter/foo.cpp | 13 + integration-tests/projects/filter/foo.h | 7 + .../projects/filter/lobster-trlc.conf | 8 + .../projects/filter/lobster.conf | 21 + .../projects/filter/report.reference_output | 438 ++++++++++++++++++ .../projects/filter/softreq_example.trlc | 26 ++ .../projects/filter/sysreq_example.trlc | 25 + .../projects/filter/test_example.json | 31 ++ lobster/config/parser.py | 5 + lobster/io.py | 26 +- 16 files changed, 669 insertions(+), 10 deletions(-) create mode 100644 integration-tests/projects/filter/.gitignore create mode 100644 integration-tests/projects/filter/BUILD create mode 100644 integration-tests/projects/filter/Makefile create mode 100644 integration-tests/projects/filter/example.rsl create mode 100644 integration-tests/projects/filter/foo.cpp create mode 100644 integration-tests/projects/filter/foo.h create mode 100644 integration-tests/projects/filter/lobster-trlc.conf create mode 100644 integration-tests/projects/filter/lobster.conf create mode 100644 integration-tests/projects/filter/report.reference_output create mode 100644 integration-tests/projects/filter/softreq_example.trlc create mode 100644 integration-tests/projects/filter/sysreq_example.trlc create mode 100644 integration-tests/projects/filter/test_example.json diff --git a/Makefile b/Makefile index c1b41d7e..05d6a805 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,7 @@ packages: integration_tests: packages (cd integration-tests/projects/basic; make) + (cd integration-tests/projects/filter; make) system_tests: make -B -C test-system/lobster-json diff --git a/docs/config_files.md b/docs/config_files.md index a0e8be56..0342a01d 100644 --- a/docs/config_files.md +++ b/docs/config_files.md @@ -34,6 +34,14 @@ The `source` attribute assigns a LOBSTER file to contribute to this level. There is some extra information you can supply with the `with` keyword. +Using `with kind ` allows to filter items with `kind` in the +LOBSTER source file. For TRLC requirements the kind attribute in the +LOBSTER source file corresponds to the TRLC requirements type. + +Using `with prefix ` allows to filter items with a certain +prefix in the `tag` attribute in the LOBSTER source file. For TRLC +requirements the tag attribute corresponds to the requiremet name. + Specifically for requirements you can say: ``` @@ -43,8 +51,6 @@ requirements "Requirements" { } ``` -We expect to add other `with` properties in the future. - #### trace to The `trace to` attribute declares the expected tracing link. This diff --git a/integration-tests/projects/filter/.gitignore b/integration-tests/projects/filter/.gitignore new file mode 100644 index 00000000..ef80d12c --- /dev/null +++ b/integration-tests/projects/filter/.gitignore @@ -0,0 +1,5 @@ +cppcode.lobster +report.lobster +requirements.lobster +lobster_report.html +json.lobster diff --git a/integration-tests/projects/filter/BUILD b/integration-tests/projects/filter/BUILD new file mode 100644 index 00000000..d8663c9b --- /dev/null +++ b/integration-tests/projects/filter/BUILD @@ -0,0 +1,12 @@ +cc_library( + name = "foofunc", + srcs = ["foo.h", "foo.cpp"] +) + +cc_test( + name = "foo_test", + srcs = ["test.cpp"], + deps = ["@com_google_googletest//:gtest_main", + "foofunc", + "//support/gtest/include:lobster_gtest"] +) diff --git a/integration-tests/projects/filter/Makefile b/integration-tests/projects/filter/Makefile new file mode 100644 index 00000000..b8d46f0a --- /dev/null +++ b/integration-tests/projects/filter/Makefile @@ -0,0 +1,31 @@ +LOBSTER_ROOT:=../../.. +LOBSTER_WIP:=$(LOBSTER_ROOT)/work-in-progress + +PATH:=$(LOBSTER_ROOT)/test_install/bin:$(PATH) +PYTHONPATH:=$(wildcard $(LOBSTER_ROOT)/test_install/lib/python*/site-packages) + +CLANG_TIDY:=$(LOBSTER_ROOT)/../llvm-project/build/bin/clang-tidy + +THIS_TEST:=$(shell realpath --relative-to $(LOBSTER_ROOT) $(PWD)) +THIS_TEST_ESCAPED:=$(subst /,\\/,$(THIS_TEST)) + +html_report.html: cppcode.lobster requirements.lobster lobster.conf json.lobster + @lobster-report + @lobster-online-report + @cp report.lobster report.reference_output + @lobster-html-report + +cppcode.lobster: foo.h foo.cpp + @lobster-cpp foo.cpp foo.h \ + --out="cppcode.lobster" --clang-tidy $(CLANG_TIDY) + +requirements.lobster: example.rsl softreq_example.trlc sysreq_example.trlc + @lobster-trlc example.rsl softreq_example.trlc sysreq_example.trlc\ + --out="requirements.lobster" + +json.lobster: test_example.json + @lobster-json test_example.json \ + --name-attribute "name" \ + --tag-attribute "tags" \ + --justification-attribute "justification" \ + --out="json.lobster" diff --git a/integration-tests/projects/filter/example.rsl b/integration-tests/projects/filter/example.rsl new file mode 100644 index 00000000..50297aa2 --- /dev/null +++ b/integration-tests/projects/filter/example.rsl @@ -0,0 +1,20 @@ +package example + +type System_Requirement +{ + text String +} + +type Software_Requirement +{ + text String + trace_trlc optional System_Requirement [1 .. *] +} + +checks System_Requirement { + len(text) >= 10, warning "this is a bit short", text +} + +checks Software_Requirement { + len(text) >= 10, warning "this is a bit short", text +} diff --git a/integration-tests/projects/filter/foo.cpp b/integration-tests/projects/filter/foo.cpp new file mode 100644 index 00000000..389c9354 --- /dev/null +++ b/integration-tests/projects/filter/foo.cpp @@ -0,0 +1,13 @@ +#include "foo.h" + +int implication(int x, int y) +{ + // lobster-trace: softreq_example.req_implication + return (x == 0) || (y != 0); +} + +int exclusive_or(int x, int y) +{ + // lobster-trace: softreq_example.req_xor + return (x == 0) != (y == 0); +} diff --git a/integration-tests/projects/filter/foo.h b/integration-tests/projects/filter/foo.h new file mode 100644 index 00000000..c978be8e --- /dev/null +++ b/integration-tests/projects/filter/foo.h @@ -0,0 +1,7 @@ +#ifndef __FOO_H__ +#define __FOO_H__ + +int implication(int x, int y); +int exclusive_or(int x, int y); + +#endif diff --git a/integration-tests/projects/filter/lobster-trlc.conf b/integration-tests/projects/filter/lobster-trlc.conf new file mode 100644 index 00000000..0fccf045 --- /dev/null +++ b/integration-tests/projects/filter/lobster-trlc.conf @@ -0,0 +1,8 @@ +example.System_Requirement { + description = text +} + +example.Software_Requirement { + description = text + tags "req" = trace_trlc +} diff --git a/integration-tests/projects/filter/lobster.conf b/integration-tests/projects/filter/lobster.conf new file mode 100644 index 00000000..c77818d2 --- /dev/null +++ b/integration-tests/projects/filter/lobster.conf @@ -0,0 +1,21 @@ +requirements "System Requirements" { + source: "requirements.lobster" with kind "System_Requirement"; +} + +requirements "Software Requirements" +{ + source: "requirements.lobster" with kind "Software_Requirement"; + trace to: "System Requirements"; +} + +implementation "Code" +{ + source: "cppcode.lobster"; + trace to: "Software Requirements"; +} + +activity "Verification Test" +{ + source: "json.lobster"; + trace to: "Software Requirements"; +} diff --git a/integration-tests/projects/filter/report.reference_output b/integration-tests/projects/filter/report.reference_output new file mode 100644 index 00000000..f20fe9a9 --- /dev/null +++ b/integration-tests/projects/filter/report.reference_output @@ -0,0 +1,438 @@ +{ + "schema": "lobster-report", + "version": 2, + "generator": "lobster_report", + "levels": [ + { + "name": "System Requirements", + "kind": "requirements", + "items": [ + { + "tag": "req sysreq_example.lobster_example", + "location": { + "kind": "github", + "gh_root": "https://github_paulboegedev/paulboege/lobster", + "commit": "main", + "file": "integration-tests/projects/filter/sysreq_example.trlc", + "line": 4 + }, + "name": "sysreq_example.lobster_example", + "messages": [], + "just_up": [], + "just_down": [], + "just_global": [], + "ref_up": [], + "ref_down": [ + "req softreq_example.req_implication", + "req softreq_example.req_xor" + ], + "tracing_status": "OK", + "framework": "TRLC", + "kind": "System_Requirement", + "text": "Provide a demonstration of LOBSTER with system and software requirements in TRLC with a source filter in lobster.conf", + "status": null + }, + { + "tag": "req sysreq_example.req_implication", + "location": { + "kind": "github", + "gh_root": "https://github_paulboegedev/paulboege/lobster", + "commit": "main", + "file": "integration-tests/projects/filter/sysreq_example.trlc", + "line": 11 + }, + "name": "sysreq_example.req_implication", + "messages": [], + "just_up": [], + "just_down": [], + "just_global": [], + "ref_up": [], + "ref_down": [ + "req softreq_example.req_implication" + ], + "tracing_status": "OK", + "framework": "TRLC", + "kind": "System_Requirement", + "text": "The example shall provide a logical implication operation between two integer inputs \nto determine if the second integer is logically implied by the first.", + "status": null + }, + { + "tag": "req sysreq_example.req_xor", + "location": { + "kind": "github", + "gh_root": "https://github_paulboegedev/paulboege/lobster", + "commit": "main", + "file": "integration-tests/projects/filter/sysreq_example.trlc", + "line": 19 + }, + "name": "sysreq_example.req_xor", + "messages": [], + "just_up": [], + "just_down": [], + "just_global": [], + "ref_up": [], + "ref_down": [ + "req softreq_example.req_xor" + ], + "tracing_status": "OK", + "framework": "TRLC", + "kind": "System_Requirement", + "text": "The example shall provide an exclusive or (XOR) operation between two integer inputs \nto evaluate scenarios where a decision is based on the condition that exactly one of two inputs must be true (non-zero).", + "status": null + } + ], + "coverage": 100.0 + }, + { + "name": "Software Requirements", + "kind": "requirements", + "items": [ + { + "tag": "req softreq_example.req_implication", + "location": { + "kind": "github", + "gh_root": "https://github_paulboegedev/paulboege/lobster", + "commit": "main", + "file": "integration-tests/projects/filter/softreq_example.trlc", + "line": 5 + }, + "name": "softreq_example.req_implication", + "messages": [], + "just_up": [], + "just_down": [], + "just_global": [], + "ref_up": [ + "req sysreq_example.req_implication", + "req sysreq_example.lobster_example" + ], + "ref_down": [ + "cpp foo.cpp:implication:3", + "json test_example.json.Implication Test 1", + "json test_example.json.Implication Test 2" + ], + "tracing_status": "OK", + "framework": "TRLC", + "kind": "Software_Requirement", + "text": "Implement a function with the signature \n int implication(int x, int y)\nthat computes the logical implication of its two integer arguments.\nThe function returns 1 (true) if the first argument is 0\nor the second argument is non-zero, and 0 (false) otherwise.", + "status": null + }, + { + "tag": "req softreq_example.req_xor", + "location": { + "kind": "github", + "gh_root": "https://github_paulboegedev/paulboege/lobster", + "commit": "main", + "file": "integration-tests/projects/filter/softreq_example.trlc", + "line": 17 + }, + "name": "softreq_example.req_xor", + "messages": [], + "just_up": [], + "just_down": [], + "just_global": [], + "ref_up": [ + "req sysreq_example.req_xor", + "req sysreq_example.lobster_example" + ], + "ref_down": [ + "cpp foo.cpp:exclusive_or:9", + "json test_example.json.XOR Test 1", + "json test_example.json.XOR Test 2", + "json test_example.json.XOR Test 3", + "json test_example.json.XOR Test 4" + ], + "tracing_status": "OK", + "framework": "TRLC", + "kind": "Software_Requirement", + "text": "Implement a function with the signature \n int exclusive_or(int x, int y)\nto compute the exclusive or (XOR) of its two integer arguments.\nThe function returns 1 when exactly one of the arguments is non-zero and 0 otherwise.", + "status": null + } + ], + "coverage": 100.0 + }, + { + "name": "Code", + "kind": "implementation", + "items": [ + { + "tag": "cpp foo.cpp:implication:3", + "location": { + "kind": "github", + "gh_root": "https://github_paulboegedev/paulboege/lobster", + "commit": "main", + "file": "integration-tests/projects/filter/foo.cpp", + "line": 3 + }, + "name": "implication", + "messages": [], + "just_up": [], + "just_down": [], + "just_global": [], + "ref_up": [ + "req softreq_example.req_implication" + ], + "ref_down": [], + "tracing_status": "OK", + "language": "C/C++", + "kind": "function" + }, + { + "tag": "cpp foo.cpp:exclusive_or:9", + "location": { + "kind": "github", + "gh_root": "https://github_paulboegedev/paulboege/lobster", + "commit": "main", + "file": "integration-tests/projects/filter/foo.cpp", + "line": 9 + }, + "name": "exclusive_or", + "messages": [], + "just_up": [], + "just_down": [], + "just_global": [], + "ref_up": [ + "req softreq_example.req_xor" + ], + "ref_down": [], + "tracing_status": "OK", + "language": "C/C++", + "kind": "function" + } + ], + "coverage": 100.0 + }, + { + "name": "Verification Test", + "kind": "activity", + "items": [ + { + "tag": "json test_example.json.Implication Test 1", + "location": { + "kind": "github", + "gh_root": "https://github_paulboegedev/paulboege/lobster", + "commit": "main", + "file": "integration-tests/projects/filter/test_example.json", + "line": null + }, + "name": "test_example.json.Implication Test 1", + "messages": [], + "just_up": [], + "just_down": [], + "just_global": [], + "ref_up": [ + "req softreq_example.req_implication" + ], + "ref_down": [], + "tracing_status": "OK", + "framework": "JSON", + "kind": "Test Vector", + "status": null + }, + { + "tag": "json test_example.json.Implication Test 2", + "location": { + "kind": "github", + "gh_root": "https://github_paulboegedev/paulboege/lobster", + "commit": "main", + "file": "integration-tests/projects/filter/test_example.json", + "line": null + }, + "name": "test_example.json.Implication Test 2", + "messages": [], + "just_up": [], + "just_down": [], + "just_global": [], + "ref_up": [ + "req softreq_example.req_implication" + ], + "ref_down": [], + "tracing_status": "OK", + "framework": "JSON", + "kind": "Test Vector", + "status": null + }, + { + "tag": "json test_example.json.XOR Test 1", + "location": { + "kind": "github", + "gh_root": "https://github_paulboegedev/paulboege/lobster", + "commit": "main", + "file": "integration-tests/projects/filter/test_example.json", + "line": null + }, + "name": "test_example.json.XOR Test 1", + "messages": [], + "just_up": [], + "just_down": [], + "just_global": [], + "ref_up": [ + "req softreq_example.req_xor" + ], + "ref_down": [], + "tracing_status": "OK", + "framework": "JSON", + "kind": "Test Vector", + "status": null + }, + { + "tag": "json test_example.json.XOR Test 2", + "location": { + "kind": "github", + "gh_root": "https://github_paulboegedev/paulboege/lobster", + "commit": "main", + "file": "integration-tests/projects/filter/test_example.json", + "line": null + }, + "name": "test_example.json.XOR Test 2", + "messages": [], + "just_up": [], + "just_down": [], + "just_global": [], + "ref_up": [ + "req softreq_example.req_xor" + ], + "ref_down": [], + "tracing_status": "OK", + "framework": "JSON", + "kind": "Test Vector", + "status": null + }, + { + "tag": "json test_example.json.XOR Test 3", + "location": { + "kind": "github", + "gh_root": "https://github_paulboegedev/paulboege/lobster", + "commit": "main", + "file": "integration-tests/projects/filter/test_example.json", + "line": null + }, + "name": "test_example.json.XOR Test 3", + "messages": [], + "just_up": [], + "just_down": [], + "just_global": [], + "ref_up": [ + "req softreq_example.req_xor" + ], + "ref_down": [], + "tracing_status": "OK", + "framework": "JSON", + "kind": "Test Vector", + "status": null + }, + { + "tag": "json test_example.json.XOR Test 4", + "location": { + "kind": "github", + "gh_root": "https://github_paulboegedev/paulboege/lobster", + "commit": "main", + "file": "integration-tests/projects/filter/test_example.json", + "line": null + }, + "name": "test_example.json.XOR Test 4", + "messages": [], + "just_up": [], + "just_down": [], + "just_global": [], + "ref_up": [ + "req softreq_example.req_xor" + ], + "ref_down": [], + "tracing_status": "OK", + "framework": "JSON", + "kind": "Test Vector", + "status": null + } + ], + "coverage": 100.0 + } + ], + "policy": { + "System Requirements": { + "name": "System Requirements", + "kind": "requirements", + "traces": [], + "source": [ + { + "file": "requirements.lobster", + "filters": [ + [ + "kind", + "System_Requirement" + ] + ], + "valid_status": [] + } + ], + "needs_tracing_up": false, + "needs_tracing_down": true, + "breakdown_requirements": [ + [ + "Software Requirements" + ] + ] + }, + "Software Requirements": { + "name": "Software Requirements", + "kind": "requirements", + "traces": [ + "System Requirements" + ], + "source": [ + { + "file": "requirements.lobster", + "filters": [ + [ + "kind", + "Software_Requirement" + ] + ], + "valid_status": [] + } + ], + "needs_tracing_up": true, + "needs_tracing_down": true, + "breakdown_requirements": [ + [ + "Code" + ], + [ + "Verification Test" + ] + ] + }, + "Code": { + "name": "Code", + "kind": "implementation", + "traces": [ + "Software Requirements" + ], + "source": [ + { + "file": "cppcode.lobster", + "filters": [] + } + ], + "needs_tracing_up": true, + "needs_tracing_down": false, + "breakdown_requirements": [] + }, + "Verification Test": { + "name": "Verification Test", + "kind": "activity", + "traces": [ + "Software Requirements" + ], + "source": [ + { + "file": "json.lobster", + "filters": [] + } + ], + "needs_tracing_up": true, + "needs_tracing_down": false, + "breakdown_requirements": [] + } + }, + "matrix": [] +} \ No newline at end of file diff --git a/integration-tests/projects/filter/softreq_example.trlc b/integration-tests/projects/filter/softreq_example.trlc new file mode 100644 index 00000000..3c3d3b3a --- /dev/null +++ b/integration-tests/projects/filter/softreq_example.trlc @@ -0,0 +1,26 @@ +package softreq_example +import example +import sysreq_example + +example.Software_Requirement req_implication +{ + text = ''' + Implement a function with the signature + int implication(int x, int y) + that computes the logical implication of its two integer arguments. + The function returns 1 (true) if the first argument is 0 + or the second argument is non-zero, and 0 (false) otherwise. + ''' + trace_trlc = [sysreq_example.lobster_example, sysreq_example.req_implication] +} + +example.Software_Requirement req_xor +{ + text = ''' + Implement a function with the signature + int exclusive_or(int x, int y) + to compute the exclusive or (XOR) of its two integer arguments. + The function returns 1 when exactly one of the arguments is non-zero and 0 otherwise. + ''' + trace_trlc = [sysreq_example.lobster_example, sysreq_example.req_xor] +} diff --git a/integration-tests/projects/filter/sysreq_example.trlc b/integration-tests/projects/filter/sysreq_example.trlc new file mode 100644 index 00000000..b22c5d5b --- /dev/null +++ b/integration-tests/projects/filter/sysreq_example.trlc @@ -0,0 +1,25 @@ +package sysreq_example +import example + +example.System_Requirement lobster_example +{ + text = ''' + Provide a demonstration of LOBSTER with system and software requirements in TRLC with a source filter in lobster.conf + ''' +} + +example.System_Requirement req_implication +{ + text = ''' + The example shall provide a logical implication operation between two integer inputs + to determine if the second integer is logically implied by the first. + ''' +} + +example.System_Requirement req_xor +{ + text = ''' + The example shall provide an exclusive or (XOR) operation between two integer inputs + to evaluate scenarios where a decision is based on the condition that exactly one of two inputs must be true (non-zero). + ''' +} diff --git a/integration-tests/projects/filter/test_example.json b/integration-tests/projects/filter/test_example.json new file mode 100644 index 00000000..d3222d02 --- /dev/null +++ b/integration-tests/projects/filter/test_example.json @@ -0,0 +1,31 @@ +[ + {"name" : "Implication Test 1", + "values" : [false, true], + "expectation" : false, + "tags" : "softreq_example.req_implication"}, + + {"name" : "Implication Test 2", + "values" : [true, true], + "expectation" : true, + "tags" : "softreq_example.req_implication"}, + + {"name" : "XOR Test 1", + "values" : [false, false], + "expectation" : false, + "tags" : "softreq_example.req_xor"}, + + {"name" : "XOR Test 2", + "values" : [false, true], + "expectation" : true, + "tags" : "softreq_example.req_xor"}, + + {"name" : "XOR Test 3", + "values" : [true, false], + "expectation" : true, + "tags" : "softreq_example.req_xor"}, + + {"name" : "XOR Test 4", + "values" : [true, true], + "expectation" : false, + "tags" : "softreq_example.req_xor"} +] diff --git a/lobster/config/parser.py b/lobster/config/parser.py index 05eeef2e..9ffca8ba 100644 --- a/lobster/config/parser.py +++ b/lobster/config/parser.py @@ -140,6 +140,11 @@ def parse_level_declaration(self): source_info["filters"].append(("prefix", self.ct.value())) + if self.ct.value() == "kind": + self.match("STRING") + source_info["filters"].append(("kind", + self.ct.value())) + elif self.ct.value() == "valid_status": if level_kind != "requirements": self.error(self.ct.loc, diff --git a/lobster/io.py b/lobster/io.py index 7f94c17d..dea829a5 100644 --- a/lobster/io.py +++ b/lobster/io.py @@ -110,14 +110,24 @@ def lobster_read(mh, filename, level, items, source_info=None): else: item = Activity.from_json(level, raw, data["version"]) - if item.tag.key() in items: - mh.error(item.location, - "duplicate definition of %s, " - "previously defined at %s" % - (item.tag.key(), - items[item.tag.key()].location.to_string())) + filter_conditions = [] if source_info is not None: item.perform_source_checks(source_info) - - items[item.tag.key()] = item + + # evaluate source_info filters + for f, v in source_info['filters']: + if f == 'prefix': + filter_conditions.append(item.tag.tag.startswith(v)) + if f == 'kind': + filter_conditions.append(item.kind == v) + + if all(filter_conditions): + if item.tag.key() in items: + mh.error(item.location, + "duplicate definition of %s, " + "previously defined at %s" % + (item.tag.key(), + items[item.tag.key()].location.to_string())) + + items[item.tag.key()] = item