From 1e35c2122a2724e587631e9876b2beb4b5060be2 Mon Sep 17 00:00:00 2001 From: jce <28319872+JasperCraeghs@users.noreply.github.com> Date: Thu, 26 Oct 2023 15:14:08 +0200 Subject: [PATCH 1/9] Fix crash when traceability_relationship_to_string is incomplete --- mlx/directives/item_directive.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mlx/directives/item_directive.py b/mlx/directives/item_directive.py index 8d3e46c8..596a6f64 100644 --- a/mlx/directives/item_directive.py +++ b/mlx/directives/item_directive.py @@ -79,9 +79,7 @@ def _list_targets_for_relation(self, relation, targets, dl_node, app): if relation in app.config.traceability_relationship_to_string: relstr = app.config.traceability_relationship_to_string[relation] else: - report_warning('Traceability: relation {rel} cannot be translated to string' - .format(rel=relation), - env.docname, self.line) + report_warning(f'Traceability: relation {relation} cannot be translated to string') relstr = relation dt_node.append(nodes.Text(relstr)) li_node.append(dt_node) From ecea7a24d2b35c8ea6db1908c1950484c00d69dd Mon Sep 17 00:00:00 2001 From: jce <28319872+JasperCraeghs@users.noreply.github.com> Date: Fri, 27 Oct 2023 09:13:53 +0200 Subject: [PATCH 2/9] Remove unused variable --- mlx/directives/item_directive.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mlx/directives/item_directive.py b/mlx/directives/item_directive.py index 596a6f64..f5fb99d0 100644 --- a/mlx/directives/item_directive.py +++ b/mlx/directives/item_directive.py @@ -73,7 +73,6 @@ def _list_targets_for_relation(self, relation, targets, dl_node, app): dl_node (nodes.definition_list): Definition list of the item. app: Sphinx's application object to use. """ - env = app.builder.env li_node = nodes.definition_list_item() dt_node = nodes.term() if relation in app.config.traceability_relationship_to_string: From af63e9145afb8897f8cc7aa0f878cbd6a1e9cfd9 Mon Sep 17 00:00:00 2001 From: jce <28319872+JasperCraeghs@users.noreply.github.com> Date: Fri, 27 Oct 2023 09:14:28 +0200 Subject: [PATCH 3/9] Fix typo in error message --- mlx/traceability.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlx/traceability.py b/mlx/traceability.py index cc7b2c9e..f56769a5 100644 --- a/mlx/traceability.py +++ b/mlx/traceability.py @@ -324,7 +324,7 @@ def initialize_environment(app): undefined_stringifications = all_relationships.difference(app.config.traceability_relationship_to_string) if undefined_stringifications: raise TraceabilityException(f"Relationships {undefined_stringifications!r} are missing from configuration " - "variable `traceability_relationships`") + "variable `traceability_relationship_to_string`") app.config.traceability_checklist['has_checklist_items'] = False add_checklist_attribute(app.config.traceability_checklist, From edc1c676d60f930a0c253c2d3c0d3bae785d8834 Mon Sep 17 00:00:00 2001 From: jce <28319872+JasperCraeghs@users.noreply.github.com> Date: Fri, 27 Oct 2023 09:18:39 +0200 Subject: [PATCH 4/9] Include document and lineno to warning --- mlx/directives/item_directive.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mlx/directives/item_directive.py b/mlx/directives/item_directive.py index f5fb99d0..e12d7ccd 100644 --- a/mlx/directives/item_directive.py +++ b/mlx/directives/item_directive.py @@ -78,7 +78,8 @@ def _list_targets_for_relation(self, relation, targets, dl_node, app): if relation in app.config.traceability_relationship_to_string: relstr = app.config.traceability_relationship_to_string[relation] else: - report_warning(f'Traceability: relation {relation} cannot be translated to string') + report_warning(f'Traceability: relation {relation} cannot be translated to string', + docname=self['document'], lineno=self['line']) relstr = relation dt_node.append(nodes.Text(relstr)) li_node.append(dt_node) From 93b28a4adffd7b9883b97f38544c4deaaa1803df Mon Sep 17 00:00:00 2001 From: jce <28319872+JasperCraeghs@users.noreply.github.com> Date: Fri, 27 Oct 2023 09:24:23 +0200 Subject: [PATCH 5/9] Replace backtick by quote in error message --- mlx/traceability.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlx/traceability.py b/mlx/traceability.py index f56769a5..a6bb1d68 100644 --- a/mlx/traceability.py +++ b/mlx/traceability.py @@ -324,7 +324,7 @@ def initialize_environment(app): undefined_stringifications = all_relationships.difference(app.config.traceability_relationship_to_string) if undefined_stringifications: raise TraceabilityException(f"Relationships {undefined_stringifications!r} are missing from configuration " - "variable `traceability_relationship_to_string`") + "variable 'traceability_relationship_to_string'") app.config.traceability_checklist['has_checklist_items'] = False add_checklist_attribute(app.config.traceability_checklist, From b1e79eb0edcf7b426e1f55873c0987a5176ac981 Mon Sep 17 00:00:00 2001 From: jce <28319872+JasperCraeghs@users.noreply.github.com> Date: Fri, 27 Oct 2023 10:20:51 +0200 Subject: [PATCH 6/9] Test Item.perform_replacement --- tests/directives/test_item_directive.py | 40 +++++++++++++++++++++---- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/tests/directives/test_item_directive.py b/tests/directives/test_item_directive.py index 2a656fc4..b19a7dbc 100644 --- a/tests/directives/test_item_directive.py +++ b/tests/directives/test_item_directive.py @@ -1,6 +1,8 @@ from unittest import TestCase from unittest.mock import Mock, MagicMock +import logging +from docutils import nodes from sphinx.application import Sphinx from sphinx.builders.latex import LaTeXBuilder from sphinx.builders.html import StandaloneHTMLBuilder @@ -13,6 +15,7 @@ from parameterized import parameterized +LOGGER = logging.getLogger() def raise_no_uri(*args, **kwargs): raise NoUri @@ -31,13 +34,14 @@ def setUp(self): self.item.node = self.node self.app.config = Mock() self.app.config.traceability_hyperlink_colors = {} + self.collection = TraceableCollection() def test_make_internal_item_ref_no_caption(self): mock_builder = MagicMock(spec=StandaloneHTMLBuilder) mock_builder.link_suffix = '.html' mock_builder.env = BuildEnvironment(self.app) self.app.builder = mock_builder - self.app.builder.env.traceability_collection = TraceableCollection() + self.app.builder.env.traceability_collection = self.collection self.app.builder.env.traceability_ref_nodes = {} self.app.builder.env.traceability_collection.add_item(self.item) p_node = self.node.make_internal_item_ref(self.app, self.node['id']) @@ -58,7 +62,7 @@ def test_make_internal_item_ref_show_caption(self): mock_builder.link_suffix = '.html' mock_builder.env = BuildEnvironment(self.app) self.app.builder = mock_builder - self.app.builder.env.traceability_collection = TraceableCollection() + self.app.builder.env.traceability_collection = self.collection self.app.builder.env.traceability_ref_nodes = {} self.app.builder.env.traceability_collection.add_item(self.item) self.item.caption = 'caption text' @@ -79,7 +83,7 @@ def test_make_internal_item_ref_only_caption(self): mock_builder.link_suffix = '.html' mock_builder.env = BuildEnvironment(self.app) self.app.builder = mock_builder - self.app.builder.env.traceability_collection = TraceableCollection() + self.app.builder.env.traceability_collection = self.collection self.app.builder.env.traceability_ref_nodes = {} self.app.builder.env.traceability_collection.add_item(self.item) self.item.caption = 'caption text' @@ -104,7 +108,7 @@ def test_make_internal_item_ref_hide_caption_html(self): mock_builder.link_suffix = '.html' mock_builder.env = BuildEnvironment(self.app) self.app.builder = mock_builder - self.app.builder.env.traceability_collection = TraceableCollection() + self.app.builder.env.traceability_collection = self.collection self.app.builder.env.traceability_ref_nodes = {} self.app.builder.env.traceability_collection.add_item(self.item) self.item.caption = 'caption text' @@ -128,7 +132,7 @@ def test_make_internal_item_ref_hide_caption_latex(self): mock_builder.get_relative_uri = Mock(side_effect=raise_no_uri) mock_builder.env = BuildEnvironment(self.app) self.app.builder = mock_builder - self.app.builder.env.traceability_collection = TraceableCollection() + self.app.builder.env.traceability_collection = self.collection self.app.builder.env.traceability_ref_nodes = {} self.app.builder.env.traceability_collection.add_item(self.item) self.item.caption = 'caption text' @@ -154,3 +158,29 @@ def test_make_internal_item_ref_hide_caption_latex(self): def test_is_relation_external(self, relation_name, expected): external = self.node.is_relation_external(relation_name) self.assertEqual(external, expected) + + def test_item_node_replacement(self): + mock_builder = MagicMock(spec=StandaloneHTMLBuilder) + mock_builder.link_suffix = '.html' + mock_builder.env = BuildEnvironment(self.app) + self.app.builder = mock_builder + self.app.builder.env.traceability_collection = self.collection + self.app.builder.env.traceability_ref_nodes = {} + self.app.builder.env.traceability_collection.add_item(self.item) + self.collection.add_relation_pair('depends_on', 'impacts_on') + # leaving out depends_on to test warning + self.app.config.traceability_relationship_to_string = {'impacts_on': 'Impacts on'} + + target_item = TraceableItem('target_id') + self.collection.add_item(target_item) + + self.collection.add_relation(self.item.identifier, 'depends_on', target_item.identifier) + + with self.assertLogs(LOGGER, logging.DEBUG) as c_m: + self.node.parent = nodes.container() + self.node.parent.append(self.node) + self.node.perform_replacement(self.app, self.collection) + + warning = "WARNING:sphinx.mlx.traceability_exception:Traceability: relation depends_on cannot be translated "\ + "to string" + self.assertEqual(c_m.output, [warning]) From 8f3b76b83e725b49cea481a95c9df470ffb5fea1 Mon Sep 17 00:00:00 2001 From: jce <28319872+JasperCraeghs@users.noreply.github.com> Date: Fri, 27 Oct 2023 10:30:24 +0200 Subject: [PATCH 7/9] Refactor test suite by creating init_builder --- tests/directives/test_item_directive.py | 55 +++++++------------------ 1 file changed, 15 insertions(+), 40 deletions(-) diff --git a/tests/directives/test_item_directive.py b/tests/directives/test_item_directive.py index b19a7dbc..b1def5fc 100644 --- a/tests/directives/test_item_directive.py +++ b/tests/directives/test_item_directive.py @@ -35,15 +35,21 @@ def setUp(self): self.app.config = Mock() self.app.config.traceability_hyperlink_colors = {} self.collection = TraceableCollection() - - def test_make_internal_item_ref_no_caption(self): - mock_builder = MagicMock(spec=StandaloneHTMLBuilder) - mock_builder.link_suffix = '.html' + self.collection.add_item(self.item) + + def init_builder(self, spec=StandaloneHTMLBuilder): + mock_builder = MagicMock(spec=spec) + if spec == StandaloneHTMLBuilder: + mock_builder.link_suffix = '.html' + else: + mock_builder.get_relative_uri = Mock(side_effect=raise_no_uri) mock_builder.env = BuildEnvironment(self.app) self.app.builder = mock_builder self.app.builder.env.traceability_collection = self.collection self.app.builder.env.traceability_ref_nodes = {} - self.app.builder.env.traceability_collection.add_item(self.item) + + def test_make_internal_item_ref_no_caption(self): + self.init_builder() p_node = self.node.make_internal_item_ref(self.app, self.node['id']) ref_node = p_node.children[0] em_node = ref_node.children[0] @@ -58,13 +64,7 @@ def test_make_internal_item_ref_no_caption(self): self.assertNotIn('onlycaptions', cache) def test_make_internal_item_ref_show_caption(self): - mock_builder = MagicMock(spec=StandaloneHTMLBuilder) - mock_builder.link_suffix = '.html' - mock_builder.env = BuildEnvironment(self.app) - self.app.builder = mock_builder - self.app.builder.env.traceability_collection = self.collection - self.app.builder.env.traceability_ref_nodes = {} - self.app.builder.env.traceability_collection.add_item(self.item) + self.init_builder() self.item.caption = 'caption text' p_node = self.node.make_internal_item_ref(self.app, self.node['id']) ref_node = p_node.children[0] @@ -79,13 +79,7 @@ def test_make_internal_item_ref_show_caption(self): self.assertEqual(p_node, cache['default'][f'{self.node["document"]}.html']) def test_make_internal_item_ref_only_caption(self): - mock_builder = MagicMock(spec=StandaloneHTMLBuilder) - mock_builder.link_suffix = '.html' - mock_builder.env = BuildEnvironment(self.app) - self.app.builder = mock_builder - self.app.builder.env.traceability_collection = self.collection - self.app.builder.env.traceability_ref_nodes = {} - self.app.builder.env.traceability_collection.add_item(self.item) + self.init_builder() self.item.caption = 'caption text' self.node['nocaptions'] = True self.node['onlycaptions'] = True @@ -104,13 +98,7 @@ def test_make_internal_item_ref_only_caption(self): self.assertEqual(p_node, cache['onlycaptions'][f'{self.node["document"]}.html']) def test_make_internal_item_ref_hide_caption_html(self): - mock_builder = MagicMock(spec=StandaloneHTMLBuilder) - mock_builder.link_suffix = '.html' - mock_builder.env = BuildEnvironment(self.app) - self.app.builder = mock_builder - self.app.builder.env.traceability_collection = self.collection - self.app.builder.env.traceability_ref_nodes = {} - self.app.builder.env.traceability_collection.add_item(self.item) + self.init_builder() self.item.caption = 'caption text' self.node['nocaptions'] = True p_node = self.node.make_internal_item_ref(self.app, self.node['id']) @@ -128,13 +116,7 @@ def test_make_internal_item_ref_hide_caption_html(self): self.assertEqual(p_node, cache['nocaptions'][f'{self.node["document"]}.html']) def test_make_internal_item_ref_hide_caption_latex(self): - mock_builder = MagicMock(spec=LaTeXBuilder) - mock_builder.get_relative_uri = Mock(side_effect=raise_no_uri) - mock_builder.env = BuildEnvironment(self.app) - self.app.builder = mock_builder - self.app.builder.env.traceability_collection = self.collection - self.app.builder.env.traceability_ref_nodes = {} - self.app.builder.env.traceability_collection.add_item(self.item) + self.init_builder(spec=LaTeXBuilder) self.item.caption = 'caption text' self.node['nocaptions'] = True p_node = self.node.make_internal_item_ref(self.app, self.node['id']) @@ -160,13 +142,6 @@ def test_is_relation_external(self, relation_name, expected): self.assertEqual(external, expected) def test_item_node_replacement(self): - mock_builder = MagicMock(spec=StandaloneHTMLBuilder) - mock_builder.link_suffix = '.html' - mock_builder.env = BuildEnvironment(self.app) - self.app.builder = mock_builder - self.app.builder.env.traceability_collection = self.collection - self.app.builder.env.traceability_ref_nodes = {} - self.app.builder.env.traceability_collection.add_item(self.item) self.collection.add_relation_pair('depends_on', 'impacts_on') # leaving out depends_on to test warning self.app.config.traceability_relationship_to_string = {'impacts_on': 'Impacts on'} From 39ac9f3bcc506ec11ec3c2ebf465951bcc4da80e Mon Sep 17 00:00:00 2001 From: jce <28319872+JasperCraeghs@users.noreply.github.com> Date: Fri, 27 Oct 2023 10:30:57 +0200 Subject: [PATCH 8/9] Remove redundant newline --- tests/directives/test_item_directive.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/directives/test_item_directive.py b/tests/directives/test_item_directive.py index b1def5fc..c3a427ce 100644 --- a/tests/directives/test_item_directive.py +++ b/tests/directives/test_item_directive.py @@ -148,7 +148,6 @@ def test_item_node_replacement(self): target_item = TraceableItem('target_id') self.collection.add_item(target_item) - self.collection.add_relation(self.item.identifier, 'depends_on', target_item.identifier) with self.assertLogs(LOGGER, logging.DEBUG) as c_m: From 5ce0eea1b12b77c0c7fba4a0149d5124fa07518a Mon Sep 17 00:00:00 2001 From: jce <28319872+JasperCraeghs@users.noreply.github.com> Date: Fri, 27 Oct 2023 10:50:07 +0200 Subject: [PATCH 9/9] Style fixes for test suite --- tests/directives/test_item_directive.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/directives/test_item_directive.py b/tests/directives/test_item_directive.py index c3a427ce..3840bda6 100644 --- a/tests/directives/test_item_directive.py +++ b/tests/directives/test_item_directive.py @@ -17,6 +17,7 @@ LOGGER = logging.getLogger() + def raise_no_uri(*args, **kwargs): raise NoUri @@ -34,7 +35,7 @@ def setUp(self): self.item.node = self.node self.app.config = Mock() self.app.config.traceability_hyperlink_colors = {} - self.collection = TraceableCollection() + self.collection = TraceableCollection() self.collection.add_item(self.item) def init_builder(self, spec=StandaloneHTMLBuilder):