From 97af3e14b2c5e18935a0783c73ce6419668e6861 Mon Sep 17 00:00:00 2001 From: Tobias Deiminger Date: Fri, 7 Nov 2025 14:51:04 +0100 Subject: [PATCH] feat(backend/sdoc_source_code): Support merge by MID Until now, finding a static merge candidate for source nodes relied on having a UID field set at source code side. Now we can also use MIDs for this purpose. Merge by MID gets priority over merge merge by UID. This requires thinking about several edge cases. See test desriptions for some of them. --- strictdoc/core/file_traceability_index.py | 93 +++++++++++++++++-- .../parent.sdoc | 0 .../source_node_base.sdoc | 2 +- .../src}/example.c | 0 .../strictdoc.toml | 0 .../test.itest | 30 ++++++ .../30_merge_with_static_node/test.itest | 24 ----- .../31_merge_with_sdoc_by_uid/parent.sdoc | 17 ++++ .../source_node_base.sdoc | 72 ++++++++++++++ .../31_merge_with_sdoc_by_uid/src/example.c | 48 ++++++++++ .../31_merge_with_sdoc_by_uid/strictdoc.toml | 14 +++ .../31_merge_with_sdoc_by_uid/test.itest | 58 ++++++++++++ .../32_merge_with_sdoc_by_mid/parent.sdoc | 12 +++ .../source_node_base.sdoc | 54 +++++++++++ .../32_merge_with_sdoc_by_mid/src/example.c | 16 ++++ .../32_merge_with_sdoc_by_mid/strictdoc.toml | 14 +++ .../32_merge_with_sdoc_by_mid/test.itest | 34 +++++++ .../source_node_base.sdoc | 48 ++++++++++ .../src/example.c | 14 +++ .../strictdoc.toml | 14 +++ .../test.itest | 9 ++ .../source_node_base.sdoc | 48 ++++++++++ .../src/example.c | 14 +++ .../strictdoc.toml | 14 +++ .../test.itest | 9 ++ 25 files changed, 624 insertions(+), 34 deletions(-) rename tests/integration/features/source_code_traceability/_source_nodes/{30_merge_with_static_node => 30_merge_with_sdoc_by_default_uid}/parent.sdoc (100%) rename tests/integration/features/source_code_traceability/_source_nodes/{30_merge_with_static_node => 30_merge_with_sdoc_by_default_uid}/source_node_base.sdoc (93%) rename tests/integration/features/source_code_traceability/_source_nodes/{30_merge_with_static_node/src/example => 30_merge_with_sdoc_by_default_uid/src}/example.c (100%) rename tests/integration/features/source_code_traceability/_source_nodes/{30_merge_with_static_node => 30_merge_with_sdoc_by_default_uid}/strictdoc.toml (100%) create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_sdoc_by_default_uid/test.itest delete mode 100644 tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_static_node/test.itest create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/parent.sdoc create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/source_node_base.sdoc create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/src/example.c create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/strictdoc.toml create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/test.itest create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/parent.sdoc create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/source_node_base.sdoc create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/src/example.c create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/strictdoc.toml create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/test.itest create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/source_node_base.sdoc create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/src/example.c create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/strictdoc.toml create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/test.itest create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/source_node_base.sdoc create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/src/example.c create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/strictdoc.toml create mode 100644 tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/test.itest diff --git a/strictdoc/core/file_traceability_index.py b/strictdoc/core/file_traceability_index.py index e97854752..40b1ea7ef 100644 --- a/strictdoc/core/file_traceability_index.py +++ b/strictdoc/core/file_traceability_index.py @@ -47,6 +47,7 @@ from strictdoc.helpers.cast import assert_cast from strictdoc.helpers.exception import StrictDocException from strictdoc.helpers.google_test import convert_function_name_to_gtest_macro +from strictdoc.helpers.mid import MID from strictdoc.helpers.ordered_set import OrderedSet if TYPE_CHECKING: @@ -601,15 +602,36 @@ def validate_and_resolve( continue assert source_node_.entity_name is not None + sdoc_node = None sdoc_node_uid = source_node_.get_sdoc_field( "UID", relevant_source_node_entry ) - if sdoc_node_uid is None: - sdoc_node_uid = f"{document_uid}/{path_to_source_file_}/{source_node_.entity_name}" - sdoc_node = traceability_index.get_node_by_uid_weak( - sdoc_node_uid + mid = source_node_.get_sdoc_field( + "MID", relevant_source_node_entry ) + # First merge criterion: Merge if SDoc node with same MID exists. + if mid is not None: + sdoc_node_mid = MID(mid) + merge_candidate_sdoc_node = ( + traceability_index.get_node_by_mid_weak(sdoc_node_mid) + ) + if isinstance(merge_candidate_sdoc_node, SDocNode): + sdoc_node = merge_candidate_sdoc_node + sdoc_node_uid = sdoc_node.reserved_uid + + if sdoc_node is None: + # If no UID from source code field or merge-by-MID, create UID by conventional scheme. + if sdoc_node_uid is None: + sdoc_node_uid = f"{document_uid}/{path_to_source_file_}/{source_node_.entity_name}" + # Second merge criterion: Merge if SDoc node with same UID exists. + tmp_sdoc_node = traceability_index.get_node_by_uid_weak( + sdoc_node_uid + ) + if isinstance(tmp_sdoc_node, SDocNode): + sdoc_node = tmp_sdoc_node + + assert sdoc_node_uid is not None if sdoc_node is not None: sdoc_node = assert_cast(sdoc_node, SDocNode) self.merge_sdoc_node_with_source_node( @@ -626,11 +648,6 @@ def validate_and_resolve( document, ) sdoc_node_uid = assert_cast(sdoc_node.reserved_uid, str) - traceability_index.graph_database.create_link( - link_type=GraphLinkType.UID_TO_NODE, - lhs_node=sdoc_node_uid, - rhs_node=sdoc_node, - ) if current_top_node is None: current_top_node = ( FileTraceabilityIndex.create_source_node_section( @@ -998,6 +1015,25 @@ def merge_sdoc_node_with_source_node( ) # Merge strategy: overwrite any field if there's a field with same name from custom tags. sdoc_node_fields = source_node.get_sdoc_fields(source_node_config_entry) + + # Sanity check: Nor UID neither MID must conflict (early auto-MID is allowed to be overwritten) + if ( + "MID" in sdoc_node.ordered_fields_lookup + and "MID" in sdoc_node_fields + ): + sdoc_mid_field = sdoc_node.get_field_by_name("MID").get_text_value() + if sdoc_mid_field != sdoc_node_fields["MID"]: + raise StrictDocException( + f"Can't merge node by UID {sdoc_node.reserved_uid}: " + f"Conflicting MID: {sdoc_mid_field} != {sdoc_node_fields['MID']}" + ) + if sdoc_node.reserved_uid is not None and "UID" in sdoc_node_fields: + if sdoc_node.reserved_uid != sdoc_node_fields["UID"]: + raise StrictDocException( + f"Can't merge node by MID {sdoc_node.reserved_mid}: " + f"Conflicting UID: {sdoc_node.reserved_uid} != {sdoc_node_fields['UID']}" + ) + FileTraceabilityIndex.set_sdoc_node_fields(sdoc_node, sdoc_node_fields) @staticmethod @@ -1081,6 +1117,45 @@ def connect_source_node_requirements( Here we link REQ and sdoc_node bidirectional. """ + if ( + sdoc_node.reserved_uid is not None + and not traceability_index.graph_database.has_link( + link_type=GraphLinkType.UID_TO_NODE, + lhs_node=sdoc_node.reserved_uid, + rhs_node=sdoc_node, + ) + ): + traceability_index.graph_database.create_link( + link_type=GraphLinkType.UID_TO_NODE, + lhs_node=sdoc_node.reserved_uid, + rhs_node=sdoc_node, + ) + + # A merge procedure may have overwritten the MID, + # in which case the graph database and search index needs an update. + if "MID" in sdoc_node.ordered_fields_lookup != sdoc_node.reserved_mid: + sdoc_mid_field = sdoc_node.get_field_by_name("MID").get_text_value() + if sdoc_mid_field != sdoc_node.reserved_mid: + # TODO: + # If we really want to support changing the auto-assigned MID, + # at least the graph database and the document search index need an update (remove old MID, add new MID). + # I currently struggle to update the search index. + parent_document = sdoc_node.get_parent_or_including_document() + sdoc_node.reserved_mid = MID(sdoc_mid_field) + if parent_document.config.enable_mid: + sdoc_node.mid_permanent = True + + if not traceability_index.graph_database.has_link( + link_type=GraphLinkType.MID_TO_NODE, + lhs_node=sdoc_node.reserved_mid, + rhs_node=sdoc_node, + ): + traceability_index.graph_database.create_link( + link_type=GraphLinkType.MID_TO_NODE, + lhs_node=sdoc_node.reserved_mid, + rhs_node=sdoc_node, + ) + for marker_ in source_node.markers: if not isinstance(marker_, FunctionRangeMarker): continue diff --git a/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_static_node/parent.sdoc b/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_sdoc_by_default_uid/parent.sdoc similarity index 100% rename from tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_static_node/parent.sdoc rename to tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_sdoc_by_default_uid/parent.sdoc diff --git a/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_static_node/source_node_base.sdoc b/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_sdoc_by_default_uid/source_node_base.sdoc similarity index 93% rename from tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_static_node/source_node_base.sdoc rename to tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_sdoc_by_default_uid/source_node_base.sdoc index 22caeec03..cd74e7c5b 100644 --- a/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_static_node/source_node_base.sdoc +++ b/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_sdoc_by_default_uid/source_node_base.sdoc @@ -39,7 +39,7 @@ ELEMENTS: TITLE: Merge example.c into static nodes [REQUIREMENT] -UID: SRC-NODES-BASE/src/example/example.c/example_1 +UID: SRC-NODES-BASE/src/example.c/example_1 TITLE: TITLE from sdoc FOO: FOO text from sdoc BAR: BAR text from sdoc diff --git a/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_static_node/src/example/example.c b/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_sdoc_by_default_uid/src/example.c similarity index 100% rename from tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_static_node/src/example/example.c rename to tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_sdoc_by_default_uid/src/example.c diff --git a/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_static_node/strictdoc.toml b/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_sdoc_by_default_uid/strictdoc.toml similarity index 100% rename from tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_static_node/strictdoc.toml rename to tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_sdoc_by_default_uid/strictdoc.toml diff --git a/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_sdoc_by_default_uid/test.itest b/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_sdoc_by_default_uid/test.itest new file mode 100644 index 000000000..37db9c53f --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_sdoc_by_default_uid/test.itest @@ -0,0 +1,30 @@ +# +# This test verifies that a source nodes is merged with a static SDoc node if +# - the source nodes is not marked up with a UID field (i.e., default UID is effective), and +# - static SDoc node was explicitly given the default UID. +# +# @relation(SDOC-SRS-141, scope=file) +# + +RUN: %strictdoc --debug export %S --output-dir %T | filecheck %s + +CHECK: Published: Hello world doc + +RUN: %check_exists --file "%T/html/_source_files/src/example.c.html" + +RUN: %cat %T/html/%THIS_TEST_FOLDER/source_node_base.html | filecheck %s --check-prefix CHECK-HTML +CHECK-HTML: Requirements from Source Nodes +CHECK-HTML: SRC-NODES-BASE/src/example.c/example_1 +CHECK-HTML: TITLE from sdoc +CHECK-HTML: class="requirement__link-parent" href="../30_merge_with_sdoc_by_default_uid/parent.html#REQ-1" +CHECK-HTML: src/example.c, lines: 3-14, function example_1() +CHECK-HTML-NOT: FOO text from sdoc +CHECK-HTML: FOO text from example.c +CHECK-HTML-NOT: BAR text from sdoc +CHECK-HTML: BAR text from example.c + +RUN: %cat %T/html/_source_files/src/example.c.html | filecheck %s --check-prefix CHECK-SOURCE-FILE +CHECK-SOURCE-FILE: SRC-NODES-BASE/src/example.c/example_1 + +RUN: %cat %T/html/source_coverage.html | filecheck %s --check-prefix CHECK-SOURCE-COVERAGE +CHECK-SOURCE-COVERAGE: 100.0 diff --git a/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_static_node/test.itest b/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_static_node/test.itest deleted file mode 100644 index edd3709f2..000000000 --- a/tests/integration/features/source_code_traceability/_source_nodes/30_merge_with_static_node/test.itest +++ /dev/null @@ -1,24 +0,0 @@ -# -# @relation(SDOC-SRS-141, scope=file) -# - -RUN: %strictdoc --debug export %S --output-dir %T | filecheck %s - -CHECK: Published: Hello world doc - -RUN: %check_exists --file "%T/html/_source_files/src/example/example.c.html" - -RUN: %cat %T/html/%THIS_TEST_FOLDER/source_node_base.html | filecheck %s --check-prefix CHECK-HTML -CHECK-HTML: Requirements from Source Nodes -CHECK-HTML: SRC-NODES-BASE/src/example/example.c/example_1 -CHECK-HTML: {{.*}}TITLE from sdoc -CHECK-HTML: class="requirement__link-parent" href="../30_merge_with_static_node/parent.html#REQ-1" -CHECK-HTML: src/example/example.c, lines: 3-14, function example_1() -CHECK-HTML: FOO text from example.c -CHECK-HTML: BAR text from example.c - -RUN: %cat %T/html/_source_files/src/example/example.c.html | filecheck %s --check-prefix CHECK-SOURCE-FILE -CHECK-SOURCE-FILE: SRC-NODES-BASE/src/example/example.c/example_1 - -RUN: %cat %T/html/source_coverage.html | filecheck %s --check-prefix CHECK-SOURCE-COVERAGE -CHECK-SOURCE-COVERAGE: 100.0 diff --git a/tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/parent.sdoc b/tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/parent.sdoc new file mode 100644 index 000000000..7eff27bf2 --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/parent.sdoc @@ -0,0 +1,17 @@ +[DOCUMENT] +TITLE: Hello world doc + +[REQUIREMENT] +UID: REQ-1 +TITLE: Requirement Title +STATEMENT: Requirement Statement + +[REQUIREMENT] +UID: REQ-2 +TITLE: Requirement Title #2 +STATEMENT: Requirement Statement #2 + +[REQUIREMENT] +UID: REQ-3 +TITLE: Requirement Title #3 +STATEMENT: Requirement Statement #3 diff --git a/tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/source_node_base.sdoc b/tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/source_node_base.sdoc new file mode 100644 index 000000000..b27f68bbc --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/source_node_base.sdoc @@ -0,0 +1,72 @@ +[DOCUMENT] +MID: c2d4542d5f1741c88dfcb4f68ad7dcbd +TITLE: Requirements from Source Nodes +UID: SRC-NODES-BASE + +[GRAMMAR] +ELEMENTS: +- TAG: SECTION + PROPERTIES: + IS_COMPOSITE: True + FIELDS: + - TITLE: UID + TYPE: String + REQUIRED: False + - TITLE: TITLE + TYPE: String + REQUIRED: True +- TAG: REQUIREMENT + PROPERTIES: + VIEW_STYLE: Narrative + FIELDS: + - TITLE: UID + TYPE: String + REQUIRED: False + - TITLE: MID + TYPE: String + REQUIRED: False + - TITLE: TITLE + TYPE: String + REQUIRED: False + - TITLE: FOO + TYPE: String + REQUIRED: False + - TITLE: BAR + TYPE: String + REQUIRED: False + RELATIONS: + - TYPE: Parent + - TYPE: File + +[[SECTION]] +TITLE: Merge example.c into static nodes + +[REQUIREMENT] +UID: REQ-SOURCE-1 +TITLE: TITLE1 from sdoc +FOO: FOO1 text from sdoc +BAR: BAR1 text from sdoc +RELATIONS: +- TYPE: Parent + VALUE: REQ-1 + +[REQUIREMENT] +UID: REQ-SOURCE-2 +MID: 80cd685d-0e18-44b8-9842-c1863a2eb9ec +TITLE: TITLE2 from sdoc +FOO: FOO2 text from sdoc +BAR: BAR2 text from sdoc +RELATIONS: +- TYPE: Parent + VALUE: REQ-2 + +[REQUIREMENT] +UID: REQ-SOURCE-3 +TITLE: TITLE3 from sdoc +FOO: FOO3 text from sdoc +BAR: BAR3 text from sdoc +RELATIONS: +- TYPE: Parent + VALUE: REQ-3 + +[[/SECTION]] diff --git a/tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/src/example.c b/tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/src/example.c new file mode 100644 index 000000000..fa900f484 --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/src/example.c @@ -0,0 +1,48 @@ +#include + +/** + * Some text. + * + * @relation(REQ-1, scope=function) + * + * UID: REQ-SOURCE-1 + * + * FOO: FOO1 text from example.c + * + * BAR: BAR1 text from example.c + */ +void example_1(void) { + print("hello world\n"); +} + +/** + * Some text. + * + * @relation(REQ-2, scope=function) + * + * UID: REQ-SOURCE-2 + * + * FOO: FOO2 text from example.c + * + * BAR: BAR2 text from example.c + */ +void example_2(void) { + print("hello world\n"); +} + +/** + * Some text. + * + * @relation(REQ-3, scope=function) + * + * UID: REQ-SOURCE-3 + * + * MID: 1973a567-a109-491d-b7f0-6bb22eafa6ab + * + * FOO: FOO3 text from example.c + * + * BAR: BAR3 text from example.c + */ +void example_3(void) { + print("hello world\n"); +} diff --git a/tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/strictdoc.toml b/tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/strictdoc.toml new file mode 100644 index 000000000..f8688a0cd --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/strictdoc.toml @@ -0,0 +1,14 @@ +[project] + +features = [ + "REQUIREMENT_TO_SOURCE_TRACEABILITY", + "SOURCE_FILE_LANGUAGE_PARSERS", +] + +source_nodes = [ + { "src/" = { uid = "SRC-NODES-BASE", node_type = "REQUIREMENT" } } +] + +exclude_source_paths = [ + "test.itest" +] diff --git a/tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/test.itest b/tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/test.itest new file mode 100644 index 000000000..6c426ff6b --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/31_merge_with_sdoc_by_uid/test.itest @@ -0,0 +1,58 @@ +# +# This test verifies that a source nodes is merged with a static SDoc node with same explicit UID annotated. +# The following cases shall be handled: +# - If MID is set at neither side, a random (but hidden) automatic MID is assigned by StrictDoc. +# - If MID is set only at the sdoc side, the MID remains effective. +# - If MID is set only at the source side, early random automatic MID is overwritten by source side MID. +# +# Cases tested elsewhere: +# - If MID is set on both sides and is equal, this is considered "merge by mid". +# - If MID is set on both sides but is different, this is considered a validation error. +# +# @relation(SDOC-SRS-141, scope=file) +# + +RUN: %strictdoc --debug export %S --output-dir %T | filecheck %s + +CHECK: Published: Hello world doc + +RUN: %check_exists --file "%T/html/_source_files/src/example.c.html" + +RUN: %cat %T/html/%THIS_TEST_FOLDER/source_node_base.html | filecheck %s --check-prefix CHECK-HTML +CHECK-HTML: Requirements from Source Nodes +CHECK-HTML-NOT: SRC-NODES-BASE/src/example.c/example_1 +CHECK-HTML: REQ-SOURCE-1 +CHECK-HTML: TITLE1 from sdoc +CHECK-HTML: src/example.c, lines: 3-16, function example_1() +CHECK-HTML-NOT: FOO1 text from sdoc +CHECK-HTML: FOO1 text from example.c +CHECK-HTML-NOT: BAR1 text from sdoc +CHECK-HTML: BAR1 text from example.c + +CHECK-HTML-NOT: SRC-NODES-BASE/src/example.c/example_2 +CHECK-HTML: REQ-SOURCE-2 +CHECK-HTML: TITLE2 from sdoc +CHECK-HTML: src/example.c, lines: 18-31, function example_2() +CHECK-HTML-NOT: FOO2 text from sdoc +CHECK-HTML: FOO2 text from example.c +CHECK-HTML-NOT: BAR2 text from sdoc +CHECK-HTML: BAR2 text from example.c + +CHECK-HTML-NOT: SRC-NODES-BASE/src/example.c/example_3 +CHECK-HTML: REQ-SOURCE-3 +CHECK-HTML: TITLE3 from sdoc +CHECK-HTML: 1973a567-a109-491d-b7f0-6bb22eafa6ab +CHECK-HTML: src/example.c, lines: 33-48, function example_3() +CHECK-HTML-NOT: FOO3 text from sdoc +CHECK-HTML: FOO3 text from example.c +CHECK-HTML-NOT: BAR3 text from sdoc +CHECK-HTML: BAR3 text from example.c + +RUN: %cat %T/html/_source_files/src/example.c.html | filecheck %s --check-prefix CHECK-SOURCE-FILE +CHECK-HTML-NOT: SRC-NODES-BASE/src/example.c/example_1 +CHECK-SOURCE-FILE: REQ-SOURCE-1 +CHECK-SOURCE-FILE: REQ-SOURCE-2 +CHECK-SOURCE-FILE: REQ-SOURCE-3 + +RUN: %cat %T/html/source_coverage.html | filecheck %s --check-prefix CHECK-SOURCE-COVERAGE +CHECK-SOURCE-COVERAGE: 100.0 diff --git a/tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/parent.sdoc b/tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/parent.sdoc new file mode 100644 index 000000000..278b98a77 --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/parent.sdoc @@ -0,0 +1,12 @@ +[DOCUMENT] +TITLE: Hello world doc + +[REQUIREMENT] +UID: REQ-1 +TITLE: Requirement Title +STATEMENT: Requirement Statement + +[REQUIREMENT] +UID: REQ-2 +TITLE: Requirement Title #2 +STATEMENT: Requirement Statement #2 diff --git a/tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/source_node_base.sdoc b/tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/source_node_base.sdoc new file mode 100644 index 000000000..c6c84f958 --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/source_node_base.sdoc @@ -0,0 +1,54 @@ +[DOCUMENT] +MID: c2d4542d5f1741c88dfcb4f68ad7dcbd +TITLE: Requirements from Source Nodes +UID: SRC-NODES-BASE + +[GRAMMAR] +ELEMENTS: +- TAG: SECTION + PROPERTIES: + IS_COMPOSITE: True + FIELDS: + - TITLE: UID + TYPE: String + REQUIRED: False + - TITLE: TITLE + TYPE: String + REQUIRED: True +- TAG: REQUIREMENT + PROPERTIES: + VIEW_STYLE: Narrative + FIELDS: + - TITLE: UID + TYPE: String + REQUIRED: False + - TITLE: MID + TYPE: String + REQUIRED: False + - TITLE: TITLE + TYPE: String + REQUIRED: False + - TITLE: FOO + TYPE: String + REQUIRED: False + - TITLE: BAR + TYPE: String + REQUIRED: False + RELATIONS: + - TYPE: Parent + - TYPE: File + +[[SECTION]] +TITLE: Merge example.c into static nodes + +[REQUIREMENT] +UID: REQ-SOURCE-1 +MID: 3bd0162d-d6d2-42a1-9324-ab8415190970 +TITLE: TITLE from sdoc +FOO: FOO text from sdoc +BAR: BAR text from sdoc +RELATIONS: +- TYPE: Parent + VALUE: REQ-1 + +[[/SECTION]] diff --git a/tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/src/example.c b/tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/src/example.c new file mode 100644 index 000000000..0fe5bf23e --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/src/example.c @@ -0,0 +1,16 @@ +#include + +/** + * Some text. + * + * @relation(REQ-1, scope=function) + * + * MID: 3bd0162d-d6d2-42a1-9324-ab8415190970 + * + * FOO: FOO text from example.c + * + * BAR: BAR text from example.c + */ +void example_1(void) { + print("hello world\n"); +} diff --git a/tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/strictdoc.toml b/tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/strictdoc.toml new file mode 100644 index 000000000..f8688a0cd --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/strictdoc.toml @@ -0,0 +1,14 @@ +[project] + +features = [ + "REQUIREMENT_TO_SOURCE_TRACEABILITY", + "SOURCE_FILE_LANGUAGE_PARSERS", +] + +source_nodes = [ + { "src/" = { uid = "SRC-NODES-BASE", node_type = "REQUIREMENT" } } +] + +exclude_source_paths = [ + "test.itest" +] diff --git a/tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/test.itest b/tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/test.itest new file mode 100644 index 000000000..aefd72bf4 --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/32_merge_with_sdoc_by_mid/test.itest @@ -0,0 +1,34 @@ +# +# This test verifies that a source nodes is merged with a static SDoc node with same explicit MID annotated. +# The following case shall be handled: +# - If MID is set and equal at both sides and no UID update is required, merge the node. +# +# Cases tested elsewhere: +# - If MID is set and equal on both sides but UID update would be required, this is considered a validation error. +# +# @relation(SDOC-SRS-141, scope=file) +# + +RUN: %strictdoc --debug export %S --output-dir %T | filecheck %s + +CHECK: Published: Hello world doc + +RUN: %check_exists --file "%T/html/_source_files/src/example.c.html" + +RUN: %cat %T/html/%THIS_TEST_FOLDER/source_node_base.html | filecheck %s --check-prefix CHECK-HTML +CHECK-HTML: Requirements from Source Nodes +CHECK-HTML-NOT: SRC-NODES-BASE/src/example.c/example_1 +CHECK-HTML: 3bd0162d-d6d2-42a1-9324-ab8415190970 +CHECK-HTML: TITLE from sdoc +CHECK-HTML: src/example.c, lines: 3-16, function example_1() +CHECK-HTML-NOT: FOO text from sdoc +CHECK-HTML: FOO text from example.c +CHECK-HTML-NOT: BAR text from sdoc +CHECK-HTML: BAR text from example.c + +RUN: %cat %T/html/_source_files/src/example.c.html | filecheck %s --check-prefix CHECK-SOURCE-FILE +CHECK-HTML-NOT: SRC-NODES-BASE/src/example.c/example_1 +CHECK-SOURCE-FILE: 3bd0162d-d6d2-42a1-9324-ab8415190970 + +RUN: %cat %T/html/source_coverage.html | filecheck %s --check-prefix CHECK-SOURCE-COVERAGE +CHECK-SOURCE-COVERAGE: 100.0 diff --git a/tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/source_node_base.sdoc b/tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/source_node_base.sdoc new file mode 100644 index 000000000..e00351a94 --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/source_node_base.sdoc @@ -0,0 +1,48 @@ +[DOCUMENT] +MID: c2d4542d5f1741c88dfcb4f68ad7dcbd +TITLE: Requirements from Source Nodes +UID: SRC-NODES-BASE + +[GRAMMAR] +ELEMENTS: +- TAG: SECTION + PROPERTIES: + IS_COMPOSITE: True + FIELDS: + - TITLE: UID + TYPE: String + REQUIRED: False + - TITLE: TITLE + TYPE: String + REQUIRED: True +- TAG: REQUIREMENT + PROPERTIES: + VIEW_STYLE: Narrative + FIELDS: + - TITLE: UID + TYPE: String + REQUIRED: False + - TITLE: MID + TYPE: String + REQUIRED: False + - TITLE: TITLE + TYPE: String + REQUIRED: False + - TITLE: FOO + TYPE: String + REQUIRED: False + - TITLE: BAR + TYPE: String + REQUIRED: False + +[[SECTION]] +TITLE: Merge example.c into static nodes + +[REQUIREMENT] +UID: REQ-SOURCE-1 +MID: 3bd0162d-d6d2-42a1-9324-ab8415190970 +TITLE: TITLE from sdoc +FOO: FOO text from sdoc +BAR: BAR text from sdoc + +[[/SECTION]] diff --git a/tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/src/example.c b/tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/src/example.c new file mode 100644 index 000000000..fcc707090 --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/src/example.c @@ -0,0 +1,14 @@ +#include + +/** + * UID: REQ-SOURCE-1 + * + * MID: e7c09b63-2bed-4046-9511-a5b17978f152 + * + * FOO: FOO text from example.c + * + * BAR: BAR text from example.c + */ +void example_1(void) { + print("hello world\n"); +} diff --git a/tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/strictdoc.toml b/tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/strictdoc.toml new file mode 100644 index 000000000..f8688a0cd --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/strictdoc.toml @@ -0,0 +1,14 @@ +[project] + +features = [ + "REQUIREMENT_TO_SOURCE_TRACEABILITY", + "SOURCE_FILE_LANGUAGE_PARSERS", +] + +source_nodes = [ + { "src/" = { uid = "SRC-NODES-BASE", node_type = "REQUIREMENT" } } +] + +exclude_source_paths = [ + "test.itest" +] diff --git a/tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/test.itest b/tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/test.itest new file mode 100644 index 000000000..bc72f832e --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/_validation/03_merge_by_uid_conflicting_mid/test.itest @@ -0,0 +1,9 @@ +# +# This test verifies that if SDoc node and source node have same UID and thus are +# subject to be merged, an error is thrown if both sides set also MID and the MIDs are not equal. +# +# @relation(SDOC-SRS-141, scope=file) +# + +RUN: %expect_exit 1 %strictdoc export %S --output-dir %T | filecheck %s --dump-input=fail +CHECK: error: Can't merge node by UID REQ-SOURCE-1: Conflicting MID: 3bd0162d-d6d2-42a1-9324-ab8415190970 != e7c09b63-2bed-4046-9511-a5b17978f152 diff --git a/tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/source_node_base.sdoc b/tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/source_node_base.sdoc new file mode 100644 index 000000000..e00351a94 --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/source_node_base.sdoc @@ -0,0 +1,48 @@ +[DOCUMENT] +MID: c2d4542d5f1741c88dfcb4f68ad7dcbd +TITLE: Requirements from Source Nodes +UID: SRC-NODES-BASE + +[GRAMMAR] +ELEMENTS: +- TAG: SECTION + PROPERTIES: + IS_COMPOSITE: True + FIELDS: + - TITLE: UID + TYPE: String + REQUIRED: False + - TITLE: TITLE + TYPE: String + REQUIRED: True +- TAG: REQUIREMENT + PROPERTIES: + VIEW_STYLE: Narrative + FIELDS: + - TITLE: UID + TYPE: String + REQUIRED: False + - TITLE: MID + TYPE: String + REQUIRED: False + - TITLE: TITLE + TYPE: String + REQUIRED: False + - TITLE: FOO + TYPE: String + REQUIRED: False + - TITLE: BAR + TYPE: String + REQUIRED: False + +[[SECTION]] +TITLE: Merge example.c into static nodes + +[REQUIREMENT] +UID: REQ-SOURCE-1 +MID: 3bd0162d-d6d2-42a1-9324-ab8415190970 +TITLE: TITLE from sdoc +FOO: FOO text from sdoc +BAR: BAR text from sdoc + +[[/SECTION]] diff --git a/tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/src/example.c b/tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/src/example.c new file mode 100644 index 000000000..02a01bd76 --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/src/example.c @@ -0,0 +1,14 @@ +#include + +/** + * UID: REQ-SOURCE-2 + * + * MID: 3bd0162d-d6d2-42a1-9324-ab8415190970 + * + * FOO: FOO text from example.c + * + * BAR: BAR text from example.c + */ +void example_1(void) { + print("hello world\n"); +} diff --git a/tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/strictdoc.toml b/tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/strictdoc.toml new file mode 100644 index 000000000..f8688a0cd --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/strictdoc.toml @@ -0,0 +1,14 @@ +[project] + +features = [ + "REQUIREMENT_TO_SOURCE_TRACEABILITY", + "SOURCE_FILE_LANGUAGE_PARSERS", +] + +source_nodes = [ + { "src/" = { uid = "SRC-NODES-BASE", node_type = "REQUIREMENT" } } +] + +exclude_source_paths = [ + "test.itest" +] diff --git a/tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/test.itest b/tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/test.itest new file mode 100644 index 000000000..48cb05075 --- /dev/null +++ b/tests/integration/features/source_code_traceability/_source_nodes/_validation/04_merge_by_mid_conflicting_uid/test.itest @@ -0,0 +1,9 @@ +# +# This test verifies that if SDoc node and source node have same MID and thus are +# subject to be merged, an error is thrown if both sides set also UID and the UID are not equal. +# +# @relation(SDOC-SRS-141, scope=file) +# + +RUN: %expect_exit 1 %strictdoc export %S --output-dir %T | filecheck %s --dump-input=fail +CHECK: error: Can't merge node by MID 3bd0162d-d6d2-42a1-9324-ab8415190970: Conflicting UID: REQ-SOURCE-1 != REQ-SOURCE-2