From b00ffef0351436b2daa0be5b967844c0f98fc370 Mon Sep 17 00:00:00 2001 From: Benjamin Pelletier Date: Mon, 11 Sep 2023 06:04:54 +0000 Subject: [PATCH] Generate documentation for in-suite suite definitions --- .../suites/astm/netrid/f3411_19.md | 2 +- .../suites/astm/netrid/f3411_19_suite1.md | 7 ++ .../suites/astm/netrid/f3411_22a.md | 2 +- .../suites/astm/netrid/f3411_22a_suite1.md | 8 ++ .../suites/documentation/documentation.py | 119 +++++++++++++++--- .../documentation/format_documentation.py | 17 ++- 6 files changed, 131 insertions(+), 24 deletions(-) create mode 100644 monitoring/uss_qualifier/suites/astm/netrid/f3411_19_suite1.md create mode 100644 monitoring/uss_qualifier/suites/astm/netrid/f3411_22a_suite1.md diff --git a/monitoring/uss_qualifier/suites/astm/netrid/f3411_19.md b/monitoring/uss_qualifier/suites/astm/netrid/f3411_19.md index ec359caacf..14feae21b7 100644 --- a/monitoring/uss_qualifier/suites/astm/netrid/f3411_19.md +++ b/monitoring/uss_qualifier/suites/astm/netrid/f3411_19.md @@ -5,6 +5,6 @@ ## Actions 1. Action generator: [`action_generators.astm.f3411.ForEachDSS`](../../../action_generators/astm/f3411/for_each_dss.py) - 1. Suite: + 1. Suite: [DSS instance validator](f3411_19_suite1.md) ([in-suite definition](f3411_19.yaml)) 2. Scenario: [ASTM F3411-19 NetRID DSS interoperability](../../../scenarios/astm/netrid/v19/dss_interoperability.md) ([`scenarios.astm.netrid.v19.DSSInteroperability`](../../../scenarios/astm/netrid/v19/dss_interoperability.py)) 3. Scenario: [ASTM NetRID nominal behavior](../../../scenarios/astm/netrid/v19/nominal_behavior.md) ([`scenarios.astm.netrid.v19.NominalBehavior`](../../../scenarios/astm/netrid/v19/nominal_behavior.py)) diff --git a/monitoring/uss_qualifier/suites/astm/netrid/f3411_19_suite1.md b/monitoring/uss_qualifier/suites/astm/netrid/f3411_19_suite1.md new file mode 100644 index 0000000000..ddae48161b --- /dev/null +++ b/monitoring/uss_qualifier/suites/astm/netrid/f3411_19_suite1.md @@ -0,0 +1,7 @@ + +# DSS instance validator test suite +Defined in [parent suite](f3411_19.md) [`suites.astm.netrid.f3411_19`](./f3411_19.yaml) + +## Actions + +1. Scenario: [ASTM NetRID DSS: Subscription Validation](../../../scenarios/astm/netrid/v22a/dss/subscription_validation.md) ([`scenarios.astm.netrid.v22a.dss.SubscriptionValidation`](../../../scenarios/astm/netrid/v22a/dss/subscription_validation.py)) diff --git a/monitoring/uss_qualifier/suites/astm/netrid/f3411_22a.md b/monitoring/uss_qualifier/suites/astm/netrid/f3411_22a.md index 38be47bdb5..64b779f6dd 100644 --- a/monitoring/uss_qualifier/suites/astm/netrid/f3411_22a.md +++ b/monitoring/uss_qualifier/suites/astm/netrid/f3411_22a.md @@ -5,6 +5,6 @@ ## Actions 1. Action generator: [`action_generators.astm.f3411.ForEachDSS`](../../../action_generators/astm/f3411/for_each_dss.py) - 1. Suite: + 1. Suite: [DSS instance validator](f3411_22a_suite1.md) ([in-suite definition](f3411_22a.yaml)) 2. Scenario: [ASTM F3411-22a NetRID DSS interoperability](../../../scenarios/astm/netrid/v22a/dss_interoperability.md) ([`scenarios.astm.netrid.v22a.DSSInteroperability`](../../../scenarios/astm/netrid/v22a/dss_interoperability.py)) 3. Scenario: [ASTM NetRID nominal behavior](../../../scenarios/astm/netrid/v22a/nominal_behavior.md) ([`scenarios.astm.netrid.v22a.NominalBehavior`](../../../scenarios/astm/netrid/v22a/nominal_behavior.py)) diff --git a/monitoring/uss_qualifier/suites/astm/netrid/f3411_22a_suite1.md b/monitoring/uss_qualifier/suites/astm/netrid/f3411_22a_suite1.md new file mode 100644 index 0000000000..7d426750b0 --- /dev/null +++ b/monitoring/uss_qualifier/suites/astm/netrid/f3411_22a_suite1.md @@ -0,0 +1,8 @@ + +# DSS instance validator test suite +Defined in [parent suite](f3411_22a.md) [`suites.astm.netrid.f3411_22a`](./f3411_22a.yaml) + +## Actions + +1. Scenario: [ASTM NetRID DSS: Simple ISA](../../../scenarios/astm/netrid/v22a/dss/isa_simple.md) ([`scenarios.astm.netrid.v22a.dss.ISASimple`](../../../scenarios/astm/netrid/v22a/dss/isa_simple.py)) +2. Scenario: [ASTM NetRID DSS: Subscription Validation](../../../scenarios/astm/netrid/v22a/dss/subscription_validation.md) ([`scenarios.astm.netrid.v22a.dss.SubscriptionValidation`](../../../scenarios/astm/netrid/v22a/dss/subscription_validation.py)) diff --git a/monitoring/uss_qualifier/suites/documentation/documentation.py b/monitoring/uss_qualifier/suites/documentation/documentation.py index 0b84e4bee2..af27db8f81 100644 --- a/monitoring/uss_qualifier/suites/documentation/documentation.py +++ b/monitoring/uss_qualifier/suites/documentation/documentation.py @@ -1,7 +1,7 @@ import glob import inspect import os -from typing import Iterator, Optional, List, Union +from typing import Iterator, Optional, List, Union, Dict from implicitdict import ImplicitDict from monitoring.uss_qualifier.action_generators.action_generator import ( @@ -47,30 +47,52 @@ def find_test_suites(start_path: Optional[str] = None) -> Iterator[str]: yield suite -def make_test_suite_documentation(test_suite_yaml_file: str) -> str: +def make_test_suite_documentation( + suite_def: TestSuiteDefinition, + suite_yaml_file: str, + suite_doc_file: str, + parent_suite_doc: Optional[str] = None, +) -> Dict[str, str]: + test_suites: Dict[str, str] = {} + lines = [] - suite_def: TestSuiteDefinition = ImplicitDict.parse( - load_dict_with_references("file://" + test_suite_yaml_file), TestSuiteDefinition - ) lines.append( "" ) lines.append(f"# {suite_def.name} test suite") - local_path = os.path.split(test_suite_yaml_file)[-1] - lines.append(f"[`{get_package_name(test_suite_yaml_file)}`](./{local_path})") + local_path = os.path.split(suite_yaml_file)[-1] + if parent_suite_doc is None: + prefix = "" + else: + parent_rel_path = os.path.relpath( + parent_suite_doc, start=os.path.dirname(suite_doc_file) + ) + prefix = f"Defined in [parent suite]({parent_rel_path}) " + lines.append(f"{prefix}[`{get_package_name(suite_yaml_file)}`](./{local_path})") lines.append("") lines.append("## Actions") lines.append("") - base_path = os.path.dirname(test_suite_yaml_file) + base_path = os.path.dirname(suite_yaml_file) for i, action in enumerate(suite_def.actions): - lines.extend(render_action(action, i + 1, base_path, 0)) + lines.extend( + _render_action( + action, + i + 1, + base_path, + 0, + suite_yaml_file, + suite_doc_file, + test_suites, + ) + ) lines.append("") - return "\n".join(lines) + test_suites[suite_doc_file] = "\n".join(lines) + return test_suites -def render_scenario( +def _render_scenario( scenario_type_name: TestScenarioTypeName, i: int, base_path: str, indent: int ) -> List[str]: lines = [] @@ -84,7 +106,7 @@ def render_scenario( return lines -def render_suite_by_type( +def _render_suite_by_type( suite_type: FileReference, i: int, base_path: str, indent: int ) -> List[str]: lines = [] @@ -102,11 +124,38 @@ def render_suite_by_type( return lines -def render_action_generator( +def _render_suite_by_definition( + suite_def: TestSuiteDefinition, + i: int, + base_path: str, + indent: int, + parent_yaml_file: str, + parent_doc_file: str, + test_suites: Dict[str, str], +) -> List[str]: + doc_path = os.path.splitext(parent_doc_file)[0] + f"_suite{i}.md" + new_docs = make_test_suite_documentation( + suite_def, parent_yaml_file, doc_path, parent_doc_file + ) + + for k, v in new_docs.items(): + test_suites[k] = v + + doc_rel_path = os.path.relpath(doc_path, base_path) + parent_rel_path = os.path.relpath(parent_yaml_file, start=base_path) + return [ + f"{' ' * indent}{i}. Suite: [{suite_def.name}]({doc_rel_path}) ([in-suite definition]({parent_rel_path}))" + ] + + +def _render_action_generator( generator_def: Union[ActionGeneratorDefinition, PotentialActionGeneratorAction], i: int, base_path: str, indent: int, + parent_yaml_file: str, + parent_doc_file: str, + test_suites: Dict[str, str], ) -> List[str]: lines = [] action_generator_type = action_generator_type_from_name( @@ -122,33 +171,63 @@ def render_action_generator( generator_def ) for j, potential_action in enumerate(potential_actions): - lines.extend(render_action(potential_action, j + 1, base_path, indent + 4)) + lines.extend( + _render_action( + potential_action, + j + 1, + base_path, + indent + 4, + parent_yaml_file, + parent_doc_file, + test_suites, + ) + ) return lines -def render_action( +def _render_action( action: Union[TestSuiteActionDeclaration, PotentialGeneratedAction], i: int, base_path: str, indent: int, + parent_yaml_file: str, + parent_doc_file: str, + test_suites: Dict[str, str], ) -> List[str]: action_type = action.get_action_type() if action_type == ActionType.TestScenario: - return render_scenario(action.test_scenario.scenario_type, i, base_path, indent) + return _render_scenario( + action.test_scenario.scenario_type, i, base_path, indent + ) elif action_type == ActionType.TestSuite: if "suite_type" in action.test_suite and action.test_suite.suite_type: - return render_suite_by_type( + return _render_suite_by_type( action.test_suite.suite_type, i, base_path, indent ) elif ( "suite_definition" in action.test_suite and action.test_suite.suite_definition ): - # TODO: Generate additional test suite documentation for in-suite suite definition - return [f"{' ' * indent}{i}. Suite: "] + return _render_suite_by_definition( + action.test_suite.suite_definition, + i, + base_path, + indent, + parent_yaml_file, + parent_doc_file, + test_suites, + ) else: raise ValueError(f"Test suite action {i} missing suite type or definition") elif action_type == ActionType.ActionGenerator: - return render_action_generator(action.action_generator, i, base_path, indent) + return _render_action_generator( + action.action_generator, + i, + base_path, + indent, + parent_yaml_file, + parent_doc_file, + test_suites, + ) else: raise NotImplementedError(f"Unsupported test suite action type: {action_type}") diff --git a/monitoring/uss_qualifier/suites/documentation/format_documentation.py b/monitoring/uss_qualifier/suites/documentation/format_documentation.py index 10b3ea2a5a..e04d5038d6 100644 --- a/monitoring/uss_qualifier/suites/documentation/format_documentation.py +++ b/monitoring/uss_qualifier/suites/documentation/format_documentation.py @@ -2,6 +2,9 @@ import os import sys +from implicitdict import ImplicitDict +from monitoring.uss_qualifier.fileio import load_dict_with_references +from monitoring.uss_qualifier.suites.definitions import TestSuiteDefinition from monitoring.uss_qualifier.suites.documentation.documentation import ( find_test_suites, make_test_suite_documentation, @@ -9,10 +12,20 @@ def main(lint: bool) -> int: - changes = False + test_suite_docs = {} for suite_yaml_file in find_test_suites(): - suite_doc_content = make_test_suite_documentation(suite_yaml_file) + suite_def: TestSuiteDefinition = ImplicitDict.parse( + load_dict_with_references("file://" + suite_yaml_file), TestSuiteDefinition + ) suite_doc_file = os.path.splitext(suite_yaml_file)[0] + ".md" + new_docs = make_test_suite_documentation( + suite_def, suite_yaml_file, suite_doc_file + ) + for k, v in new_docs.items(): + test_suite_docs[k] = v + + changes = False + for suite_doc_file, suite_doc_content in test_suite_docs.items(): if os.path.exists(suite_doc_file): with open(suite_doc_file, "r") as f: existing_content = f.read()