From 95a1eace7e1e600994938c09d0fce3ff45fd7b37 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 1 May 2024 17:29:39 +0100 Subject: [PATCH] Move MS-05 tests into MS0501Test Move MS-05 utility functions into MS05Utils --- nmostesting/IS12Utils.py | 103 +--- nmostesting/IS14Utils.py | 111 +++- nmostesting/MS05Utils.py | 144 ++++- nmostesting/suites/IS1201Test.py | 997 +------------------------------ nmostesting/suites/IS1202Test.py | 94 +-- nmostesting/suites/IS1401Test.py | 35 +- 6 files changed, 311 insertions(+), 1173 deletions(-) diff --git a/nmostesting/IS12Utils.py b/nmostesting/IS12Utils.py index c51f0236..e563650e 100644 --- a/nmostesting/IS12Utils.py +++ b/nmostesting/IS12Utils.py @@ -18,12 +18,12 @@ import time from enum import IntEnum -from itertools import takewhile, dropwhile from jsonschema import FormatChecker, SchemaError, validate, ValidationError + from .Config import WS_MESSAGE_TIMEOUT from .GenericTest import NMOSTestException from .TestHelper import WebsocketWorker, load_resolved_schema -from .MS05Utils import NcMethodStatus, NcObjectMethods, NcBlockMethods, NcClassManagerMethods, StandardClassIds +from .MS05Utils import NcMethodStatus, NcObjectMethods, NcBlockMethods, NcClassManagerMethods CONTROL_API_KEY = "ncp" MS05_API_KEY = "controlframework" @@ -41,7 +41,7 @@ class MessageTypes(IntEnum): class IS12Utils(MS05Utils): def __init__(self, apis): - MS05Utils.__init__(self, apis) + MS05Utils.__init__(self, apis, CONTROL_API_KEY) self.apis = apis self.spec_path = self.apis[CONTROL_API_KEY]["spec_path"] self.spec_branch = self.apis[CONTROL_API_KEY]["spec_branch"] @@ -51,6 +51,10 @@ def __init__(self, apis): self.expect_notifications = False self.notifications = [] + # Overridden functions + def initialize_connection(self, test): + self.open_ncp_websocket(test) + def _load_is12_schemas(self): """Load datatype and control class decriptors and create datatype JSON schemas""" # Load IS-12 schemas @@ -219,34 +223,35 @@ def execute_command(self, test, oid, method_id, arguments): response = self.send_command(test, command_JSON) return response["result"] - def get_property_value_polymorphic(self, test, property_id, oid, **kwargs): - return self.get_property_value(test, oid, property_id) - - def get_property_value(self, test, oid, property_id): + def get_property_value(self, test, property_id, oid, **kwargs): """Get value of property from object. Raises NMOSTestException on error""" return self.execute_command(test, oid, NcObjectMethods.GENERIC_GET.value, {'id': property_id})["value"] - def get_property(self, test, oid, property_id): + def get_property(self, test, property_id, oid, **kwargs): """Get property from object. Raises NMOSTestException on error""" return self.execute_command(test, oid, NcObjectMethods.GENERIC_GET.value, {'id': property_id}) - def set_property(self, test, oid, property_id, argument): + def set_property(self, test, property_id, argument, oid, **kwargs): """Get property from object. Raises NMOSTestException on error""" return self.execute_command(test, oid, NcObjectMethods.GENERIC_SET.value, {'id': property_id, 'value': argument}) - def get_sequence_item(self, test, oid, property_id, index): + def get_sequence_item(self, test, property_id, index, oid, **kwargs): """Get value from sequence property. Raises NMOSTestException on error""" return self.execute_command(test, oid, NcObjectMethods.GET_SEQUENCE_ITEM.value, - {'id': property_id, 'index': index})["value"] + {'id': property_id, 'index': index}) + + def get_sequence_item_value(self, test, property_id, index, oid, **kwargs): + """Get value from sequence property. Raises NMOSTestException on error""" + return self.get_sequence_item(test, oid, index, oid=oid)['value'] - def set_sequence_item(self, test, oid, property_id, index, value): + def set_sequence_item(self, test, property_id, index, value, oid, **kwargs): """Add value to a sequence property. Raises NMOSTestException on error""" return self.execute_command(test, oid, NcObjectMethods.SET_SEQUENCE_ITEM.value, @@ -264,25 +269,29 @@ def remove_sequence_item(self, test, oid, property_id, index): NcObjectMethods.REMOVE_SEQUENCE_ITEM.value, {'id': property_id, 'index': index}) - def get_sequence_length(self, test, oid, property_id): - """Get value from sequence property. Raises NMOSTestException on error""" + def get_sequence_length(self, test, property_id, oid, **kwargs): + """Get sequence property. Raises NMOSTestException on error""" return self.execute_command(test, oid, NcObjectMethods.GET_SEQUENCE_LENGTH.value, - {'id': property_id})["value"] + {'id': property_id}) + + def get_sequence_length_value(self, test, property_id, oid, **kwargs): + """Get value from sequence property. Raises NMOSTestException on error""" + return self.get_sequence_length(self, test, property_id, oid=oid)['value'] - def get_member_descriptors(self, test, oid, recurse): + def get_member_descriptors(self, test, recurse, oid, **kwargs): """Get BlockMemberDescritors for this block. Raises NMOSTestException on error""" return self.execute_command(test, oid, NcBlockMethods.GET_MEMBERS_DESCRIPTOR.value, {'recurse': recurse})["value"] - def find_members_by_path(self, test, oid, role_path): + def find_members_by_path(self, test, path, oid, **kwargs): """Query members based on role path. Raises NMOSTestException on error""" return self.execute_command(test, oid, NcBlockMethods.FIND_MEMBERS_BY_PATH.value, - {'path': role_path})["value"] + {'path': path})["value"] - def find_members_by_role(self, test, oid, role, case_sensitive, match_whole_string, recurse): + def find_members_by_role(self, test, role, case_sensitive, match_whole_string, recurse, oid, **kwargs): """Query members based on role. Raises NMOSTestException on error""" return self.execute_command(test, oid, NcBlockMethods.FIND_MEMBERS_BY_ROLE.value, @@ -291,7 +300,7 @@ def find_members_by_role(self, test, oid, role, case_sensitive, match_whole_stri 'matchWholeString': match_whole_string, 'recurse': recurse})["value"] - def find_members_by_class_id(self, test, oid, class_id, include_derived, recurse): + def find_members_by_class_id(self, test, class_id, include_derived, recurse, oid, **kwargs): """Query members based on class id. Raises NMOSTestException on error""" return self.execute_command(test, oid, NcBlockMethods.FIND_MEMBERS_BY_CLASS_ID.value, @@ -325,57 +334,3 @@ def update_subscritions(self, test, subscriptions): command_JSON = self.create_subscription_JSON(subscriptions) response = self.send_command(test, command_JSON) return response - - def primitive_to_python_type(self, type): - """Convert MS-05 primitive type to corresponding Python type""" - - types = { - "NcBoolean": bool, - "NcInt16": int, - "NcInt32": int, - "NcInt64": int, - "NcUint16": int, - "NcUint32": int, - "NcUint64": int, - "NcFloat32": float, - "NcFloat64": float, - "NcString": str - } - - return types.get(type, False) - - def get_base_class_id(self, class_id): - """ Given a class_id returns the standard base class id as a string""" - return '.'.join([str(v) for v in takewhile(lambda x: x > 0, class_id)]) - - def is_non_standard_class(self, class_id): - """ Check class_id to determine if it is for a non-standard class """ - # Assumes at least one value follows the authority key - return len([v for v in dropwhile(lambda x: x > 0, class_id)]) > 1 - - def is_manager(self, class_id): - """ Check class id to determine if this is a manager """ - return len(class_id) > 1 and class_id[0] == 1 and class_id[1] == 3 - - def query_device_model(self, test): - """ Query Device Model from the Node under test. - self.device_model_metadata set on Device Model validation error. - NMOSTestException raised if unable to query Device Model """ - self.open_ncp_websocket(test) - if not self.device_model: - self.device_model = self._nc_object_factory( - test, - StandardClassIds.NCBLOCK.value, - self.ROOT_BLOCK_OID, - "root") - - if not self.device_model: - raise NMOSTestException(test.FAIL("Unable to query Device Model")) - return self.device_model - - def resolve_datatype(self, test, datatype): - """Resolve datatype to its base type""" - class_manager = self.get_class_manager(test) - if class_manager.datatype_descriptors[datatype].get("parentType"): - return self.resolve_datatype(test, class_manager.datatype_descriptors[datatype].get("parentType")) - return datatype diff --git a/nmostesting/IS14Utils.py b/nmostesting/IS14Utils.py index 99a3808f..220a36bb 100644 --- a/nmostesting/IS14Utils.py +++ b/nmostesting/IS14Utils.py @@ -12,52 +12,97 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .MS05Utils import MS05Utils, StandardClassIds +from .MS05Utils import MS05Utils, NcBlockMethods from . import TestHelper -from .GenericTest import NMOSTestException CONFIGURATION_API_KEY = 'configuration' class IS14Utils(MS05Utils): def __init__(self, apis): - MS05Utils.__init__(self, apis) + MS05Utils.__init__(self, apis, CONFIGURATION_API_KEY) self.configuration_url = apis[CONFIGURATION_API_KEY]['url'] - # Overridden functions - def query_device_model(self, test): - """ Query Device Model from the Node under test. - self.device_model_metadata set on Device Model validation error. - NMOSTestException raised if unable to query Device Model """ - if not self.device_model: - self.device_model = self._nc_object_factory( - test, - StandardClassIds.NCBLOCK.value, - self.ROOT_BLOCK_OID, - "root") - - if not self.device_model: - raise NMOSTestException(test.FAIL("Unable to query Device Model")) - return self.device_model - def _format_property_id(self, property_id): return str(property_id['level']) + 'p' + str(property_id['index']) + def _format_method_id(self, method_id): + return str(method_id['level']) + 'm' + str(method_id['index']) + def _format_role_path(self, role_path): return '.'.join(r for r in role_path) - def get_property_value_polymorphic(self, test, property_id, role_path, **kwargs): - """Get value of property from object. Raises NMOSTestException on error""" - formatted_property_id = self._format_property_id(property_id) + def _create_role_path_base(self, role_path): formatted_role_path = self._format_role_path(role_path) - # delimit role path? + return '{}rolePaths/{}'.format(self.configuration_url, + formatted_role_path) + + def _create_property_value_endpoint(self, role_path, property_id): + formatted_property_id = self._format_property_id(property_id) + return self._create_role_path_base(role_path) + '/properties/{}/value'.format(formatted_property_id) + + def _create_methods_endpoint(self, role_path, method_id): + formatted_method_id = self._format_method_id(method_id) + return self._create_role_path_base(role_path) + '/methods/{}'.format(formatted_method_id) + + # Overridden functions + def get_property(self, test, property_id, role_path, **kwargs): + """Get value of property from object. Raises NMOSTestException on error""" + property_value_endpoint = self._create_property_value_endpoint(role_path, property_id) + + valid, r = TestHelper.do_request('GET', property_value_endpoint) + + if valid and r.status_code == 200: + try: + return r.json() + except ValueError: + pass + + return None + + def get_property_value(self, test, property_id, role_path, **kwargs): + """Get value of property from object. Raises NMOSTestException on error""" + return self.get_property(test, property_id, role_path=role_path)['value'] + + def set_property(self, test, property_id, argument, role_path, **kwargs): + """Get value of property from object. Raises NMOSTestException on error""" + property_value_endpoint = self._create_property_value_endpoint(role_path, property_id) + + valid, r = TestHelper.do_request('SET', property_value_endpoint, data={'value': argument}) + + if valid and r.status_code == 200: + try: + return r.json() + except ValueError: + pass + + return None + + def get_sequence_item(self, test, property_id, index, role_path, **kwargs): + # Hmmm should we reply on get or should we assume that get_sequence_item has been + # implemented? + value = self.get_property_value(test, property_id, role_path=role_path) + + return value[index] + + def get_sequence_length_value(self, test, property_id, role_path, **kwargs): + # Hmmm should we reply on get or should we assume that get_sequence_item has been + # implemented? + value = self.get_property_value(test, property_id, role_path=role_path) + + return len(value) + + def get_sequence_length(self, test, property_id, oid, **kwargs): + """Get sequence property. Raises NMOSTestException on error""" + return None + + def get_member_descriptors(self, test, recurse, role_path, **kwargs): + """Get BlockMemberDescritors for this block. Raises NMOSTestException on error""" + methods_endpoint = self._create_methods_endpoint(role_path, NcBlockMethods.GET_MEMBERS_DESCRIPTOR.value) # get the api base from the apis - get_property_endpoint = '{}rolePaths/{}/properties/{}/value'.format(self.configuration_url, - formatted_role_path, - formatted_property_id) - valid, r = TestHelper.do_request('GET', get_property_endpoint) + valid, r = TestHelper.do_request('PATCH', methods_endpoint, data={'argument': {'recurse': recurse}}) if valid and r.status_code == 200: try: @@ -67,4 +112,16 @@ def get_property_value_polymorphic(self, test, property_id, role_path, **kwargs) return None + def find_members_by_path(self, test, path, role_path, **kwargs): + """Query members based on role path. Raises NMOSTestException on error""" + return None + + def find_members_by_role(self, test, role, case_sensitive, match_whole_string, recurse, role_path, **kwargs): + """Query members based on role. Raises NMOSTestException on error""" + return None + + def find_members_by_class_id(self, test, class_id, include_derived, recurse, role_path, **kwargs): + """Query members based on class id. Raises NMOSTestException on error""" + return None + # end of overridden functions diff --git a/nmostesting/MS05Utils.py b/nmostesting/MS05Utils.py index 107a0fab..1dc64203 100644 --- a/nmostesting/MS05Utils.py +++ b/nmostesting/MS05Utils.py @@ -19,8 +19,10 @@ from copy import deepcopy from enum import IntEnum, Enum -from .GenericTest import NMOSTestException +from itertools import takewhile, dropwhile from jsonschema import FormatChecker, SchemaError, validate, ValidationError + +from .GenericTest import NMOSTestException from .TestHelper import load_resolved_schema MS05_API_KEY = "controlframework" @@ -29,25 +31,73 @@ class MS05Utils(NMOSUtils): - def __init__(self, apis): + def __init__(self, apis, protocol_api_key): NMOSUtils.__init__(self, apis[NODE_API_KEY]["url"]) self.apis = apis self.ms05_spec_branch = self.apis[MS05_API_KEY]["spec_branch"] self.ROOT_BLOCK_OID = 1 self.device_model = None self.class_manager = None + self.protocol_api_key = protocol_api_key - # Overriddenfunctions specialized for IS-12 and IS-14 - def query_device_model(self, test): + # Overridden functions specialized for IS-12 and IS-14 + def initialize_connection(self, test): + # For IS-12 this is overridden with the websocket initialization + # For IS-14 no connection initialization is needed + pass + + def get_property(test, property_id, **kwargs): + pass + + def get_property_value(self, test, property_id, **kwargs): + pass + + def set_property(self, test, property_id, argument, **kwargs): + pass + + def get_sequence_item(self, test, property_id, index, **kwargs): + pass + + def get_sequence_length(self, test, property_id, **kwargs): + """Get sequence property. Raises NMOSTestException on error""" + pass + + def get_sequence_length_value(self, test, property_id, **kwargs): + pass + + def get_member_descriptors(self, test, recurse, **kwargs): pass - # JRT this could probably be spolit into an "initialize connection function, - # and the actual querying of the device model - def get_property_value_polymorphic(self, test, property_id, role_path, **kwargs): + def find_members_by_path(self, test, path, **kwargs): + """Query members based on role path. Raises NMOSTestException on error""" + pass + + def find_members_by_role(self, test, role, case_sensitive, match_whole_string, recurse, **kwargs): + """Query members based on role. Raises NMOSTestException on error""" + pass + + def find_members_by_class_id(self, test, class_id, include_derived, recurse, **kwargs): + """Query members based on class id. Raises NMOSTestException on error""" pass # End of overridden functions + def query_device_model(self, test): + """ Query Device Model from the Node under test. + self.device_model_metadata set on Device Model validation error. + NMOSTestException raised if unable to query Device Model """ + self.initialize_connection(test) + if not self.device_model: + self.device_model = self._nc_object_factory( + test, + StandardClassIds.NCBLOCK.value, + self.ROOT_BLOCK_OID, + "root") + + if not self.device_model: + raise NMOSTestException(test.FAIL("Unable to query Device Model")) + return self.device_model + def _primitive_to_JSON(self, type): """Convert MS-05 primitive type to corresponding JSON type""" @@ -66,7 +116,7 @@ def _primitive_to_JSON(self, type): return types.get(type, False) - def load_reference_resources(self, protocol_api_key): + def load_reference_resources(self): """Load datatype and control class decriptors and create datatype JSON schemas""" # Calculate paths to MS-05 descriptors # including Feature Sets specified as additional_paths in test definition @@ -95,7 +145,7 @@ def load_reference_resources(self, protocol_api_key): # Generate MS-05 datatype schemas from MS-05 datatype descriptors self.reference_datatype_schemas = self.generate_json_schemas( datatype_descriptors=self.reference_datatype_descriptors, - schema_path=os.path.join(self.apis[protocol_api_key]["spec_path"], 'APIs/schemas/')) + schema_path=os.path.join(self.apis[self.protocol_api_key]["spec_path"], 'APIs/schemas/')) def _load_model_descriptors(self, descriptor_paths): descriptors = {} @@ -194,6 +244,17 @@ def descriptor_to_schema(self, descriptor): return json_schema + def generate_device_model_datatype_schemas(self, test): + # Generate datatype schemas based on the datatype decriptors + # queried from the Node under test's Device Model. + # This will include any Non-standard data types + class_manager = self.get_class_manager(test) + + # Create JSON schemas for the queried datatypes + return self.generate_json_schemas( + datatype_descriptors=class_manager.datatype_descriptors, + schema_path=os.path.join(self.apis[self.protocol_api_key]["spec_path"], 'APIs/tmp_schemas/')) + def validate_reference_datatype_schema(self, test, payload, datatype_name, context=""): """Validate payload against reference datatype schema""" self.validate_schema(test, payload, self.reference_datatype_schemas[datatype_name]) @@ -257,7 +318,7 @@ def validate_descriptor(self, test, reference, descriptor, context=""): return def _get_class_manager_descriptors(self, test, property_id, class_manager_oid, role_path): - response = self.get_property_value_polymorphic(test, property_id, oid=class_manager_oid, role_path=role_path) + response = self.get_property_value(test, property_id, oid=class_manager_oid, role_path=role_path) if not response: return None @@ -279,7 +340,7 @@ def _nc_object_factory(self, test, class_id, oid, role, _role_path=None): role_path.append(role) try: - runtime_constraints = self.get_property_value_polymorphic( + runtime_constraints = self.get_property_value( test, NcObjectProperties.RUNTIME_PROPERTY_CONSTRAINTS.value, oid=oid, @@ -287,7 +348,7 @@ def _nc_object_factory(self, test, class_id, oid, role, _role_path=None): # Check class id to determine if this is a block if len(class_id) > 1 and class_id[0] == 1 and class_id[1] == 1: - member_descriptors = self.get_property_value_polymorphic( + member_descriptors = self.get_property_value( test, NcBlockProperties.MEMBERS.value, oid=oid, @@ -297,7 +358,7 @@ def _nc_object_factory(self, test, class_id, oid, role, _role_path=None): raise NMOSTestException(test.FAIL('Unable to get members for object: oid={}, role Path={}' .format(str(oid), str(role_path)))) - nc_block = NcBlock(class_id, oid, role, member_descriptors, runtime_constraints) + nc_block = NcBlock(class_id, oid, role, role_path, member_descriptors, runtime_constraints) for m in member_descriptors: child_object = self._nc_object_factory(test, m["classId"], m["oid"], m["role"], role_path) @@ -327,11 +388,12 @@ def _nc_object_factory(self, test, class_id, oid, role, _role_path=None): return NcClassManager(class_id, oid, role, + role_path, class_descriptors, datatype_descriptors, runtime_constraints) - return NcObject(class_id, oid, role, runtime_constraints) + return NcObject(class_id, oid, role, role_path, runtime_constraints) except NMOSTestException as e: raise NMOSTestException(test.FAIL("Error in Device Model " + role + ": " + str(e.args[0].detail))) @@ -361,6 +423,44 @@ def _get_manager(self, test, class_id): return members[0] + def primitive_to_python_type(self, type): + """Convert MS-05 primitive type to corresponding Python type""" + + types = { + "NcBoolean": bool, + "NcInt16": int, + "NcInt32": int, + "NcInt64": int, + "NcUint16": int, + "NcUint32": int, + "NcUint64": int, + "NcFloat32": float, + "NcFloat64": float, + "NcString": str + } + + return types.get(type, False) + + def get_base_class_id(self, class_id): + """ Given a class_id returns the standard base class id as a string""" + return '.'.join([str(v) for v in takewhile(lambda x: x > 0, class_id)]) + + def is_non_standard_class(self, class_id): + """ Check class_id to determine if it is for a non-standard class """ + # Assumes at least one value follows the authority key + return len([v for v in dropwhile(lambda x: x > 0, class_id)]) > 1 + + def is_manager(self, class_id): + """ Check class id to determine if this is a manager """ + return len(class_id) > 1 and class_id[0] == 1 and class_id[1] == 3 + + def resolve_datatype(self, test, datatype): + """Resolve datatype to its base type""" + class_manager = self.get_class_manager(test) + if class_manager.datatype_descriptors[datatype].get("parentType"): + return self.resolve_datatype(test, class_manager.datatype_descriptors[datatype].get("parentType")) + return datatype + class NcMethodStatus(IntEnum): OK = 200 @@ -473,16 +573,17 @@ class StandardClassIds(Enum): class NcObject(): - def __init__(self, class_id, oid, role, runtime_constraints): + def __init__(self, class_id, oid, role, role_path, runtime_constraints): self.class_id = class_id self.oid = oid self.role = role + self.role_path = role_path self.runtime_constraints = runtime_constraints class NcBlock(NcObject): - def __init__(self, class_id, oid, role, descriptors, runtime_constraints): - NcObject.__init__(self, class_id, oid, role, runtime_constraints) + def __init__(self, class_id, oid, role, role_path, descriptors, runtime_constraints): + NcObject.__init__(self, class_id, oid, role, role_path, runtime_constraints) self.child_objects = [] self.member_descriptors = descriptors @@ -490,6 +591,7 @@ def __init__(self, class_id, oid, role, descriptors, runtime_constraints): def add_child_object(self, nc_object): self.child_objects.append(nc_object) + # JRT this could be simplified now that we have the role_path as a member def get_role_paths(self): role_paths = [] for child_object in self.child_objects: @@ -565,13 +667,13 @@ def match(query_class_id, class_id, include_derived): class NcManager(NcObject): - def __init__(self, class_id, oid, role, runtime_constraints): - NcObject.__init__(self, class_id, oid, role, runtime_constraints) + def __init__(self, class_id, oid, role, role_path, runtime_constraints): + NcObject.__init__(self, class_id, oid, role, role_path, runtime_constraints) class NcClassManager(NcManager): - def __init__(self, class_id, oid, role, class_descriptors, datatype_descriptors, runtime_constraints): - NcObject.__init__(self, class_id, oid, role, runtime_constraints) + def __init__(self, class_id, oid, role, role_path, class_descriptors, datatype_descriptors, runtime_constraints): + NcObject.__init__(self, class_id, oid, role, role_path, runtime_constraints) self.class_descriptors = class_descriptors self.datatype_descriptors = datatype_descriptors diff --git a/nmostesting/suites/IS1201Test.py b/nmostesting/suites/IS1201Test.py index fd128f6a..bb80e279 100644 --- a/nmostesting/suites/IS1201Test.py +++ b/nmostesting/suites/IS1201Test.py @@ -12,19 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os import time -from itertools import product - from ..Config import WS_MESSAGE_TIMEOUT -from ..GenericTest import GenericTest, NMOSTestException +from ..GenericTest import NMOSTestException from ..IS12Utils import IS12Utils from ..MS05Utils import NcMethodStatus, NcBlockProperties, \ NcObjectMethods, NcObjectProperties, \ StandardClassIds, NcBlock, NcDatatypeType, \ - NcPropertyChangeType, NcObjectEvents, NcDeviceManagerProperties + NcPropertyChangeType, NcObjectEvents from .MS0501Test import MS0501Test @@ -34,17 +31,18 @@ MS05_API_KEY = "controlframework" -class IS1201Test(GenericTest): +class IS1201Test(MS0501Test): def __init__(self, apis, **kwargs): # Remove the RAML key to prevent this test suite from auto-testing IS-04 API apis[NODE_API_KEY].pop("raml", None) - GenericTest.__init__(self, apis, **kwargs) + MS0501Test.__init__(self, apis, **kwargs) self.node_url = apis[NODE_API_KEY]["url"] self.ncp_url = apis[CONTROL_API_KEY]["url"] self.is12_utils = IS12Utils(apis) - self.is12_utils.load_reference_resources(CONTROL_API_KEY) - self.ms0501Test = MS0501Test(apis, self.is12_utils) + self.set_utils(self.is12_utils) + self.is12_utils.load_reference_resources() +# self.ms0501Test = MS0501Test(apis, self.is12_utils) def set_up_tests(self): super().set_up_tests() @@ -70,136 +68,6 @@ def tear_down_tests(self): # Clean up Websocket resources self.is12_utils.close_ncp_websocket() - def execute_tests(self, test_names): - """Perform tests defined within this class""" - # Override to allow 'auto' testing of MS-05 types and classes - - for test_name in test_names: - if test_name in ["auto", "all"] and not self.disable_auto: - # Validate all standard datatypes and classes advertised by the Class Manager - try: - self.result += self.auto_tests() - except NMOSTestException as e: - self.result.append(e.args[0]) - self.execute_test(test_name) - - def get_datatype_schema(self, test, type_name): - """Get generated JSON schema for datatype specified""" - if not self.datatype_schemas: - self.datatype_schemas = self._generate_device_model_datatype_schemas(test) - - return self.datatype_schemas.get(type_name) - - def _generate_device_model_datatype_schemas(self, test): - # Generate datatype schemas based on the datatype decriptors - # queried from the Node under test's Device Model. - # This will include any Non-standard data types - class_manager = self.is12_utils.get_class_manager(test) - - # Create JSON schemas for the queried datatypes - return self.is12_utils.generate_json_schemas( - datatype_descriptors=class_manager.datatype_descriptors, - schema_path=os.path.join(self.apis[CONTROL_API_KEY]["spec_path"], 'APIs/tmp_schemas/')) - - def _validate_property_type(self, test, value, data_type, is_nullable, context=""): - """Validate the the property value is correct according to the type. Raises NMOSTestException on error""" - if value is None: - if is_nullable: - return - else: - raise NMOSTestException(test.FAIL(context + "Non-nullable property set to null.")) - - if self.is12_utils.primitive_to_python_type(data_type): - # Special case: if this is a floating point value it - # can be intepreted as an int in the case of whole numbers - # e.g. 0.0 -> 0, 1.0 -> 1 - if self.is12_utils.primitive_to_python_type(data_type) == float and isinstance(value, int): - return - - if not isinstance(value, self.is12_utils.primitive_to_python_type(data_type)): - raise NMOSTestException(test.FAIL(context + str(value) + " is not of type " + str(data_type))) - else: - self.is12_utils.validate_schema(test, value, self.get_datatype_schema(test, data_type), context) - - return - - def get_property(self, test, oid, property_id, context): - """Get property from object. Sets self.device_model_metadata on error""" - try: - return self.is12_utils.get_property(test, oid, property_id) - except NMOSTestException as e: - self.device_model_metadata["error"] = True - self.device_model_metadata["error_msg"] += context \ - + "Error getting property: " \ - + str(property_id) + ": " \ - + str(e.args[0].detail) \ - + "; " - return None - - def get_property_value(self, test, oid, property_id, context): - """Get value of property from object. Sets self.device_model_metadata on error""" - try: - return self.is12_utils.get_property_value(test, oid, property_id) - except NMOSTestException as e: - self.device_model_metadata["error"] = True - self.device_model_metadata["error_msg"] += context \ - + "Error getting property: " \ - + str(property_id) + ": " \ - + str(e.args[0].detail) \ - + "; " - return None - - # def validate_model_definitions(self, descriptors, schema_name, reference_descriptors): - # """ Validate Class Manager model definitions against reference model descriptors. - # Returns [test result array] """ - # results = list() - - # reference_descriptor_keys = sorted(reference_descriptors.keys()) - - # for key in reference_descriptor_keys: - # test = Test("Validate " + str(key) + " definition", "auto_" + str(key)) - # try: - # if descriptors.get(key): - # descriptor = descriptors[key] - - # # Validate descriptor obeys the JSON schema - # self.is12_utils.validate_reference_datatype_schema(test, descriptor, schema_name) - - # # Validate the descriptor is correct - # self.is12_utils.validate_descriptor(test, reference_descriptors[key], descriptor) - - # results.append(test.PASS()) - # else: - # results.append(test.UNCLEAR("Not Implemented")) - # except NMOSTestException as e: - # results.append(e.args[0]) - - # return results - - # def auto_tests(self): - # """Automatically validate all standard datatypes and control classes. Returns [test result array]""" - # # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Framework.html - - # results = list() - # test = Test("Initialize auto tests", "auto_init") - - # #self.is12_utils.open_ncp_websocket(test) - - # class_manager = self.is12_utils.get_class_manager(test) - - # results += self.validate_model_definitions(class_manager.class_descriptors, - # 'NcClassDescriptor', - # self.is12_utils.reference_class_descriptors) - - # results += self.validate_model_definitions(class_manager.datatype_descriptors, - # 'NcDatatypeDescriptor', - # self.is12_utils.reference_datatype_descriptors) - # return results - - def auto_tests(self): - """Automatically validate all standard datatypes and control classes. Returns [test result array]""" - return self.ms0501Test.auto_tests() - def test_01(self, test): """Control Endpoint: Node under test advertises IS-12 control endpoint matching API under test""" # https://specs.amwa.tv/is-12/releases/v1.0.0/docs/IS-04_interactions.html @@ -236,837 +104,6 @@ def test_03(self, test): return test.PASS() - def test_04(self, test): - """Device Model: Root Block exists with correct oid and role""" - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Blocks.html - - self.is12_utils.open_ncp_websocket(test) - - role = self.is12_utils.get_property_value( - test, - self.is12_utils.ROOT_BLOCK_OID, - NcObjectProperties.ROLE.value) - - if role != "root": - return test.FAIL("Unexpected role in Root Block: " + str(role), - "https://specs.amwa.tv/ms-05-02/branches/{}" - "/docs/Blocks.html" - .format(self.apis[CONTROL_API_KEY]["spec_branch"])) - - return test.PASS() - - def check_get_sequence_item(self, test, oid, sequence_values, property_metadata, context=""): - if sequence_values is None and not property_metadata["isNullable"]: - self.get_sequence_item_metadata["error"] = True - self.get_sequence_item_metadata["error_msg"] += \ - context + property_metadata["name"] + ": Non-nullable property set to null, " - return - try: - # GetSequenceItem - self.get_sequence_item_metadata["checked"] = True - sequence_index = 0 - for property_value in sequence_values: - value = self.is12_utils.get_sequence_item(test, oid, property_metadata['id'], sequence_index) - if property_value != value: - self.get_sequence_item_metadata["error"] = True - self.get_sequence_item_metadata["error_msg"] += \ - context + property_metadata["name"] \ - + ": Expected: " + str(property_value) + ", Actual: " + str(value) \ - + " at index " + sequence_index + ", " - sequence_index += 1 - return True - except NMOSTestException as e: - self.get_sequence_item_metadata["error"] = True - self.get_sequence_item_metadata["error_msg"] += \ - context + property_metadata["name"] + ": " + str(e.args[0].detail) + ", " - return False - - def check_get_sequence_length(self, test, oid, sequence_values, property_metadata, context=""): - if sequence_values is None and not property_metadata["isNullable"]: - self.get_sequence_length_metadata["error"] = True - self.get_sequence_length_metadata["error_msg"] += \ - context + property_metadata["name"] + ": Non-nullable property set to null, " - return - - try: - self.get_sequence_length_metadata["checked"] = True - length = self.is12_utils.get_sequence_length(test, oid, property_metadata['id']) - - if length == len(sequence_values): - return True - self.get_sequence_length_metadata["error_msg"] += \ - context + property_metadata["name"] \ - + ": GetSequenceLength error. Expected: " \ - + str(len(sequence_values)) + ", Actual: " + str(length) + ", " - except NMOSTestException as e: - self.get_sequence_length_metadata["error_msg"] += \ - context + property_metadata["name"] + ": " + str(e.args[0].detail) + ", " - self.get_sequence_length_metadata["error"] = True - return False - - def check_sequence_methods(self, test, oid, sequence_values, property_metadata, context=""): - """Check that sequence manipulation methods work correctly""" - self.check_get_sequence_item(test, oid, sequence_values, property_metadata, context) - self.check_get_sequence_length(test, oid, sequence_values, property_metadata, context) - - def check_object_properties(self, test, reference_class_descriptor, oid, context): - for class_property in reference_class_descriptor['properties']: - response = self.get_property(test, oid, class_property.get('id'), context) - - if response is None: - # Can't find this property - do we have an ID clash - self.device_model_metadata["error"] = True - self.device_model_metadata["error_msg"] += \ - "Property does not exist - it is possible that the class id for this class is NOT unique? " \ - + "classId: " + ".".join(map(str, reference_class_descriptor['classId'])) - continue - - object_property = response["value"] - - if class_property["isDeprecated"]: - self.deprecated_property_metadata["checked"] = True - if response["status"] != NcMethodStatus.PropertyDeprecated.value: - self.deprecated_property_metadata["error"] = True - self.deprecated_property_metadata["error_msg"] = context + \ - " PropertyDeprecated status code expected when getting " + class_property["name"] - - if not object_property: - continue - - # validate property type - if class_property['isSequence']: - for property_value in object_property: - self._validate_property_type( - test, - property_value, - class_property['typeName'], - class_property['isNullable'], - context=context + class_property["typeName"] - + ": " + class_property["name"] + ": ") - self.check_sequence_methods(test, - oid, - object_property, - class_property, - context=context) - else: - self._validate_property_type( - test, - object_property, - class_property['typeName'], - class_property['isNullable'], - context=context + class_property["typeName"] - + class_property["name"] + ": ") - return - - def check_unique_roles(self, role, role_cache): - """Check role is unique within containing Block""" - if role in role_cache: - self.unique_roles_error = True - else: - role_cache.append(role) - - def check_unique_oid(self, oid): - """Check oid is globally unique""" - if oid in self.oid_cache: - self.unique_oids_error = True - else: - self.oid_cache.append(oid) - - def check_manager(self, class_id, owner, class_descriptors, manager_cache): - """Check manager is singleton and that it inherits from NcManager""" - # detemine the standard base class name - base_id = self.is12_utils.get_base_class_id(class_id) - base_class_name = class_descriptors[base_id]["name"] - - # manager checks - if self.is12_utils.is_manager(class_id): - if owner != self.is12_utils.ROOT_BLOCK_OID: - self.managers_members_root_block_error = True - if base_class_name in manager_cache: - self.managers_are_singletons_error = True - else: - manager_cache.append(base_class_name) - - def check_touchpoints(self, test, oid, context): - """Touchpoint checks""" - touchpoints = self.get_property_value( - test, - oid, - NcObjectProperties.TOUCHPOINTS.value, - context) - - if touchpoints is not None: - self.touchpoints_metadata["checked"] = True - try: - for touchpoint in touchpoints: - schema = self.get_datatype_schema(test, "NcTouchpointNmos") \ - if touchpoint["contextNamespace"] == "x-nmos" \ - else self.get_datatype_schema(test, "NcTouchpointNmosChannelMapping") - self.is12_utils.validate_schema( - test, - touchpoint, - schema, - context=context + schema["title"] + ": ") - - except NMOSTestException as e: - self.touchpoints_metadata["error"] = True - self.touchpoints_metadata["error_msg"] = context + str(e.args[0].detail) - - def check_block(self, test, block, class_descriptors, context=""): - for child_object in block.child_objects: - # If this child object is a Block, recurse - if type(child_object) is NcBlock: - self.check_block(test, - child_object, - class_descriptors, - context=context + str(child_object.role) + ': ') - role_cache = [] - manager_cache = [] - for descriptor in block.member_descriptors: - self.is12_utils.validate_schema( - test, - descriptor, - self.get_datatype_schema(test, "NcBlockMemberDescriptor"), - context="NcBlockMemberDescriptor: ") - - self.check_unique_roles(descriptor['role'], role_cache) - self.check_unique_oid(descriptor['oid']) - # check for non-standard classes - if self.is12_utils.is_non_standard_class(descriptor['classId']): - self.organization_metadata["checked"] = True - self.check_manager(descriptor['classId'], descriptor["owner"], class_descriptors, manager_cache) - self.check_touchpoints(test, descriptor['oid'], context=context + str(descriptor['role']) + ': ') - - class_identifier = ".".join(map(str, descriptor['classId'])) - if class_identifier and class_identifier in class_descriptors: - self.check_object_properties(test, - class_descriptors[class_identifier], - descriptor['oid'], - context=context + str(descriptor['role']) + ': ') - else: - self.device_model_metadata["error"] = True - self.device_model_metadata["error_msg"] += str(descriptor['role']) + ': ' \ - + "Class not advertised by Class Manager: " \ - + str(descriptor['classId']) + ". " - - if class_identifier not in self.is12_utils.reference_class_descriptors and \ - not self.is12_utils.is_non_standard_class(descriptor['classId']): - # Not a standard or non-standard class - self.organization_metadata["error"] = True - self.organization_metadata["error_msg"] = str(descriptor['role']) + ': ' \ - + "Non-standard class id does not contain authority key: " \ - + str(descriptor['classId']) + ". " - - def check_device_model(self, test): - if not self.device_model_metadata["checked"]: - self.is12_utils.open_ncp_websocket(test) - class_manager = self.is12_utils.get_class_manager(test) - device_model = self.is12_utils.query_device_model(test) - - self.check_block(test, - device_model, - class_manager.class_descriptors) - - self.device_model_metadata["checked"] = True - return - - def test_05(self, test): - """Device Model: Device Model is correct according to classes and datatypes advertised by Class Manager""" - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Managers.html - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Workers.html - - self.check_device_model(test) - - if self.device_model_metadata["error"]: - return test.FAIL(self.device_model_metadata["error_msg"]) - - if not self.device_model_metadata["checked"]: - return test.UNCLEAR("Unable to check Device Model.") - - return test.PASS() - - def test_06(self, test): - """Device Model: roles are unique within a containing Block""" - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/NcObject.html - - try: - self.check_device_model(test) - except NMOSTestException as e: - # Couldn't validate model so can't perform test - return test.FAIL(e.args[0].detail, e.args[0].link) - - if self.unique_roles_error: - return test.FAIL("Roles must be unique. ", - "https://specs.amwa.tv/ms-05-02/branches/{}" - "/docs/NcObject.html" - .format(self.apis[MS05_API_KEY]["spec_branch"])) - - if not self.device_model_metadata["checked"]: - return test.UNCLEAR("Unable to check Device Model.") - - return test.PASS() - - def test_07(self, test): - """Device Model: oids are globally unique""" - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/NcObject.html - - try: - self.check_device_model(test) - except NMOSTestException as e: - # Couldn't validate model so can't perform test - return test.FAIL(e.args[0].detail, e.args[0].link) - - if self.unique_oids_error: - return test.FAIL("Oids must be unique. ", - "https://specs.amwa.tv/ms-05-02/branches/{}" - "/docs/NcObject.html" - .format(self.apis[MS05_API_KEY]["spec_branch"])) - - if not self.device_model_metadata["checked"]: - return test.UNCLEAR("Unable to check Device Model.") - - return test.PASS() - - def test_08(self, test): - """Device Model: non-standard classes contain an authority key""" - # For organizations which own a unique CID or OUI the authority key MUST be the organization - # identifier as an integer which MUST be negated. - # For organizations which do not own a unique CID or OUI the authority key MUST be 0 - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Framework.html#ncclassid - - try: - self.check_device_model(test) - except NMOSTestException as e: - # Couldn't validate model so can't perform test - return test.FAIL(e.args[0].detail, e.args[0].link) - - if self.organization_metadata["error"]: - return test.FAIL(self.organization_metadata["error_msg"], - "https://specs.amwa.tv/ms-05-02/branches/{}" - "/docs/Framework.html#ncclassid" - .format(self.apis[MS05_API_KEY]["spec_branch"])) - - if not self.device_model_metadata["checked"]: - return test.UNCLEAR("Unable to check Device Model.") - - if not self.organization_metadata["checked"]: - return test.UNCLEAR("No non-standard classes found.") - - return test.PASS() - - def test_09(self, test): - """Device Model: touchpoint datatypes are correct""" - # For general NMOS contexts (IS-04, IS-05 and IS-07) the NcTouchpointNmos datatype MUST be used - # which has a resource of type NcTouchpointResourceNmos. - # For IS-08 Audio Channel Mapping the NcTouchpointResourceNmosChannelMapping datatype MUST be used - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/NcObject.html#touchpoints - - try: - self.check_device_model(test) - except NMOSTestException as e: - # Couldn't validate model so can't perform test - return test.FAIL(e.args[0].detail, e.args[0].link) - - if self.touchpoints_metadata["error"]: - return test.FAIL(self.touchpoints_metadata["error_msg"], - "https://specs.amwa.tv/ms-05-02/branches/{}" - "/docs/NcObject.html#touchpoints" - .format(self.apis[MS05_API_KEY]["spec_branch"])) - - if not self.device_model_metadata["checked"]: - return test.UNCLEAR("Unable to check Device Model.") - - if not self.touchpoints_metadata["checked"]: - return test.UNCLEAR("No Touchpoints found.") - return test.PASS() - - def test_10(self, test): - """Device Model: deprecated properties are indicated""" - # Getting deprecated properties MUST return a PropertyDeprecated status - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Framework.html#ncmethodstatus - - try: - self.check_device_model(test) - except NMOSTestException as e: - # Couldn't validate model so can't perform test - return test.FAIL(e.args[0].detail, e.args[0].link) - - if self.deprecated_property_metadata["error"]: - return test.FAIL(self.deprecated_property_metadata["error_msg"], - "https://specs.amwa.tv/ms-05-02/branches/{}" - "/docs/Framework.html#ncmethodstatus" - .format(self.apis[MS05_API_KEY]["spec_branch"])) - - if not self.device_model_metadata["checked"]: - return test.UNCLEAR("Unable to check Device Model.") - - if not self.deprecated_property_metadata["checked"]: - return test.UNCLEAR("No deprecated properties found.") - return test.PASS() - - def test_11(self, test): - """Managers: managers are members of the Root Block""" - # All managers MUST always exist as members in the Root Block and have a fixed role. - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Managers.html - - try: - self.check_device_model(test) - except NMOSTestException as e: - # Couldn't validate model so can't perform test - return test.FAIL(e.args[0].detail, e.args[0].link) - - if self.managers_members_root_block_error: - return test.FAIL("Managers must be members of Root Block. ", - "https://specs.amwa.tv/ms-05-02/branches/{}" - "/docs/Managers.html" - .format(self.apis[MS05_API_KEY]["spec_branch"])) - - if not self.device_model_metadata["checked"]: - return test.UNCLEAR("Unable to check Device Model.") - - return test.PASS() - - def test_12(self, test): - """Managers: managers are singletons""" - # Managers are singleton (MUST only be instantiated once) classes. - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Managers.html - - try: - self.check_device_model(test) - except NMOSTestException as e: - # Couldn't validate model so can't perform test - return test.FAIL(e.args[0].detail, e.args[0].link) - - if self.managers_are_singletons_error: - return test.FAIL("Managers must be singleton classes. ", - "https://specs.amwa.tv/ms-05-02/branches/{}" - "/docs/Managers.html" - .format(self.apis[MS05_API_KEY]["spec_branch"])) - - if not self.device_model_metadata["checked"]: - return test.UNCLEAR("Unable to check Device Model.") - - return test.PASS() - - def test_13(self, test): - """Managers: Class Manager exists with correct role""" - # Class manager exists in root - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Managers.html - - spec_link = "https://specs.amwa.tv/ms-05-02/branches/{}/docs/Managers.html"\ - .format(self.apis[CONTROL_API_KEY]["spec_branch"]) - - class_manager = self.is12_utils.get_class_manager(test) - - class_id_str = ".".join(map(str, StandardClassIds.NCCLASSMANAGER.value)) - class_descriptor = self.is12_utils.reference_class_descriptors[class_id_str] - - if class_manager.role != class_descriptor["fixedRole"]: - return test.FAIL("Class Manager MUST have a role of ClassManager.", spec_link) - - return test.PASS() - - def test_14(self, test): - """Managers: Device Manager exists with correct Role""" - # A minimal device implementation MUST have a device manager in the Root Block. - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Managers.html - - spec_link = "https://specs.amwa.tv/ms-05-02/branches/{}/docs/Managers.html"\ - .format(self.apis[CONTROL_API_KEY]["spec_branch"]) - - device_manager = self.is12_utils.get_device_manager(test) - - class_id_str = ".".join(map(str, StandardClassIds.NCDEVICEMANAGER.value)) - class_descriptor = self.is12_utils.reference_class_descriptors[class_id_str] - - if device_manager.role != class_descriptor["fixedRole"]: - return test.FAIL("Device Manager MUST have a role of DeviceManager.", spec_link) - - # Check MS-05-02 Version - property_id = NcDeviceManagerProperties.NCVERSION.value - - version = self.is12_utils.get_property_value(test, device_manager.oid, property_id) - - if self.is12_utils.compare_api_version(version, self.apis[MS05_API_KEY]["version"]): - return test.FAIL("Unexpected version. Expected: " - + self.apis[MS05_API_KEY]["version"] - + ". Actual: " + str(version)) - return test.PASS() - - def test_15(self, test): - """Class Manager: GetControlClass method is correct""" - # Where the functionality of a device uses control classes and datatypes listed in this - # specification it MUST comply with the model definitions published - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Framework.html#ncclassmanager - - class_manager = self.is12_utils.get_class_manager(test) - - for _, class_descriptor in class_manager.class_descriptors.items(): - for include_inherited in [False, True]: - actual_descriptor = self.is12_utils.get_control_class(test, - class_manager.oid, - class_descriptor["classId"], - include_inherited) - expected_descriptor = class_manager.get_control_class(class_descriptor["classId"], - include_inherited) - self.is12_utils.validate_descriptor( - test, - expected_descriptor, - actual_descriptor, - context=str(class_descriptor["classId"]) + ": ") - - return test.PASS() - - def test_16(self, test): - """Class Manager: GetDatatype method is correct""" - # Where the functionality of a device uses control classes and datatypes listed in this - # specification it MUST comply with the model definitions published - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Framework.html#ncclassmanager - - class_manager = self.is12_utils.get_class_manager(test) - - for _, datatype_descriptor in class_manager.datatype_descriptors.items(): - for include_inherited in [False, True]: - actual_descriptor = self.is12_utils.get_datatype(test, - class_manager.oid, - datatype_descriptor["name"], - include_inherited) - expected_descriptor = class_manager.get_datatype(datatype_descriptor["name"], - include_inherited) - self.is12_utils.validate_descriptor( - test, - expected_descriptor, - actual_descriptor, - context=datatype_descriptor["name"] + ": ") - - return test.PASS() - - def test_17(self, test): - """NcObject: Get and Set methods are correct""" - # Generic getter and setter. The value of any property of a control class MUST be retrievable - # using the Get method. - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/NcObject.html#generic-getter-and-setter - - link = "https://specs.amwa.tv/ms-05-02/branches/{}" \ - "/docs/NcObject.html#generic-getter-and-setter" \ - .format(self.apis[MS05_API_KEY]["spec_branch"]) - - # Attempt to set labels - self.is12_utils.open_ncp_websocket(test) - - property_id = NcObjectProperties.USER_LABEL.value - - old_user_label = self.is12_utils.get_property_value(test, self.is12_utils.ROOT_BLOCK_OID, property_id) - - # Set user label - new_user_label = "NMOS Testing Tool" - - self.is12_utils.set_property(test, self.is12_utils.ROOT_BLOCK_OID, property_id, new_user_label) - - # Check user label - label = self.is12_utils.get_property_value(test, self.is12_utils.ROOT_BLOCK_OID, property_id) - if label != new_user_label: - if label == old_user_label: - return test.FAIL("Unable to set user label", link) - else: - return test.FAIL("Unexpected user label: " + str(label), link) - - # Reset user label - self.is12_utils.set_property(test, self.is12_utils.ROOT_BLOCK_OID, property_id, old_user_label) - - # Check user label - label = self.is12_utils.get_property_value(test, self.is12_utils.ROOT_BLOCK_OID, property_id) - if label != old_user_label: - if label == new_user_label: - return test.FAIL("Unable to set user label", link) - else: - return test.FAIL("Unexpected user label: " + str(label), link) - - return test.PASS() - - def test_18(self, test): - """NcObject: GetSequenceItem method is correct""" - # Where the functionality of a device uses control classes and datatypes listed in this - # specification it MUST comply with the model definitions published - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Framework.html#ncobject - - try: - self.check_device_model(test) - except NMOSTestException as e: - # Couldn't validate model so can't perform test - return test.FAIL(e.args[0].detail, e.args[0].link) - - if self.get_sequence_item_metadata["error"]: - return test.FAIL(self.get_sequence_item_metadata["error_msg"]) - - if not self.get_sequence_item_metadata["checked"]: - return test.UNCLEAR("GetSequenceItem not tested.") - - return test.PASS() - - def test_19(self, test): - """NcObject: GetSequenceLength method is correct""" - # Where the functionality of a device uses control classes and datatypes listed in this - # specification it MUST comply with the model definitions published - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Framework.html#ncobject - - try: - self.check_device_model(test) - except NMOSTestException as e: - # Couldn't validate model so can't perform test - return test.FAIL(e.args[0].detail, e.args[0].link) - - if self.get_sequence_length_metadata["error"]: - return test.FAIL(self.get_sequence_length_metadata["error_msg"]) - - if not self.get_sequence_length_metadata["checked"]: - return test.UNCLEAR("GetSequenceItem not tested.") - - return test.PASS() - - def do_get_member_descriptors_test(self, test, block, context=""): - # Recurse through the child blocks - for child_object in block.child_objects: - if type(child_object) is NcBlock: - self.do_get_member_descriptors_test(test, child_object, context + block.role + ": ") - - search_conditions = [{"recurse": True}, {"recurse": False}] - - for search_condition in search_conditions: - expected_members = block.get_member_descriptors(search_condition["recurse"]) - - queried_members = self.is12_utils.get_member_descriptors(test, block.oid, search_condition["recurse"]) - - if not isinstance(queried_members, list): - raise NMOSTestException(test.FAIL(context - + block.role - + ": Did not return an array of results.")) - - if len(queried_members) != len(expected_members): - raise NMOSTestException(test.FAIL(context - + block.role - + ": Unexpected number of block members found. Expected: " - + str(len(expected_members)) + ", Actual: " - + str(len(queried_members)))) - - expected_members_oids = [m["oid"] for m in expected_members] - - for queried_member in queried_members: - self.is12_utils.validate_reference_datatype_schema( - test, - queried_member, - "NcBlockMemberDescriptor", - context=context - + block.role - + ": NcBlockMemberDescriptor: ") - - if queried_member["oid"] not in expected_members_oids: - raise NMOSTestException(test.FAIL(context - + block.role - + ": Unsuccessful attempt to get member descriptors.")) - - def test_20(self, test): - """NcBlock: GetMemberDescriptors method is correct""" - # Where the functionality of a device uses control classes and datatypes listed in this - # specification it MUST comply with the model definitions published - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Framework.html#ncblock - - device_model = self.is12_utils.query_device_model(test) - - self.do_get_member_descriptors_test(test, device_model) - - return test.PASS() - - def do_find_member_by_path_test(self, test, block, context=""): - # Recurse through the child blocks - for child_object in block.child_objects: - if type(child_object) is NcBlock: - self.do_find_member_by_path_test(test, child_object, context + block.role + ": ") - - # Get ground truth role paths - role_paths = block.get_role_paths() - - for role_path in role_paths: - # Get ground truth data from local device model object tree - expected_member = block.find_members_by_path(role_path) - - queried_members = self.is12_utils.find_members_by_path(test, block.oid, role_path) - - if not isinstance(queried_members, list): - raise NMOSTestException(test.FAIL(context - + block.role - + ": Did not return an array of results for query: " - + str(role_path))) - - for queried_member in queried_members: - self.is12_utils.validate_reference_datatype_schema( - test, - queried_member, - "NcBlockMemberDescriptor", - context=context - + block.role - + ": NcBlockMemberDescriptor: ") - - if len(queried_members) != 1: - raise NMOSTestException(test.FAIL(context - + block.role - + ": Incorrect member found by role path: " + str(role_path))) - - queried_member_oids = [m['oid'] for m in queried_members] - - if expected_member.oid not in queried_member_oids: - raise NMOSTestException(test.FAIL(context - + block.role - + ": Unsuccessful attempt to find member by role path: " - + str(role_path))) - - def test_21(self, test): - """NcBlock: FindMemberByPath method is correct""" - # Where the functionality of a device uses control classes and datatypes listed in this - # specification it MUST comply with the model definitions published - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Framework.html#ncblock - - device_model = self.is12_utils.query_device_model(test) - - # Recursively check each block in Device Model - self.do_find_member_by_path_test(test, device_model) - - return test.PASS() - - def do_find_member_by_role_test(self, test, block, context=""): - # Recurse through the child blocks - for child_object in block.child_objects: - if type(child_object) is NcBlock: - self.do_find_member_by_role_test(test, child_object, context + block.role + ": ") - - role_paths = IS12Utils.sampled_list(block.get_role_paths()) - # Generate every combination of case_sensitive, match_whole_string and recurse - truth_table = IS12Utils.sampled_list(list(product([False, True], repeat=3))) - search_conditions = [] - for state in truth_table: - search_conditions += [{"case_sensitive": state[0], "match_whole_string": state[1], "recurse": state[2]}] - - for role_path in role_paths: - role = role_path[-1] - # Case sensitive role, case insensitive role, CS role substring and CI role substring - query_strings = [role, role.upper(), role[-4:], role[-4:].upper()] - - for condition in search_conditions: - for query_string in query_strings: - # Get ground truth result - expected_results = \ - block.find_members_by_role(query_string, - case_sensitive=condition["case_sensitive"], - match_whole_string=condition["match_whole_string"], - recurse=condition["recurse"]) - actual_results = \ - self.is12_utils.find_members_by_role(test, - block.oid, - query_string, - case_sensitive=condition["case_sensitive"], - match_whole_string=condition["match_whole_string"], - recurse=condition["recurse"]) - - expected_results_oids = [m.oid for m in expected_results] - - if len(actual_results) != len(expected_results): - raise NMOSTestException(test.FAIL(context - + block.role - + ": Expected " - + str(len(expected_results)) - + ", but got " - + str(len(actual_results)) - + " when searching with query=" + str(query_string) - + ", case sensitive=" + str(condition["case_sensitive"]) - + ", match whole string=" - + str(condition["match_whole_string"]) - + ", recurse=" + str(condition["recurse"]))) - - for actual_result in actual_results: - if actual_result["oid"] not in expected_results_oids: - raise NMOSTestException(test.FAIL(context - + block.role - + ": Unexpected search result. " - + str(actual_result) - + " when searching with query=" + str(query_string) - + ", case sensitive=" + str(condition["case_sensitive"]) - + ", match whole string=" - + str(condition["match_whole_string"]) - + ", recurse=" + str(condition["recurse"]))) - - def test_22(self, test): - """NcBlock: FindMembersByRole method is correct""" - # Where the functionality of a device uses control classes and datatypes listed in this - # specification it MUST comply with the model definitions published - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Framework.html#ncblock - - device_model = self.is12_utils.query_device_model(test) - - # Recursively check each block in Device Model - self.do_find_member_by_role_test(test, device_model) - - return test.PASS() - - def do_find_members_by_class_id_test(self, test, block, context=""): - # Recurse through the child blocks - for child_object in block.child_objects: - if type(child_object) is NcBlock: - self.do_find_members_by_class_id_test(test, child_object, context + block.role + ": ") - - class_ids = [e.value for e in StandardClassIds] - - truth_table = IS12Utils.sampled_list(list(product([False, True], repeat=2))) - search_conditions = [] - for state in truth_table: - search_conditions += [{"include_derived": state[0], "recurse": state[1]}] - - for class_id in class_ids: - for condition in search_conditions: - # Recursively check each block in Device Model - expected_results = block.find_members_by_class_id(class_id, - condition["include_derived"], - condition["recurse"]) - - actual_results = self.is12_utils.find_members_by_class_id(test, - block.oid, - class_id, - condition["include_derived"], - condition["recurse"]) - - expected_results_oids = [m.oid for m in expected_results] - - if len(actual_results) != len(expected_results): - raise NMOSTestException(test.FAIL(context - + block.role - + ": Expected " - + str(len(expected_results)) - + ", but got " - + str(len(actual_results)) - + " when searching with class id=" + str(class_id) - + ", include derived=" + str(condition["include_derived"]) - + ", recurse=" + str(condition["recurse"]))) - - for actual_result in actual_results: - if actual_result["oid"] not in expected_results_oids: - raise NMOSTestException(test.FAIL(context - + block.role - + ": Unexpected search result. " + str(actual_result) - + " when searching with class id=" + str(class_id) - + ", include derived=" + str(condition["include_derived"]) - + ", recurse=" + str(condition["recurse"]))) - - def test_23(self, test): - """NcBlock: FindMembersByClassId method is correct""" - # Where the functionality of a device uses control classes and datatypes listed in this - # specification it MUST comply with the model definitions published - # https://specs.amwa.tv/ms-05-02/releases/v1.0.0/docs/Framework.html#ncblock - - device_model = self.is12_utils.query_device_model(test) - - self.do_find_members_by_class_id_test(test, device_model) - - return test.PASS() - def do_error_test(self, test, command_json, expected_status=None): """Execute command with expected error status.""" # when expected_status = None checking of the status code is skipped @@ -1250,9 +287,9 @@ def test_32(self, test): self.is12_utils.open_ncp_websocket(test) - length = self.is12_utils.get_sequence_length(test, - self.is12_utils.ROOT_BLOCK_OID, - NcBlockProperties.MEMBERS.value) + length = self.is12_utils.get_sequence_length_value(test, + NcBlockProperties.MEMBERS.value, + oid=self.is12_utils.ROOT_BLOCK_OID) out_of_bounds_index = length + 10 command_json = \ @@ -1287,7 +324,7 @@ def test_33(self, test): for oid in oids.keys(): new_user_label = "NMOS Testing Tool " + str(oid) - old_user_label = self.is12_utils.get_property_value(test, oid, NcObjectProperties.USER_LABEL.value) + old_user_label = self.is12_utils.get_property_value(test, NcObjectProperties.USER_LABEL.value, oid=oid) context = "oid: " + str(oid) + ", " @@ -1295,7 +332,7 @@ def test_33(self, test): for label in [new_user_label, old_user_label]: # Set property and log notificaiton self.is12_utils.start_logging_notifications() - self.is12_utils.set_property(test, oid, NcObjectProperties.USER_LABEL.value, label) + self.is12_utils.set_property(test, NcObjectProperties.USER_LABEL.value, label, oid=oid) self.is12_utils.stop_logging_notifications() for notification in self.is12_utils.get_notifications(): @@ -1579,7 +616,8 @@ def _check_constraints(self, test, block, context=""): class_manager = self.is12_utils.get_class_manager(test) - block_member_descriptors = self.is12_utils.get_member_descriptors(test, block.oid, recurse=False) + block_member_descriptors = self.is12_utils.get_member_descriptors(test, recurse=False, + oid=block.oid, role_path=block.role_path) for descriptor in block_member_descriptors: class_descriptor = self.is12_utils.get_control_class(test, @@ -1589,11 +627,14 @@ def _check_constraints(self, test, block, context=""): # Get runtime property constraints # will set error on device_model_metadata on failure + role_path = block.role_path.copy() + role_path.append(descriptor['role']) object_runtime_constraints = \ self.get_property_value(test, - descriptor['oid'], NcObjectProperties.RUNTIME_PROPERTY_CONSTRAINTS.value, - context) + context, + oid=descriptor['oid'], + role_path=role_path) for class_property in class_descriptor.get('properties'): if class_property['isReadOnly']: diff --git a/nmostesting/suites/IS1202Test.py b/nmostesting/suites/IS1202Test.py index 9550211e..19355782 100644 --- a/nmostesting/suites/IS1202Test.py +++ b/nmostesting/suites/IS1202Test.py @@ -21,8 +21,8 @@ from ..Config import IS12_INTERACTIVE_TESTING from ..GenericTest import NMOSTestException from ..ControllerTest import ControllerTest, TestingFacadeException -from ..IS12Utils import IS12Utils, NcDatatypeType, \ - NcObjectProperties, NcBlock +from ..IS12Utils import IS12Utils +from ..MS05Utils import NcDatatypeType, NcObjectProperties, NcBlock NODE_API_KEY = "node" CONTROL_API_KEY = "ncp" @@ -37,7 +37,7 @@ def __init__(self, apis, **kwargs): self.node_url = self.apis[NODE_API_KEY]["url"] self.ncp_url = self.apis[CONTROL_API_KEY]["url"] self.is12_utils = IS12Utils(apis) - self.is12_utils.load_reference_resources(CONTROL_API_KEY) + self.is12_utils.load_reference_resources() self.device_model = None self.constraint_error = False self.constraint_error_msg = "" @@ -114,7 +114,7 @@ def create_ncp_socket(self, test): def get_property_value(self, test, oid, property_id, context): try: - return self.is12_utils.get_property_value(test, oid, property_id) + return self.is12_utils.get_property_value(test, property_id, oid=oid) except NMOSTestException as e: self.device_model_metadata["error"] = True self.device_model_metadata["error_msg"] += context \ @@ -148,7 +148,7 @@ def _get_properties(self, test, block, get_constraints=True, get_sequences=False class_manager = self.is12_utils.get_class_manager(test) - block_member_descriptors = self.is12_utils.get_member_descriptors(test, block.oid, recurse=False) + block_member_descriptors = self.is12_utils.get_member_descriptors(test, recurse=False, oid=block.oid) # Note that the userLabel of the block may also be changed, and therefore might be # subject to runtime constraints constraints @@ -162,8 +162,8 @@ def _get_properties(self, test, block, get_constraints=True, get_sequences=False # Get runtime property constraints object_runtime_constraints = \ self.is12_utils.get_property_value(test, - descriptor['oid'], - NcObjectProperties.RUNTIME_PROPERTY_CONSTRAINTS.value) + NcObjectProperties.RUNTIME_PROPERTY_CONSTRAINTS.value, + oid=descriptor['oid']) for class_property in class_descriptor.get('properties'): if get_readonly != class_property['isReadOnly']: @@ -202,7 +202,7 @@ def _get_methods(self, test, block, context=""): class_manager = self.is12_utils.get_class_manager(test) - block_member_descriptors = self.is12_utils.get_member_descriptors(test, block.oid, recurse=False) + block_member_descriptors = self.is12_utils.get_member_descriptors(test, recurse=False, oid=block.oid) # We're only testing the methods from non-standard classes, as the standard methods are already tested elsewhere non_standard_member_descriptors = [d for d in block_member_descriptors @@ -242,14 +242,14 @@ def _do_check(check_function): pass def _do_set_sequence(): - index = self.is12_utils.get_sequence_length(test, - constrained_property['oid'], - constrained_property['property_id']) + index = self.is12_utils.get_sequence_length_value(test, + constrained_property['property_id'], + oid=constrained_property['oid']) self.is12_utils.set_sequence_item(test, - constrained_property['oid'], constrained_property['property_id'], index - 1, - value) + value, + oid=constrained_property['oid']) if constrained_property.get("is_sequence"): _do_check(lambda: self.is12_utils.add_sequence_item(test, @@ -259,9 +259,9 @@ def _do_set_sequence(): _do_check(_do_set_sequence) else: _do_check(lambda: self.is12_utils.set_property(test, - constrained_property['oid'], constrained_property['property_id'], - value)) + value, + oid=constrained_property['oid'])) def _check_parameter_constraints_number(self, test, constrained_property): constraints = constrained_property.get('constraints') @@ -276,21 +276,22 @@ def _check_parameter_constraints_number(self, test, constrained_property): # Expect this to work OK if constrained_property.get("is_sequence"): - index = self.is12_utils.get_sequence_length(test, - constrained_property['oid'], - constrained_property['property_id']) + index = self.is12_utils.get_sequence_length_value(test, + constrained_property['property_id'], + constrained_property['oid']) self.is12_utils.add_sequence_item(test, constrained_property['oid'], constrained_property['property_id'], new_value) - self.is12_utils.set_sequence_item(test, constrained_property['oid'], + self.is12_utils.set_sequence_item(test, constrained_property['property_id'], - index, new_value) + index, new_value, + oid=constrained_property['oid']) else: self.is12_utils.set_property(test, - constrained_property['oid'], constrained_property['property_id'], - new_value) + new_value, + oid=constrained_property['oid']) # Attempt to set to an "illegal" value if constraints.get("minimum") is not None: @@ -325,23 +326,23 @@ def _check_parameter_constraints_string(self, test, constrained_property): # Expect this to work OK if constrained_property.get("is_sequence"): - index = self.is12_utils.get_sequence_length(test, - constrained_property['oid'], - constrained_property['property_id']) + index = self.is12_utils.get_sequence_length_value(test, + constrained_property['property_id'], + oid=constrained_property['oid']) self.is12_utils.add_sequence_item(test, constrained_property['oid'], constrained_property['property_id'], new_value) self.is12_utils.set_sequence_item(test, - constrained_property['oid'], constrained_property['property_id'], index, - new_value) + new_value, + oid=constrained_property['oid']) else: self.is12_utils.set_property(test, - constrained_property['oid'], constrained_property['property_id'], - new_value) + new_value, + oid=constrained_property['oid']) if constraints.get("pattern"): # Possible negative example strings @@ -416,8 +417,8 @@ def _do_check_property_test(self, test, question, get_constraints=False, get_seq constraint = constrained_property.get('constraints') original_value = self.is12_utils.get_property_value(test, - constrained_property['oid'], - constrained_property['property_id']) + constrained_property['property_id'], + oid=constrained_property['oid']) except NMOSTestException as e: return test.FAIL(constrained_property.get("name") @@ -441,9 +442,9 @@ def _do_check_property_test(self, test, question, get_constraints=False, get_seq try: # Reset to original value self.is12_utils.set_property(test, - constrained_property['oid'], constrained_property['property_id'], - original_value) + original_value, + oid=constrained_property['oid']) except NMOSTestException as e: return test.FAIL(constrained_property.get("name") + ": error restoring original value of property: " @@ -598,8 +599,8 @@ def _do_check_readonly_properties(self, test, question, get_sequences=False): # Cache original property value try: original_value = self.is12_utils.get_property_value(test, - readonly_property['oid'], - readonly_property['property_id']) + readonly_property['property_id'], + oid=readonly_property['oid']) except NMOSTestException as e: return test.FAIL(readonly_property.get("name") @@ -609,9 +610,9 @@ def _do_check_readonly_properties(self, test, question, get_sequences=False): try: # Try setting this value self.is12_utils.set_property(test, - readonly_property['oid'], readonly_property['property_id'], - original_value) + original_value, + oid=readonly_property['oid']) # if it gets this far it's failed return test.FAIL(readonly_property.get("name") + ": read only property is writable") @@ -722,12 +723,12 @@ def check_add_sequence_item(self, test, oid, property_id, property_name, sequenc try: self.add_sequence_item_metadata["checked"] = True # Add a value to the end of the sequence - new_item = self.is12_utils.get_sequence_item(test, oid, property_id, index=0) + new_item = self.is12_utils.get_sequence_item_value(test, property_id, index=0, oid=oid) self.is12_utils.add_sequence_item(test, oid, property_id, new_item) # check the value - value = self.is12_utils.get_sequence_item(test, oid, property_id, index=sequence_length) + value = self.is12_utils.get_sequence_item_value(test, property_id, index=sequence_length, oid=oid) if value != new_item: self.add_sequence_item_metadata["error"] = True self.add_sequence_item_metadata["error_msg"] += \ @@ -743,13 +744,14 @@ def check_add_sequence_item(self, test, oid, property_id, property_name, sequenc def check_set_sequence_item(self, test, oid, property_id, property_name, sequence_length, context=""): try: self.set_sequence_item_metadata["checked"] = True - new_value = self.is12_utils.get_sequence_item(test, oid, property_id, index=sequence_length - 1) + new_value = self.is12_utils.get_sequence_item_value(test, property_id, index=sequence_length - 1, oid=oid) # set to another value - self.is12_utils.set_sequence_item(test, oid, property_id, index=sequence_length, value=new_value) + self.is12_utils.set_sequence_item(test, property_id, index=sequence_length, value=new_value, + oid=oid) # check the value - value = self.is12_utils.get_sequence_item(test, oid, property_id, index=sequence_length) + value = self.is12_utils.get_sequence_item_value(test, property_id, index=sequence_length, oid=oid) if value != new_value: self.set_sequence_item_metadata["error"] = True self.set_sequence_item_metadata["error_msg"] += \ @@ -776,7 +778,7 @@ def check_remove_sequence_item(self, test, oid, property_id, property_name, sequ def check_sequence_methods(self, test, oid, property_id, property_name, context=""): """Check that sequence manipulation methods work correctly""" - response = self.is12_utils.get_property_value(test, oid, property_id) + response = self.is12_utils.get_property_value(test, property_id, oid=oid) if response is None or not isinstance(response, list) or len(response) == 0: # Hmmm, these tests depend on sequences already having some data in them. @@ -789,17 +791,17 @@ def check_sequence_methods(self, test, oid, property_id, property_name, context= if not self.check_add_sequence_item(test, oid, property_id, property_name, sequence_length, context=context): return - if sequence_length + 1 != self.is12_utils.get_sequence_length(test, oid, property_id): + if sequence_length + 1 != self.is12_utils.get_sequence_length_value(test, property_id, oid=oid): self.add_sequence_item_metadata["error"] = True self.add_sequence_item_metadata["error_msg"] = property_name + \ ": add_sequence_item resulted in unexpected sequence length." self.check_set_sequence_item(test, oid, property_id, property_name, sequence_length, context=context) - if sequence_length + 1 != self.is12_utils.get_sequence_length(test, oid, property_id): + if sequence_length + 1 != self.is12_utils.get_sequence_length_value(test, property_id, oid=oid): self.set_sequence_item_metadata["error"] = True self.set_sequence_item_metadata["error_msg"] = property_name + \ ": set_sequence_item resulted in unexpected sequence length." self.check_remove_sequence_item(test, oid, property_id, property_name, sequence_length, context) - if sequence_length != self.is12_utils.get_sequence_length(test, oid, property_id): + if sequence_length != self.is12_utils.get_sequence_length_value(test, property_id, oid=oid): self.remove_sequence_item_metadata["error"] = True self.remove_sequence_item_metadata["error_msg"] = property_name + \ ": remove_sequence_item resulted in unexpected sequence length." diff --git a/nmostesting/suites/IS1401Test.py b/nmostesting/suites/IS1401Test.py index 5036c12d..1b9e74b8 100644 --- a/nmostesting/suites/IS1401Test.py +++ b/nmostesting/suites/IS1401Test.py @@ -12,43 +12,24 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ..GenericTest import GenericTest, NMOSTestException +# from ..GenericTest import NMOSTestException from ..IS14Utils import IS14Utils from .MS0501Test import MS0501Test CONFIGURATION_API_KEY = "configuration" -class IS1401Test(GenericTest): +class IS1401Test(MS0501Test): """ Runs IS-04-01-Test """ - def __init__(self, apis, auths, **kwargs): - GenericTest.__init__(self, apis, auths=auths, **kwargs) - self.is14_utils = IS14Utils(apis) - self.is14_utils.load_reference_resources(CONFIGURATION_API_KEY) - self.ms0501Test = MS0501Test(apis, self.is14_utils) + def __init__(self, apis, **kwargs): + MS0501Test.__init__(self, apis, **kwargs) + self.set_utils(IS14Utils(apis)) + self.ms05_utils.load_reference_resources() def set_up_tests(self): - pass + super().set_up_tests() def tear_down_tests(self): - pass - - def execute_tests(self, test_names): - """Perform tests defined within this class""" - # Override to allow 'auto' testing of MS-05 types and classes - - for test_name in test_names: - self.execute_test(test_name) - if test_name in ["auto", "all"] and not self.disable_auto: - # Append datatype and class definition auto tests - # Validate all standard datatypes and classes advertised by the Class Manager - try: - self.result += self.ms0501Test.auto_tests() - except NMOSTestException as e: - self.result.append(e.args[0]) - - def test_01(self, test): - """First test""" - return test.UNCLEAR("I D K") + super().tear_down_tests()