diff --git a/.basedpyright/baseline.json b/.basedpyright/baseline.json index d6a6bd69f5..8b30978984 100644 --- a/.basedpyright/baseline.json +++ b/.basedpyright/baseline.json @@ -4656,70 +4656,6 @@ "endColumn": 35, "lineCount": 1 } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 60, - "endColumn": 73, - "lineCount": 1 - } - }, - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 11, - "endColumn": 49, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 77, - "endColumn": 87, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 58, - "endColumn": 68, - "lineCount": 1 - } - }, - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 12, - "endColumn": 56, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 39, - "endColumn": 55, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 64, - "endColumn": 80, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 64, - "endColumn": 78, - "lineCount": 1 - } } ], "./monitoring/uss_qualifier/action_generators/flight_planning/planner_combinations.py": [ @@ -4894,772 +4830,6 @@ } } ], - "./monitoring/uss_qualifier/reports/globally_expanded/generate.py": [ - { - "code": "reportArgumentType", - "range": { - "startColumn": 42, - "endColumn": 74, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 66, - "endColumn": 74, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 41, - "endColumn": 54, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 110, - "endColumn": 116, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 52, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 37, - "endColumn": 45, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 46, - "endColumn": 54, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 29, - "endColumn": 37, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 27, - "endColumn": 35, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 23, - "endColumn": 31, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 27, - "endColumn": 35, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 71, - "endColumn": 79, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 42, - "endColumn": 50, - "lineCount": 1 - } - }, - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 27, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 23, - "endColumn": 58, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 48, - "endColumn": 56, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 35, - "endColumn": 43, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 28, - "endColumn": 36, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 24, - "endColumn": 32, - "lineCount": 1 - } - }, - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 20, - "endColumn": 35, - "lineCount": 2 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 43, - "endColumn": 47, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 34, - "endColumn": 39, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 61, - "endColumn": 66, - "lineCount": 1 - } - }, - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 20, - "endColumn": 35, - "lineCount": 2 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 67, - "endColumn": 80, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 35, - "endColumn": 39, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 57, - "endColumn": 69, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 93, - "endColumn": 102, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 35, - "endColumn": 39, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 57, - "endColumn": 69, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 93, - "endColumn": 102, - "lineCount": 1 - } - }, - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 19, - "endColumn": 58, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 48, - "endColumn": 56, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 29, - "endColumn": 37, - "lineCount": 1 - } - }, - { - "code": "reportCallIssue", - "range": { - "startColumn": 16, - "endColumn": 28, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 16, - "endColumn": 28, - "lineCount": 1 - } - }, - { - "code": "reportCallIssue", - "range": { - "startColumn": 16, - "endColumn": 28, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 16, - "endColumn": 28, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 32, - "endColumn": 48, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 32, - "endColumn": 48, - "lineCount": 1 - } - }, - { - "code": "reportCallIssue", - "range": { - "startColumn": 32, - "endColumn": 53, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 44, - "endColumn": 52, - "lineCount": 1 - } - }, - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 35, - "endColumn": 36, - "lineCount": 3 - } - } - ], - "./monitoring/uss_qualifier/reports/report.py": [ - { - "code": "reportArgumentType", - "range": { - "startColumn": 35, - "endColumn": 47, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 65, - "endColumn": 85, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 27, - "endColumn": 47, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 40, - "endColumn": 47, - "lineCount": 1 - } - } - ], - "./monitoring/uss_qualifier/reports/sequence_view/events.py": [ - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 36, - "endColumn": 43, - "lineCount": 1 - } - }, - { - "code": "reportReturnType", - "range": { - "startColumn": 8, - "endColumn": 24, - "lineCount": 1 - } - } - ], - "./monitoring/uss_qualifier/reports/sequence_view/generate.py": [ - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 12, - "endColumn": 57, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 46, - "endColumn": 56, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 72, - "endColumn": 82, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 43, - "endColumn": 59, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 51, - "endColumn": 67, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 74, - "endColumn": 87, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 48, - "endColumn": 62, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 38, - "endColumn": 42, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 45, - "endColumn": 65, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 79, - "endColumn": 86, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 35, - "endColumn": 39, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 36, - "endColumn": 50, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 81, - "endColumn": 88, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 34, - "endColumn": 55, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 50, - "endColumn": 58, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 51, - "endColumn": 55, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 34, - "endColumn": 46, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 46, - "endColumn": 58, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 43, - "endColumn": 57, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 39, - "endColumn": 53, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 51, - "endColumn": 65, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 46, - "endColumn": 59, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 45, - "endColumn": 77, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 69, - "endColumn": 77, - "lineCount": 1 - } - } - ], - "./monitoring/uss_qualifier/reports/sequence_view/kml.py": [ - { - "code": "reportReturnType", - "range": { - "startColumn": 9, - "endColumn": 26, - "lineCount": 1 - } - }, - { - "code": "reportInvalidTypeForm", - "range": { - "startColumn": 14, - "endColumn": 17, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 30, - "endColumn": 74, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 28, - "endColumn": 52, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 28, - "endColumn": 53, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 66, - "endColumn": 74, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 1, - "endColumn": 74, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 1, - "endColumn": 69, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 1, - "endColumn": 71, - "lineCount": 1 - } - } - ], - "./monitoring/uss_qualifier/reports/sequence_view/summary_types.py": [ - { - "code": "reportOptionalIterable", - "range": { - "startColumn": 17, - "endColumn": 34, - "lineCount": 1 - } - } - ], - "./monitoring/uss_qualifier/reports/tested_requirements/breakdown.py": [ - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 43, - "endColumn": 50, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 49, - "endColumn": 56, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 44, - "endColumn": 57, - "lineCount": 1 - } - }, - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 11, - "endColumn": 44, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 67, - "endColumn": 77, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 60, - "endColumn": 70, - "lineCount": 1 - } - }, - { - "code": "reportOperatorIssue", - "range": { - "startColumn": 12, - "endColumn": 51, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 34, - "endColumn": 50, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 39, - "endColumn": 55, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 12, - "endColumn": 35, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 43, - "endColumn": 52, - "lineCount": 1 - } - } - ], "./monitoring/uss_qualifier/requirements/documentation.py": [ { "code": "reportAttributeAccessIssue", diff --git a/monitoring/uss_qualifier/action_generators/documentation/definitions.py b/monitoring/uss_qualifier/action_generators/documentation/definitions.py index a58e4b5b8f..d170d6a867 100644 --- a/monitoring/uss_qualifier/action_generators/documentation/definitions.py +++ b/monitoring/uss_qualifier/action_generators/documentation/definitions.py @@ -3,7 +3,6 @@ from monitoring.uss_qualifier.action_generators.definitions import GeneratorTypeName from monitoring.uss_qualifier.scenarios.definitions import TestScenarioTypeName from monitoring.uss_qualifier.suites.definitions import ( - ActionType, TestSuiteDefinition, TestSuiteTypeName, ) @@ -34,9 +33,3 @@ class PotentialGeneratedAction(ImplicitDict): test_scenario: Optional[PotentialTestScenarioAction] test_suite: Optional[PotentialTestSuiteAction] action_generator: Optional[PotentialActionGeneratorAction] - - def get_action_type(self) -> ActionType: - matches = [v for v in ActionType if v in self and self[v]] - if len(matches) != 1: - raise ActionType.build_invalid_action_declaration() - return ActionType(matches[0]) diff --git a/monitoring/uss_qualifier/action_generators/documentation/documentation.py b/monitoring/uss_qualifier/action_generators/documentation/documentation.py index e168427407..2af7a6f09b 100644 --- a/monitoring/uss_qualifier/action_generators/documentation/documentation.py +++ b/monitoring/uss_qualifier/action_generators/documentation/documentation.py @@ -14,7 +14,6 @@ PotentialTestSuiteAction, ) from monitoring.uss_qualifier.suites.definitions import ( - ActionType, TestSuiteActionDeclaration, ) @@ -36,8 +35,7 @@ def list_potential_actions_for_action_generator_definition( def list_potential_actions_for_action_declaration( declaration: TestSuiteActionDeclaration, ) -> list[PotentialGeneratedAction]: - action_type = declaration.get_action_type() - if action_type == ActionType.TestScenario: + if "test_scenario" in declaration and declaration.test_scenario: return [ PotentialGeneratedAction( test_scenario=PotentialTestScenarioAction( @@ -45,7 +43,7 @@ def list_potential_actions_for_action_declaration( ) ) ] - elif action_type == ActionType.TestSuite: + elif "test_suite" in declaration and declaration.test_suite: if "suite_type" in declaration.test_suite and declaration.test_suite.suite_type: return [ PotentialGeneratedAction( @@ -65,7 +63,7 @@ def list_potential_actions_for_action_declaration( ) ) ] - elif action_type == ActionType.ActionGenerator: + elif "action_generator" in declaration and declaration.action_generator: return [ PotentialGeneratedAction( action_generator=PotentialActionGeneratorAction( @@ -74,4 +72,4 @@ def list_potential_actions_for_action_declaration( ) ] else: - raise NotImplementedError(f"Action type {action_type} is not supported") + raise declaration.invalid_type_error diff --git a/monitoring/uss_qualifier/reports/globally_expanded/generate.py b/monitoring/uss_qualifier/reports/globally_expanded/generate.py index 79b2036245..9cf20dd9fc 100644 --- a/monitoring/uss_qualifier/reports/globally_expanded/generate.py +++ b/monitoring/uss_qualifier/reports/globally_expanded/generate.py @@ -21,8 +21,6 @@ from monitoring.uss_qualifier.reports.sequence_view.summary_types import ( ActionNode, ActionNodeType, - EpochType, - EventType, Indexer, TestedCase, TestedScenario, @@ -110,6 +108,7 @@ def generate_globally_expanded_report( """ + assert report.configuration.v1 and report.configuration.v1.test_run resource_pool = make_resources_config(report.configuration.v1.test_run) def indented_ul(value) -> list[str]: @@ -184,6 +183,7 @@ def describe_pool_resource(k: str, v: dict) -> str: def _generate_sections(node: ActionNode) -> Iterator[_Section]: if node.node_type == ActionNodeType.Scenario: + assert node.scenario yield _generate_scenario_section(node.scenario) elif node.node_type == ActionNodeType.SkippedAction: yield _generate_skipped_scenario_section(node) @@ -195,7 +195,7 @@ def _generate_sections(node: ActionNode) -> Iterator[_Section]: def _generate_skipped_scenario_section(node: ActionNode) -> _Section: return _Section( title=f"[skipped] {node.name}", - body=f"This instance of this test scenario was skipped in this test run because: {node.skipped_action.reason}", + body=f"This instance of this test scenario was skipped in this test run because: {node.skipped_action.reason if node.skipped_action else '?'}", ) @@ -237,15 +237,15 @@ def _indent_headings(elements: Sequence[marko.element.Element], levels: int) -> for element in elements: if isinstance(element, marko.block.Heading): element.level = min(element.level + levels, 6) - if hasattr(element, "children") and element.children: - _indent_headings(element.children, levels) + if hasattr(element, "children") and element.children: # pyright: ignore[reportAttributeAccessIssue] + _indent_headings(element.children, levels) # pyright: ignore[reportAttributeAccessIssue] def _inflate_fragments(parent: marko.element.Element, origin_filename: str) -> None: - if hasattr(parent, "children") and parent.children: + if hasattr(parent, "children") and parent.children: # pyright: ignore[reportAttributeAccessIssue] c = 0 - while c < len(parent.children): - child = parent.children[c] + while c < len(parent.children): # pyright: ignore[reportAttributeAccessIssue] + child = parent.children[c] # pyright: ignore[reportAttributeAccessIssue] if ( isinstance(child, marko.block.Heading) and hasattr(child, "children") @@ -260,12 +260,12 @@ def _inflate_fragments(parent: marko.element.Element, origin_filename: str) -> N doc = _get_test_step_fragment(absolute_path, child.level) _update_links(doc, absolute_path) _strip_link(child) - parent.children = ( - parent.children[0 : c + 1] + doc.children + parent.children[c + 1 :] + parent.children = ( # pyright: ignore[reportAttributeAccessIssue] + parent.children[0 : c + 1] + doc.children + parent.children[c + 1 :] # pyright: ignore[reportAttributeAccessIssue] ) c += len(doc.children) elif isinstance(child, marko.element.Element): - _inflate_fragments(parent.children[c], origin_filename) + _inflate_fragments(parent.children[c], origin_filename) # pyright: ignore[reportAttributeAccessIssue] c += 1 else: c += 1 @@ -286,14 +286,14 @@ def add_resource_origin(): note = marko.parse( """∅ _This resource was not applicable to this test run and was therefore not provided._\n\n""" ) - doc.children = doc.children[0:c] + note.children + doc.children[c:] + doc.children = doc.children[0:c] + note.children + doc.children[c:] # pyright: ignore[reportAttributeAccessIssue,reportOperatorIssue] c += len(note.children) return # Insert resource origin information origin = marko.parse( f"\n\n✅ Provided by {scenario.resource_origins[current_resource]}.\n" ) - doc.children = doc.children[0:c] + origin.children + doc.children[c:] + doc.children = doc.children[0:c] + origin.children + doc.children[c:] # pyright: ignore[reportOperatorIssue] c += len(origin.children) while c < len(doc.children): @@ -327,11 +327,11 @@ def add_resource_origin(): def _strip_link(element: marko.element.Element) -> None: - if hasattr(element, "children") and element.children: - for c in range(len(element.children)): - child = element.children[c] + if hasattr(element, "children") and element.children: # pyright: ignore[reportAttributeAccessIssue] + for c in range(len(element.children)): # pyright: ignore[reportAttributeAccessIssue] + child = element.children[c] # pyright: ignore[reportAttributeAccessIssue] if isinstance(child, marko.block.inline.Link): - element.children[c] = child.children[0] + element.children[c] = child.children[0] # pyright: ignore[reportAttributeAccessIssue] elif isinstance(child, marko.element.Element): _strip_link(child) @@ -372,7 +372,7 @@ def add_context_to_case(): """∅ _This test case was not applicable to this test run and is therefore not statused._\n\n""" ) doc.children = ( - doc.children[0 : test_case_i0 + 1] + doc.children[0 : test_case_i0 + 1] # pyright: ignore[reportAttributeAccessIssue,reportOperatorIssue] + note.children + doc.children[test_case_i0 + 1 :] ) @@ -394,7 +394,7 @@ def add_context_to_case(): test_case_i0 = c test_case_level = child.level for epoch in scenario.epochs: - if epoch.type != EpochType.Case: + if epoch.case is None: continue if case_name == epoch.case.name: test_case = epoch.case @@ -407,7 +407,7 @@ def add_context_to_case(): test_case_level = child.level cleanup = True for epoch in scenario.epochs: - if epoch.type != EpochType.Case: + if epoch.case is None: continue if len(epoch.case.steps) == 1 and epoch.case.steps[0].name == "Cleanup": test_case = epoch.case @@ -444,7 +444,7 @@ def add_context_to_step(): ) dc = len(note.children) doc.children = ( - doc.children[0 : test_step_i0 + 1] + doc.children[0 : test_step_i0 + 1] # pyright: ignore[reportOperatorIssue] + note.children + doc.children[test_step_i0 + 1 :] ) @@ -494,6 +494,7 @@ def _add_context_to_step( def add_context_to_check(): nonlocal c, i1, added, test_check_name, test_check_i0, test_check_level if test_check_name is not None: + assert test_check_i0 is not None dc = _add_context_to_check(doc, step, test_check_name, test_check_i0, c) c += dc i1 += dc @@ -533,14 +534,14 @@ def _add_context_to_check( check_text = [""] for event in step.events: if ( - event.type == EventType.PassedCheck + event.passed_check is not None and event.passed_check.name == test_check_name ): check_text.append( f"✅ {', '.join(event.passed_check.participants)} ({event.passed_check.timestamp})" ) elif ( - event.type == EventType.FailedCheck + event.failed_check is not None and event.failed_check.name == test_check_name ): check_text.append( @@ -552,7 +553,7 @@ def _add_context_to_check( additions = marko.parse( """∅ _This check was not applicable to this test run and is therefore not statused._\n\n""" ) - doc.children = doc.children[0:i1] + additions.children + doc.children[i1:] + doc.children = doc.children[0:i1] + additions.children + doc.children[i1:] # pyright: ignore[reportOperatorIssue] return len(additions.children) @@ -571,8 +572,8 @@ def _update_links(element: marko.element.Element, origin_filename: str) -> None: url = url.replace("/github.com/", "/raw.githubusercontent.com/") url = url.replace("/blob/", "/") element.dest = url - if hasattr(element, "children") and element.children: - for child in element.children: + if hasattr(element, "children") and element.children: # pyright: ignore[reportAttributeAccessIssue] + for child in element.children: # pyright: ignore[reportAttributeAccessIssue] if isinstance(child, marko.element.Element): _update_links(child, origin_filename) @@ -580,7 +581,7 @@ def _update_links(element: marko.element.Element, origin_filename: str) -> None: def _add_section_numbers(elements: Sequence[marko.element.Element]) -> None: heading_level = 2 levels = [0] - headings = [None] + headings: list[str | None] = [None] prev_heading = None for i, element in enumerate(elements): if isinstance(element, marko.block.Heading): @@ -599,7 +600,7 @@ def _add_section_numbers(elements: Sequence[marko.element.Element]) -> None: heading_level += 1 else: headings.append(text_of(element)) - heading_trace = " -> ".join(headings) + heading_trace = " -> ".join([str(heading) for heading in headings]) raise ValueError( f"Encountered a level {element.level} heading ({text_of(element)}) at element {i} following a level {heading_level} heading ({prev_heading}); expected heading levels to increase by 1 level at a time. Trace: {heading_trace}" ) @@ -612,4 +613,4 @@ def _add_section_numbers(elements: Sequence[marko.element.Element]) -> None: else: element.children = [ marko.block.inline.RawText(section_number) - ] + element.children + ] + element.children # pyright: ignore[reportOperatorIssue] diff --git a/monitoring/uss_qualifier/reports/report.py b/monitoring/uss_qualifier/reports/report.py index e377221515..1e10606d8b 100644 --- a/monitoring/uss_qualifier/reports/report.py +++ b/monitoring/uss_qualifier/reports/report.py @@ -356,11 +356,14 @@ def queries(self) -> list[fetch.Query]: queries = list() for case in self.cases: for step in case.steps: - if step.has_field_with_value("queries"): + if "queries" in step and step.queries: queries.extend(step.queries) - if self.has_field_with_value("cleanup") and self.cleanup.has_field_with_value( - "queries" + if ( + "cleanup" in self + and self.cleanup + and "queries" in self.cleanup + and self.cleanup.queries ): queries.extend(self.cleanup.queries) @@ -471,38 +474,11 @@ class TestSuiteActionReport(ImplicitDict): skipped_action: Optional[SkippedActionReport] """If this action was skipped, this field will hold its report""" - def get_applicable_report(self) -> tuple[bool, bool, bool]: - """Determine which type of report is applicable for this action. - - Note that skipped_action is applicable if none of the other return values are true. - - Returns: - * Whether test_suite is applicable - * Whether test_scenario is applicable - * Whether action_generator is applicable - """ - test_suite = "test_suite" in self and self.test_suite is not None - test_scenario = "test_scenario" in self and self.test_scenario is not None - action_generator = ( - "action_generator" in self and self.action_generator is not None + @property + def invalid_type_error(self): + return ValueError( + "Invalid TestSuiteActionReport: test_scenario, test_suite, action_generator or skipped_action must be specified" ) - skipped_action = "skipped_action" in self and self.skipped_action is not None - if ( - sum( - 1 if case else 0 - for case in [ - test_suite, - test_scenario, - action_generator, - skipped_action, - ] - ) - != 1 - ): - raise ValueError( - "Exactly one of `test_suite`, `test_scenario`, `action_generator`, or `skipped_action` must be populated" - ) - return test_suite, test_scenario, action_generator def _conditional( self, @@ -511,28 +487,25 @@ def _conditional( action_generator_func: Callable[[ActionGeneratorReport], Any] | None = None, skipped_action_func: Callable[[SkippedActionReport], Any] | None = None, ) -> Any: - test_suite, test_scenario, action_generator = self.get_applicable_report() - if test_suite: - assert self.test_suite is not None + if "test_suite" in self and self.test_suite: return test_suite_func(self.test_suite) - if test_scenario: - assert self.test_scenario is not None + elif "test_scenario" in self and self.test_scenario: if test_scenario_func is not None: return test_scenario_func(self.test_scenario) else: return test_suite_func(self.test_scenario) - if action_generator: - assert self.action_generator is not None + elif "action_generator" in self and self.action_generator: if action_generator_func is not None: return action_generator_func(self.action_generator) else: return test_suite_func(self.action_generator) - - assert self.skipped_action is not None - if skipped_action_func is not None: - return skipped_action_func(self.skipped_action) + elif "skipped_action" in self and self.skipped_action: + if skipped_action_func is not None: + return skipped_action_func(self.skipped_action) + else: + return test_suite_func(self.skipped_action) else: - return test_suite_func(self.skipped_action) + raise self.invalid_type_error def successful(self) -> bool: return self._conditional(lambda report: report.successful) @@ -546,44 +519,36 @@ def all_participants(self) -> set[ParticipantID]: def query_passed_checks( self, participant_id: str | None = None ) -> Iterator[tuple[JSONPathExpression, PassedCheck]]: - test_suite, test_scenario, action_generator = self.get_applicable_report() - if test_suite: + if "test_suite" in self and self.test_suite: report = self.test_suite prefix = "test_suite" - elif test_scenario: + elif "test_scenario" in self and self.test_scenario: report = self.test_scenario prefix = "test_scenario" - elif action_generator: + elif "action_generator" in self and self.action_generator: report = self.action_generator prefix = "action_generator" else: return - if report is None: - return - for path, pc in report.query_passed_checks(participant_id): yield f"{prefix}.{path}", pc def query_failed_checks( self, participant_id: str | None = None ) -> Iterator[tuple[JSONPathExpression, FailedCheck]]: - test_suite, test_scenario, action_generator = self.get_applicable_report() - if test_suite: + if "test_suite" in self and self.test_suite: report = self.test_suite prefix = "test_suite" - elif test_scenario: + elif "test_scenario" in self and self.test_scenario: report = self.test_scenario prefix = "test_scenario" - elif action_generator: + elif "action_generator" in self and self.action_generator: report = self.action_generator prefix = "action_generator" else: return - if report is None: - return - for path, fc in report.query_failed_checks(participant_id): yield f"{prefix}.{path}", fc @@ -607,6 +572,18 @@ def end_time(self) -> StringBasedDateTime | None: def latest_timestamp(self) -> datetime | None: return self._conditional(lambda report: report.latest_time) + def __str__(self): + if "test_suite" in self and self.test_suite: + return "test_suite" + elif "test_scenario" in self and self.test_scenario: + return "test_scenario" + elif "action_generator" in self and self.action_generator: + return "action_generator" + elif "skipped_action" in self and self.skipped_action: + return "skipped_action" + else: + return "unknown" + class AllConditionsEvaluationReport(ImplicitDict): """Result of an evaluation of AllConditions determined by whether all the subconditions are satisfied.""" diff --git a/monitoring/uss_qualifier/reports/sequence_view/events.py b/monitoring/uss_qualifier/reports/sequence_view/events.py index 7519e96d78..4c1c3140f8 100644 --- a/monitoring/uss_qualifier/reports/sequence_view/events.py +++ b/monitoring/uss_qualifier/reports/sequence_view/events.py @@ -15,7 +15,6 @@ from monitoring.uss_qualifier.reports.sequence_view.summary_types import ( Epoch, Event, - EventType, Indexer, NoteEvent, TestedCase, @@ -67,7 +66,7 @@ def _step_events( scenario_participants: dict[ParticipantID, TestedParticipant], all_events: list[Event], after: datetime | None, -) -> tuple[TestedStep, datetime]: +) -> tuple[TestedStep, datetime | None]: events = [] # Create events for this step's passed checks @@ -116,7 +115,7 @@ def _step_events( found = False for e in all_events: if ( - e.type == EventType.Query + e.query is not None and e.query.request.initiated_at == query_timestamp ): query_events.append(e) diff --git a/monitoring/uss_qualifier/reports/sequence_view/generate.py b/monitoring/uss_qualifier/reports/sequence_view/generate.py index 720ae82e63..e49788217b 100644 --- a/monitoring/uss_qualifier/reports/sequence_view/generate.py +++ b/monitoring/uss_qualifier/reports/sequence_view/generate.py @@ -31,8 +31,6 @@ from monitoring.uss_qualifier.reports.sequence_view.summary_types import ( ActionNode, ActionNodeType, - EpochType, - EventType, Indexer, OverviewRow, SkippedAction, @@ -44,13 +42,13 @@ from monitoring.uss_qualifier.scenarios.documentation.parsing import ( get_documentation_by_name, ) -from monitoring.uss_qualifier.suites.definitions import ActionType, TestSuiteDefinition +from monitoring.uss_qualifier.suites.definitions import TestSuiteDefinition UNATTRIBUTED_PARTICIPANT = "unattributed" def _skipped_action_of(report: SkippedActionReport) -> ActionNode: - if report.declaration.get_action_type() == ActionType.TestSuite: + if "test_suite" in report.declaration and report.declaration.test_suite: if ( "suite_type" in report.declaration.test_suite and report.declaration.test_suite.suite_type @@ -75,7 +73,7 @@ def _skipped_action_of(report: SkippedActionReport) -> ActionNode: "Cannot process skipped action for test suite that does not define suite_type nor suite_definition" ) name = "All actions in test suite" - elif report.declaration.get_action_type() == ActionType.TestScenario: + elif "test_scenario" in report.declaration and report.declaration.test_scenario: docs = get_documentation_by_name(report.declaration.test_scenario.scenario_type) return ActionNode( name=docs.name, @@ -83,7 +81,9 @@ def _skipped_action_of(report: SkippedActionReport) -> ActionNode: children=[], skipped_action=SkippedAction(reason=report.reason), ) - elif report.declaration.get_action_type() == ActionType.ActionGenerator: + elif ( + "action_generator" in report.declaration and report.declaration.action_generator + ): generator_type = action_generator_type_from_name( report.declaration.action_generator.generator_type ) @@ -94,9 +94,7 @@ def _skipped_action_of(report: SkippedActionReport) -> ActionNode: ) name = "All actions from action generator" else: - raise ValueError( - f"Cannot process skipped action of type '{report.declaration.get_action_type()}'" - ) + raise ValueError("Cannot process skipped action of unknown type") parent.children.append( ActionNode( name=name, @@ -117,26 +115,21 @@ def compute_action_node(report: TestSuiteActionReport, indexer: Indexer) -> Acti Returns: Report information summarized to support a sequence view artifact. """ - ( - is_test_suite, - is_test_scenario, - is_action_generator, - ) = report.get_applicable_report() - if is_test_scenario: + if "test_scenario" in report and report.test_scenario: return ActionNode( name=report.test_scenario.name, node_type=ActionNodeType.Scenario, children=[], scenario=compute_tested_scenario(report.test_scenario, indexer), ) - elif is_test_suite: + elif "test_suite" in report and report.test_suite: children = [compute_action_node(a, indexer) for a in report.test_suite.actions] return ActionNode( name=report.test_suite.name, node_type=ActionNodeType.Suite, children=children, ) - elif is_action_generator: + elif "action_generator" in report and report.action_generator: generator_type = action_generator_type_from_name( report.action_generator.generator_type ) @@ -147,8 +140,10 @@ def compute_action_node(report: TestSuiteActionReport, indexer: Indexer) -> Acti compute_action_node(a, indexer) for a in report.action_generator.actions ], ) - else: + elif "skipped_action" in report and report.skipped_action: return _skipped_action_of(report.skipped_action) + else: + raise report.invalid_type_error def _compute_overview_rows(node: ActionNode) -> Iterator[OverviewRow]: @@ -177,9 +172,13 @@ def _align_overview_rows(rows: list[OverviewRow]) -> None: row.filled = True to_fill -= 1 elif len(row.suite_cells) < max_suite_cols: - if row.suite_cells[-1].first_row and all( - c.node_type == ActionNodeType.Scenario - for c in row.suite_cells[-1].node.children + if ( + row.suite_cells[-1].first_row + and row.suite_cells[-1].node is not None + and all( + c.node_type == ActionNodeType.Scenario + for c in row.suite_cells[-1].node.children + ) ): row.suite_cells[-1].colspan += max_suite_cols - len(row.suite_cells) row.filled = True @@ -212,6 +211,7 @@ def _align_overview_rows(rows: list[OverviewRow]) -> None: def _enumerate_all_participants(node: ActionNode) -> list[ParticipantID]: if node.node_type == ActionNodeType.Scenario: + assert node.scenario return list(node.scenario.participants) else: result = set() @@ -225,6 +225,7 @@ def _generate_scenario_pages( node: ActionNode, config: SequenceViewConfiguration, output_path: str ) -> None: if node.node_type == ActionNodeType.Scenario: + assert node.scenario all_participants = list(node.scenario.participants) all_participants.sort() if UNATTRIBUTED_PARTICIPANT in all_participants: @@ -241,8 +242,6 @@ def _generate_scenario_pages( test_scenario=node.scenario, all_participants=all_participants, kml_file=kml_file if config.render_kml else None, - EpochType=EpochType, - EventType=EventType, UNATTRIBUTED_PARTICIPANT=UNATTRIBUTED_PARTICIPANT, len=len, str=str, @@ -308,6 +307,7 @@ def generate_sequence_view( ) -> None: node = compute_action_node(report.report, Indexer()) + assert report.configuration.v1 and report.configuration.v1.test_run resources_config = make_resources_config(report.configuration.v1.test_run) os.makedirs(output_path, exist_ok=True) diff --git a/monitoring/uss_qualifier/reports/sequence_view/kml.py b/monitoring/uss_qualifier/reports/sequence_view/kml.py index 511c6aa79f..33b1774625 100644 --- a/monitoring/uss_qualifier/reports/sequence_view/kml.py +++ b/monitoring/uss_qualifier/reports/sequence_view/kml.py @@ -36,7 +36,7 @@ class QueryKMLRenderer(Protocol): def __call__( self, query: Query, req: ImplicitDict, resp: ImplicitDict - ) -> list[kml.Element]: + ) -> list[kml.Element]: # pyright: ignore[reportInvalidTypeForm, reportReturnType] """Function that renders the provided query information into KML elements. Args: @@ -47,6 +47,8 @@ def __call__( Returns: List of KML elements to include the folder for the query. """ + __name__: str + @dataclass class QueryKMLRenderInfo: @@ -99,7 +101,11 @@ def make_scenario_kml(scenario: TestedScenario) -> str: step_folder = kml.Folder(kml.name(step.name)) case_folder.append(step_folder) for event in step.events: - if not event.query or "query_type" not in event.query: + if ( + not event.query + or "query_type" not in event.query + or not event.query.query_type + ): continue # Only visualize queries of known types if event.query.query_type not in _query_kml_renderers: continue # Only visualize queries with renderers @@ -116,11 +122,14 @@ def make_scenario_kml(scenario: TestedScenario) -> str: ) step_folder.append(query_folder) - kwargs = {} + kwargs: dict[str, ImplicitDict | Query] = {} if render_info.include_query: kwargs["query"] = event.query if render_info.request_type: try: + if not event.query.request.json: + raise ValueError("No JSON in request") + kwargs["req"] = ImplicitDict.parse( event.query.request.json, render_info.request_type, @@ -139,6 +148,9 @@ def make_scenario_kml(scenario: TestedScenario) -> str: and render_info.response_type ): try: + if not event.query.response.json: + raise ValueError("No JSON in response") + kwargs["resp"] = ImplicitDict.parse( event.query.response.json, render_info.response_type, @@ -153,7 +165,7 @@ def make_scenario_kml(scenario: TestedScenario) -> str: ) continue try: - query_folder.extend(render_info.renderer(**kwargs)) + query_folder.extend(render_info.renderer(**kwargs)) # pyright: ignore[reportArgumentType] except (TypeError, KeyError, ValueError) as e: msg = f"Error rendering {render_info.renderer.__name__}" query_folder.append( @@ -174,7 +186,7 @@ def make_scenario_kml(scenario: TestedScenario) -> str: return etree.tostring(format_xml_with_cdata(doc), pretty_print=True).decode("utf-8") -@query_kml_renderer(QueryType.F3548v21DSSQueryOperationalIntentReferences) +@query_kml_renderer(QueryType.F3548v21DSSQueryOperationalIntentReferences) # pyright: ignore[reportArgumentType] def render_query_op_intent_references( req: QueryOperationalIntentReferenceParameters, resp: QueryOperationalIntentReferenceResponse, @@ -182,12 +194,12 @@ def render_query_op_intent_references( return [op_intent_refs_query(req, resp)] -@query_kml_renderer(QueryType.F3548v21USSGetOperationalIntentDetails) +@query_kml_renderer(QueryType.F3548v21USSGetOperationalIntentDetails) # pyright: ignore[reportArgumentType] def render_get_op_intent_details(resp: GetOperationalIntentDetailsResponse): return [full_op_intent(resp.operational_intent)] -@query_kml_renderer(QueryType.InterUSSFlightPlanningV1UpsertFlightPlan) +@query_kml_renderer(QueryType.InterUSSFlightPlanningV1UpsertFlightPlan) # pyright: ignore[reportArgumentType] def render_flight_planning_upsert_flight_plan( req: UpsertFlightPlanRequest, resp: UpsertFlightPlanResponse ): diff --git a/monitoring/uss_qualifier/reports/sequence_view/summary_types.py b/monitoring/uss_qualifier/reports/sequence_view/summary_types.py index 861da638c0..2b0c351e53 100644 --- a/monitoring/uss_qualifier/reports/sequence_view/summary_types.py +++ b/monitoring/uss_qualifier/reports/sequence_view/summary_types.py @@ -23,13 +23,6 @@ class NoteEvent(ImplicitDict): timestamp: datetime -class EventType(str, Enum): - PassedCheck = "PassedCheck" - FailedCheck = "FailedCheck" - Query = "Query" - Note = "Note" - - class Event(ImplicitDict): event_index: int = 0 passed_check: Optional[PassedCheck] = None @@ -38,19 +31,6 @@ class Event(ImplicitDict): query: Optional[Query] = None note: Optional[NoteEvent] = None - @property - def type(self) -> EventType: - if self.passed_check: - return EventType.PassedCheck - elif self.failed_check: - return EventType.FailedCheck - elif self.query: - return EventType.Query - elif self.note: - return EventType.Note - else: - raise ValueError("Invalid Event type") - @property def timestamp(self) -> datetime: if self.passed_check: @@ -66,7 +46,7 @@ def timestamp(self) -> datetime: def get_query_links(self) -> str: links = [] - for e in self.query_events: + for e in self.query_events or []: if isinstance(e, str): links.append(e) else: @@ -94,24 +74,10 @@ def rows(self) -> int: return sum(s.rows for s in self.steps) -class EpochType(str, Enum): - Case = "Case" - Events = "Events" - - class Epoch(ImplicitDict): case: Optional[TestedCase] = None events: Optional[list[Event]] = None - @property - def type(self) -> EpochType: - if self.case: - return EpochType.Case - elif self.events: - return EpochType.Events - else: - raise ValueError("Invalid Epoch did not specify case or events") - @property def rows(self) -> int: if self.case: diff --git a/monitoring/uss_qualifier/reports/templates/sequence_view/scenario.html b/monitoring/uss_qualifier/reports/templates/sequence_view/scenario.html index 79a0d2cd3a..3dba385eca 100644 --- a/monitoring/uss_qualifier/reports/templates/sequence_view/scenario.html +++ b/monitoring/uss_qualifier/reports/templates/sequence_view/scenario.html @@ -52,7 +52,7 @@

{% set first_row = namespace(epoch=True, step=True) %} {% for epoch in test_scenario.epochs %} {% set first_row.epoch = True %} - {% if epoch.type == EpochType.Case %} + {% if epoch.case %} {% for test_step in epoch.case.steps %} {% set first_row.step = True %} {% for event in test_step.events %} @@ -76,7 +76,7 @@

{% endif %} {{ event.event_index }} - {% if event.type == EventType.PassedCheck %} + {% if event.passed_check %} ✅ {{ event.passed_check.name }} @@ -88,7 +88,7 @@

{% endif %} {% endfor %} - {% elif event.type == EventType.FailedCheck %} + {% elif event.failed_check %} {{ severity_symbol(event.failed_check.severity) }} {% if event.failed_check.documentation_url %} @@ -117,7 +117,7 @@

{% endif %} {% endfor %} - {% elif event.type == EventType.Query %} + {% elif event.query %} 🌐 {% set query_dict = {event.query.request.method + " " + event.query.request.url_hostname + " " + str(event.query.response.status_code): event.query} %} @@ -132,20 +132,20 @@

{% endif %} {% endfor %} - {% elif event.type == EventType.Note %} + {% elif event.note %} 📓 {{ event.note.key }}: {{ event.note.message }} {% else %} - ???Render error: unknown EventType '{{ event.type }}' + ???Render error: unknown event type {% endif %} {% set first_row.epoch = False %} {% set first_row.step = False %} {% endfor %} {% endfor %} - {% elif epoch.type == EpochType.Events %} + {% elif epoch.events %} {% for event in epoch.events %} {% if first_row.epoch %} diff --git a/monitoring/uss_qualifier/reports/tested_requirements/breakdown.py b/monitoring/uss_qualifier/reports/tested_requirements/breakdown.py index e3c3c05573..4574451d2e 100644 --- a/monitoring/uss_qualifier/reports/tested_requirements/breakdown.py +++ b/monitoring/uss_qualifier/reports/tested_requirements/breakdown.py @@ -43,7 +43,6 @@ get_scenario_type_by_name, ) from monitoring.uss_qualifier.suites.definitions import ( - ActionType, TestSuiteActionDeclaration, TestSuiteDefinition, ) @@ -118,9 +117,7 @@ def _populate_breakdown_with_action_report( participant_ids: Iterable[ParticipantID], req_set: set[RequirementID] | None, ) -> None: - test_suite, test_scenario, action_generator = action.get_applicable_report() - if test_scenario: - assert action.test_scenario + if "test_scenario" in action and action.test_scenario: return _populate_breakdown_with_scenario_report( breakdown, action.test_scenario, @@ -128,12 +125,12 @@ def _populate_breakdown_with_action_report( participant_ids, req_set, ) - elif test_suite: + elif "test_suite" in action and action.test_suite: for subaction in action.test_suite.actions: _populate_breakdown_with_action_report( breakdown, subaction, acceptable_findings, participant_ids, req_set ) - elif action_generator: + elif "action_generator" in action and action.action_generator: for subaction in action.action_generator.actions: _populate_breakdown_with_action_report( breakdown, subaction, acceptable_findings, participant_ids, req_set @@ -265,12 +262,11 @@ def _populate_breakdown_with_action_declaration( acceptable_findings: list[FullyQualifiedCheck], req_set: set[RequirementID] | None, ) -> None: - action_type = action.get_action_type() - if action_type == ActionType.TestScenario: + if "test_scenario" in action and action.test_scenario: _populate_breakdown_with_scenario( breakdown, action.test_scenario.scenario_type, acceptable_findings, req_set ) - elif action_type == ActionType.TestSuite: + elif "test_suite" in action and action.test_suite: if "suite_type" in action.test_suite and action.test_suite.suite_type: suite_def: TestSuiteDefinition = ImplicitDict.parse( load_dict_with_references(action.test_suite.suite_type), @@ -290,7 +286,7 @@ def _populate_breakdown_with_action_declaration( ) else: raise ValueError("Test suite action missing suite type or definition") - elif action_type == ActionType.ActionGenerator: + elif "action_generator" in action and action.action_generator: potential_actions = list_potential_actions_for_action_generator_definition( action.action_generator ) @@ -299,7 +295,7 @@ def _populate_breakdown_with_action_declaration( breakdown, a, acceptable_findings, req_set ) else: - raise NotImplementedError(f"Unsupported test suite action type: {action_type}") + raise NotImplementedError("Unsupported test suite action type") def _populate_breakdown_with_scenario( @@ -392,5 +388,5 @@ def _populate_breakdown_with_scenario( ), ) tested_step.checks.append(tested_check) - if not tested_check.url: + if not tested_check.url and check.url: tested_check.url = check.url diff --git a/monitoring/uss_qualifier/reports/tested_requirements/summaries.py b/monitoring/uss_qualifier/reports/tested_requirements/summaries.py index 64c8a653ca..f5688a0a96 100644 --- a/monitoring/uss_qualifier/reports/tested_requirements/summaries.py +++ b/monitoring/uss_qualifier/reports/tested_requirements/summaries.py @@ -57,18 +57,14 @@ def find_participant_system_versions( ) -> list[str]: if isinstance(participant_ids, ParticipantID): participant_ids = [participant_ids] - test_suite, test_scenario, action_generator = report.get_applicable_report() result = [] - if test_suite: - assert report.test_suite is not None + if "test_suite" in report and report.test_suite: for action in report.test_suite.actions: result.extend(find_participant_system_versions(action, participant_ids)) - elif action_generator: - assert report.action_generator is not None + elif "action_generator" in report and report.action_generator: for action in report.action_generator.actions: result.extend(find_participant_system_versions(action, participant_ids)) - elif test_scenario: - assert report.test_scenario is not None + elif "test_scenario" in report and report.test_scenario: if ( report.test_scenario.scenario_type in ( diff --git a/monitoring/uss_qualifier/reports/validation/report_validation.py b/monitoring/uss_qualifier/reports/validation/report_validation.py index 55db68f70f..b3311a7886 100644 --- a/monitoring/uss_qualifier/reports/validation/report_validation.py +++ b/monitoring/uss_qualifier/reports/validation/report_validation.py @@ -176,33 +176,30 @@ def _get_applicable_elements_from_action( report: TestSuiteActionReport, location: JSONAddress, ) -> Iterator[TestReportElement]: - test_suite, test_scenario, action_generator = report.get_applicable_report() - if test_scenario: - assert report.test_scenario is not None + if "test_scenario" in report and report.test_scenario: return _get_applicable_elements_from_test_scenario( applicability, report.test_scenario, JSONAddress(location + ".test_scenario"), ) - elif test_suite: - assert report.test_suite is not None + elif "test_suite" in report and report.test_suite: return _get_applicable_elements_from_test_suite( applicability, report.test_suite, JSONAddress(location + ".test_suite") ) - elif action_generator: - assert report.action_generator is not None + elif "action_generator" in report and report.action_generator: return _get_applicable_elements_from_action_generator( applicability, report.action_generator, JSONAddress(location + ".action_generator"), ) - else: - assert report.skipped_action is not None + elif "skipped_action" in report and report.skipped_action: return _get_applicable_elements_from_skipped_action( applicability, report.skipped_action, JSONAddress(location + ".skipped_action"), ) + else: + raise report.invalid_type_error # ===== Evaluation of conditions ===== diff --git a/monitoring/uss_qualifier/scripts/report_analyzer.py b/monitoring/uss_qualifier/scripts/report_analyzer.py index 48b3e3ea01..30cbebf6ab 100644 --- a/monitoring/uss_qualifier/scripts/report_analyzer.py +++ b/monitoring/uss_qualifier/scripts/report_analyzer.py @@ -58,7 +58,7 @@ def main(): for a in r.report.test_suite.actions: print("Types of actions (test_suite, test_scenario, action_generator): ") - print(a._get_applicable_report()) + print(a) suite_reports = { subr.test_suite.name: subr.test_suite diff --git a/monitoring/uss_qualifier/suites/definitions.py b/monitoring/uss_qualifier/suites/definitions.py index ca5a94c00b..037885f6f4 100644 --- a/monitoring/uss_qualifier/suites/definitions.py +++ b/monitoring/uss_qualifier/suites/definitions.py @@ -68,18 +68,6 @@ class ReactionToFailure(str, Enum): """If the test suite action fails, do not execute any more actions in that test suite""" -class ActionType(str, Enum): - TestScenario = "test_scenario" - TestSuite = "test_suite" - ActionGenerator = "action_generator" - - @staticmethod - def build_invalid_action_declaration() -> Exception: - return ValueError( - f"Exactly one of ({', '.join(a for a in ActionType)}) must be specified in a TestSuiteActionDeclaration" - ) - - class TestSuiteActionDeclaration(ImplicitDict): """Defines a step in the sequence of things to do for a test suite. @@ -98,33 +86,41 @@ class TestSuiteActionDeclaration(ImplicitDict): on_failure: ReactionToFailure = ReactionToFailure.Continue """What to do if this action fails""" - def get_action_type(self) -> ActionType: - matches = [v for v in ActionType if v in self and self[v]] - if len(matches) != 1: - raise ActionType.build_invalid_action_declaration() - return ActionType(matches[0]) + @property + def invalid_type_error(self): + return ValueError( + "Invalid TestSuiteActionDeclaration: test_scenario, test_suite or action_generator must be specified" + ) def get_resource_links(self) -> dict[ResourceID, ResourceID]: - action_type = self.get_action_type() - if action_type == ActionType.TestScenario and self.test_scenario: + if "test_scenario" in self and self.test_scenario: return self.test_scenario.resources or {} - elif action_type == ActionType.TestSuite and self.test_suite: + elif "test_suite" in self and self.test_suite: return self.test_suite.resources or {} - elif action_type == ActionType.ActionGenerator and self.action_generator: + elif "action_generator" in self and self.action_generator: return self.action_generator.resources else: - raise ActionType.build_invalid_action_declaration() + raise self.invalid_type_error def get_child_type(self) -> str: - action_type = self.get_action_type() - if action_type == ActionType.TestScenario and self.test_scenario: + if "test_scenario" in self and self.test_scenario: return self.test_scenario.scenario_type - elif action_type == ActionType.TestSuite and self.test_suite: + elif "test_suite" in self and self.test_suite: return self.test_suite.type_name - elif action_type == ActionType.ActionGenerator and self.action_generator: + elif "action_generator" in self and self.action_generator: return self.action_generator.generator_type else: - raise ActionType.build_invalid_action_declaration() + raise self.invalid_type_error + + def __str__(self) -> str: + if "test_scenario" in self and self.test_scenario: + return "TestScenario" + elif "test_suite" in self and self.test_suite: + return "TestSuite" + elif "action_generator" in self and self.action_generator: + return "ActionGenerator" + else: + return "UnknownType" ResourceTypeNameSpecifyingOptional = ResourceTypeName diff --git a/monitoring/uss_qualifier/suites/documentation/documentation.py b/monitoring/uss_qualifier/suites/documentation/documentation.py index 6e915eaf22..8a93e29bb6 100644 --- a/monitoring/uss_qualifier/suites/documentation/documentation.py +++ b/monitoring/uss_qualifier/suites/documentation/documentation.py @@ -39,7 +39,6 @@ from monitoring.uss_qualifier.scenarios.scenario import get_scenario_type_by_name from monitoring.uss_qualifier.suites import suite as suite_module from monitoring.uss_qualifier.suites.definitions import ( - ActionType, TestSuiteActionDeclaration, TestSuiteDefinition, TestSuiteTypeName, @@ -285,10 +284,9 @@ def _render_action( action: TestSuiteActionDeclaration | PotentialGeneratedAction, context: TestSuiteRenderContext, ) -> list[str]: - action_type = action.get_action_type() - if action_type == ActionType.TestScenario and action.test_scenario: + if "test_scenario" in action and action.test_scenario: return _render_scenario(action.test_scenario.scenario_type, context) - elif action_type == ActionType.TestSuite and action.test_suite: + elif "test_suite" in action and action.test_suite: if "suite_type" in action.test_suite and action.test_suite.suite_type: return _render_suite_by_type(action.test_suite.suite_type, context) elif ( @@ -302,10 +300,10 @@ def _render_action( raise ValueError( f"Test suite action {context.list_index} missing suite type or definition" ) - elif action_type == ActionType.ActionGenerator and action.action_generator: + elif "action_generator" in action and action.action_generator: return _render_action_generator(action.action_generator, context) else: - raise NotImplementedError(f"Unsupported test suite action type: {action_type}") + raise action.invalid_type_error @dataclass @@ -342,10 +340,9 @@ def combine(new_reqs: dict[RequirementID, RequirementInSuite]) -> None: def _collect_requirements_from_action( action: TestSuiteActionDeclaration | PotentialGeneratedAction, ) -> dict[RequirementID, RequirementInSuite]: - action_type = action.get_action_type() - if action_type == ActionType.TestScenario and action.test_scenario: + if "test_scenario" in action and action.test_scenario: return _collect_requirements_from_scenario(action.test_scenario.scenario_type) - elif action_type == ActionType.TestSuite and action.test_suite: + elif "test_suite" in action and action.test_suite: if "suite_type" in action.test_suite and action.test_suite.suite_type: suite_def = ImplicitDict.parse( load_dict_with_references(action.test_suite.suite_type), @@ -363,12 +360,10 @@ def _collect_requirements_from_action( raise ValueError( "Neither suite_type nor suite_definition specified in test_suite action" ) - elif action_type == ActionType.ActionGenerator and action.action_generator: + elif "action_generator" in action and action.action_generator: return _collect_requirements_from_action_generator(action.action_generator) else: - raise NotImplementedError( - f"Test suite action type {action_type} not yet supported" - ) + raise action.invalid_type_error def _collect_requirements_from_scenario( diff --git a/monitoring/uss_qualifier/suites/suite.py b/monitoring/uss_qualifier/suites/suite.py index a374481af5..d8bffb21cf 100644 --- a/monitoring/uss_qualifier/suites/suite.py +++ b/monitoring/uss_qualifier/suites/suite.py @@ -56,7 +56,6 @@ get_scenario_type_by_name, ) from monitoring.uss_qualifier.suites.definitions import ( - ActionType, ReactionToFailure, TestSuiteActionDeclaration, TestSuiteDeclaration, @@ -91,25 +90,24 @@ def __init__( resources_for_child = make_child_resources( resources, action.get_resource_links(), - f"Test suite action to run {action.get_action_type()} {action.get_child_type()}", + f"Test suite action to run {action} {action.get_child_type()}", ) - action_type = action.get_action_type() - if action_type == ActionType.TestScenario and action.test_scenario: + if "test_scenario" in action and action.test_scenario: self.test_scenario = TestScenario.make_test_scenario( declaration=action.test_scenario, resource_pool=resources_for_child ) - elif action_type == ActionType.TestSuite and action.test_suite: + elif "test_suite" in action and action.test_suite: self.test_suite = TestSuite( declaration=action.test_suite, resources=resources, ) - elif action_type == ActionType.ActionGenerator and action.action_generator: + elif "action_generator" in action and action.action_generator: self.action_generator = ActionGenerator.make_from_definition( definition=action.action_generator, resources=resources_for_child ) else: - raise ActionType.build_invalid_action_declaration() + raise action.invalid_type_error def get_name(self) -> str: if self.test_suite: @@ -128,7 +126,7 @@ def run(self, context: ExecutionContext) -> TestSuiteActionReport: skip_report = context.evaluate_skip() if skip_report: logger.warning( - f"Skipping {self.declaration.get_action_type()} '{self.get_name()}' because: {skip_report.reason}" + f"Skipping {self.declaration} '{self.get_name()}' because: {skip_report.reason}" ) report = TestSuiteActionReport(skipped_action=skip_report) else: @@ -291,7 +289,7 @@ def __init__( ) except MissingResourceError as e: logger.warning( - f"Skipping action {a} ({action_dec.get_action_type()} {action_dec.get_child_type()}) because {str(e)}" + f"Skipping action {a} ({action_dec} {action_dec.get_child_type()}) because {str(e)}" ) actions.append( SkippedActionReport( @@ -700,7 +698,7 @@ def end_action( if self.current_frame.action is not action: raise RuntimeError( - f"Action {self.current_frame.action.declaration.get_action_type()} {self.current_frame.action.declaration.get_child_type()} was started, but a different action {action.declaration.get_action_type()} {action.declaration.get_child_type()} was ended" + f"Action {self.current_frame.action.declaration} {self.current_frame.action.declaration.get_child_type()} was started, but a different action {action.declaration} {action.declaration.get_child_type()} was ended" ) self.current_frame.report = report self.current_frame = self.current_frame.parent