Skip to content

Commit

Permalink
Generate documentation for in-suite suite definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
BenjaminPelletier committed Sep 11, 2023
1 parent f1cacce commit b00ffef
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 24 deletions.
2 changes: 1 addition & 1 deletion monitoring/uss_qualifier/suites/astm/netrid/f3411_19.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
## Actions

1. Action generator: [`action_generators.astm.f3411.ForEachDSS`](../../../action_generators/astm/f3411/for_each_dss.py)
1. Suite: <in-suite definition>
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))
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!--This file is autogenerated via `make format`; do not change manually-->
# 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))
2 changes: 1 addition & 1 deletion monitoring/uss_qualifier/suites/astm/netrid/f3411_22a.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
## Actions

1. Action generator: [`action_generators.astm.f3411.ForEachDSS`](../../../action_generators/astm/f3411/for_each_dss.py)
1. Suite: <in-suite definition>
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))
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!--This file is autogenerated via `make format`; do not change manually-->
# 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))
119 changes: 99 additions & 20 deletions monitoring/uss_qualifier/suites/documentation/documentation.py
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down Expand Up @@ -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(
"<!--This file is autogenerated via `make format`; do not change manually-->"
)
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 = []
Expand All @@ -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 = []
Expand All @@ -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(
Expand All @@ -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: <in-suite definition>"]
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}")
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,30 @@
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,
)


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()
Expand Down

0 comments on commit b00ffef

Please sign in to comment.