Skip to content

Commit

Permalink
[uss_qualifier] Separate action generator from test suite (#192)
Browse files Browse the repository at this point in the history
Separate action generator from test suite
  • Loading branch information
BenjaminPelletier authored Sep 8, 2023
1 parent 8a4064e commit d5168af
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 194 deletions.
1 change: 0 additions & 1 deletion monitoring/uss_qualifier/action_generators/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
from .repeat import Repeat
84 changes: 84 additions & 0 deletions monitoring/uss_qualifier/action_generators/action_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from __future__ import annotations
from abc import ABC, abstractmethod
import inspect
from typing import Generic, Dict, Optional, TypeVar

from implicitdict import ImplicitDict
from monitoring import uss_qualifier as uss_qualifier_module
from monitoring.monitorlib.inspection import (
import_submodules,
get_module_object_by_name,
)
from monitoring.uss_qualifier.action_generators.definitions import (
ActionGeneratorSpecificationType,
ActionGeneratorDefinition,
)
from monitoring.uss_qualifier.reports.report import TestSuiteActionReport
from monitoring.uss_qualifier.resources.definitions import ResourceID
from monitoring.uss_qualifier.resources.resource import ResourceType


class ActionGenerator(ABC, Generic[ActionGeneratorSpecificationType]):
definition: ActionGeneratorDefinition

@abstractmethod
def __init__(
self,
specification: ActionGeneratorSpecificationType,
resources: Dict[ResourceID, ResourceType],
):
"""Create an instance of the action generator.
Concrete subclasses of ActionGenerator must implement their constructor according to this specification.
:param specification: A serializable (subclass of implicitdict.ImplicitDict) specification for how to create the action generator. This parameter may be omitted if not needed.
:param resources: All of the resources available in the test suite in which the action generator is run.
"""
raise NotImplementedError(
"A concrete action generator type must implement __init__ method"
)

@abstractmethod
def run_next_action(self) -> Optional[TestSuiteActionReport]:
"""Run the next action from the generator, or else return None if there are no more actions"""
raise NotImplementedError(
"A concrete action generator must implement `actions` method"
)

@staticmethod
def make_from_definition(
definition: ActionGeneratorDefinition, resources: Dict[ResourceID, ResourceType]
) -> ActionGeneratorType:
from monitoring.uss_qualifier import (
action_generators as action_generators_module,
)

import_submodules(action_generators_module)
action_generator_type = get_module_object_by_name(
parent_module=uss_qualifier_module,
object_name=definition.generator_type,
)
if not issubclass(action_generator_type, ActionGenerator):
raise NotImplementedError(
"Action generator type {} is not a subclass of the ActionGenerator base class".format(
action_generator_type.__name__
)
)
constructor_signature = inspect.signature(action_generator_type.__init__)
specification_type = None
constructor_args = {}
for arg_name, arg in constructor_signature.parameters.items():
if arg_name == "specification":
specification_type = arg.annotation
break
if specification_type is not None:
constructor_args["specification"] = ImplicitDict.parse(
definition.specification, specification_type
)
constructor_args["resources"] = resources
generator = action_generator_type(**constructor_args)
generator.definition = definition
return generator


ActionGeneratorType = TypeVar("ActionGeneratorType", bound=ActionGenerator)
31 changes: 31 additions & 0 deletions monitoring/uss_qualifier/action_generators/definitions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from typing import TypeVar, Dict

from implicitdict import ImplicitDict
from monitoring.uss_qualifier.resources.definitions import ResourceID


GeneratorTypeName = str
"""This plain string represents a type of action generator, expressed as a Python class name qualified relative to the `uss_qualifier.action_generators` module"""


ActionGeneratorSpecificationType = TypeVar(
"ActionGeneratorSpecificationType", bound=ImplicitDict
)


class ActionGeneratorDefinition(ImplicitDict):
generator_type: GeneratorTypeName
"""Type of action generator"""

specification: dict = {}
"""Specification of action generator; format is the ActionGeneratorSpecificationType that corresponds to the `generator_type`"""

resources: Dict[ResourceID, ResourceID]
"""Mapping of the ID a resource will be known by in the child action -> the ID a resource is known by in the parent test suite.
The child action resource ID <key> is supplied by the parent test suite resource ID <value>.
Resources not included in this field will not be available to the child action.
If the parent resource ID is suffixed with ? then the resource will not be required (and will not be populated for the child action when not present in the parent)
"""
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from monitoring.uss_qualifier.action_generators.repetition.repeat import Repeat
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,74 @@
},
"action": {
"test_suite": {
"suite_type": "suites.interuss.generate_test_data_twice",
"suite_definition": {
"name": "Generate RID test data twice",
"resources": {
"adjacent_circular_flights_data": "resources.netrid.FlightDataResource",
"adjacent_circular_storage_config": "resources.netrid.FlightDataStorageResource",
"kml_flights_data": "resources.netrid.FlightDataResource",
"kml_storage_config": "resources.netrid.FlightDataStorageResource"
},
"actions": [
{
"action_generator": {
"generator_type": "action_generators.repetition.Repeat",
"specification": {
"action_to_repeat": {
"test_suite": {
"suite_definition": {
"name": "Generate RID test data",
"resources": {
"adjacent_circular_flights_data": "resources.netrid.FlightDataResource",
"adjacent_circular_storage_config": "resources.netrid.FlightDataStorageResource",
"kml_flights_data": "resources.netrid.FlightDataResource",
"kml_storage_config": "resources.netrid.FlightDataStorageResource"
},
"actions": [
{
"test_scenario": {
"scenario_type": "scenarios.astm.netrid.StoreFlightData",
"resources": {
"flights_data": "adjacent_circular_flights_data",
"storage_configuration": "adjacent_circular_storage_config"
}
},
"on_failure": "Continue"
},
{
"test_scenario": {
"scenario_type": "scenarios.astm.netrid.StoreFlightData",
"resources": {
"flights_data": "kml_flights_data",
"storage_configuration": "kml_storage_config"
}
},
"on_failure": "Continue"
}
]
},
"resources": {
"adjacent_circular_flights_data": "adjacent_circular_flights_data",
"adjacent_circular_storage_config": "adjacent_circular_storage_config",
"kml_flights_data": "kml_flights_data",
"kml_storage_config": "kml_storage_config"
}
},
"on_failure": "Abort"
},
"times_to_repeat": 2
},
"resources": {
"adjacent_circular_flights_data": "adjacent_circular_flights_data",
"adjacent_circular_storage_config": "adjacent_circular_storage_config",
"kml_flights_data": "kml_flights_data",
"kml_storage_config": "kml_storage_config"
}
},
"on_failure": "Continue"
}
]
},
"resources": {
"adjacent_circular_flights_data": "adjacent_circular_flights_data",
"adjacent_circular_storage_config": "adjacent_circular_storage_config",
Expand Down
34 changes: 5 additions & 29 deletions monitoring/uss_qualifier/suites/definitions.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from __future__ import annotations
from enum import Enum
from typing import Dict, List, Optional, TypeVar
from typing import Dict, List, Optional

from implicitdict import ImplicitDict
from monitoring.uss_qualifier.action_generators.definitions import (
ActionGeneratorDefinition,
)

from monitoring.uss_qualifier.fileio import load_dict_with_references, FileReference
from monitoring.uss_qualifier.reports.capability_definitions import (
Expand Down Expand Up @@ -53,33 +56,6 @@ def type_name(self) -> str:
return "<in-configuration definition>"


GeneratorTypeName = str
"""This plain string represents a type of action generator, expressed as a Python class name qualified relative to the `uss_qualifier.action_generators` module"""


ActionGeneratorSpecificationType = TypeVar(
"ActionGeneratorSpecificationType", bound=ImplicitDict
)


class ActionGeneratorDefinition(ImplicitDict):
generator_type: GeneratorTypeName
"""Type of action generator"""

specification: dict = {}
"""Specification of action generator; format is the ActionGeneratorSpecificationType that corresponds to the `generator_type`"""

resources: Dict[ResourceID, ResourceID]
"""Mapping of the ID a resource will be known by in the child action -> the ID a resource is known by in the parent test suite.
The child action resource ID <key> is supplied by the parent test suite resource ID <value>.
Resources not included in this field will not be available to the child action.
If the parent resource ID is suffixed with ? then the resource will not be required (and will not be populated for the child action when not present in the parent)
"""


class ReactionToFailure(str, Enum):
Continue = "Continue"
"""If the test suite action fails, continue to the next action in that test suite"""
Expand Down Expand Up @@ -168,7 +144,7 @@ class TestSuiteDefinition(ImplicitDict):
@staticmethod
def load_from_declaration(
declaration: TestSuiteDeclaration,
) -> "TestSuiteDefinition":
) -> TestSuiteDefinition:
if "suite_type" in declaration:
return ImplicitDict.parse(
load_dict_with_references(declaration.suite_type), TestSuiteDefinition
Expand Down
19 changes: 0 additions & 19 deletions monitoring/uss_qualifier/suites/interuss/generate_test_data.yaml

This file was deleted.

This file was deleted.

Loading

0 comments on commit d5168af

Please sign in to comment.