Skip to content

Commit

Permalink
Add robustness for missing classes and datatype definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathan-r-thorpe committed Nov 13, 2024
1 parent 592b4f6 commit 2cbb4c0
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 39 deletions.
42 changes: 30 additions & 12 deletions nmostesting/MS05Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,9 @@ def get_datatype(self, test, name, include_inherited, **kwargs):
method_result = NcMethodResult.factory(result)

if not isinstance(method_result, NcMethodResultError):
self.reference_datatype_schema_validate(test, method_result.value, NcDatatypeDescriptor.__name__,
self.reference_datatype_schema_validate(test,
method_result.value,
NcDatatypeDescriptor.get_descriptor_type(method_result.value),
role_path=kwargs.get("role_path"))
method_result.value = NcDatatypeDescriptor.factory(method_result.value)

Expand Down Expand Up @@ -333,8 +335,9 @@ def _datatype_descriptor_to_schema(self, descriptor):
else:
json_schema["allOf"] = []
json_schema["allOf"].append({"$ref": f"{descriptor.parentType}.json"})

# Primitive datatype
elif isinstance(descriptor, NcDatatypeDescriptorPrimitive):
if isinstance(descriptor, NcDatatypeDescriptorPrimitive):
json_schema["type"] = self._primitive_to_JSON(descriptor.name)

# Struct datatype
Expand Down Expand Up @@ -503,7 +506,7 @@ def _get_class_manager_datatype_descriptors(self, test, class_manager_oid, role_

# Validate descriptors against schema
for r in response:
self.reference_datatype_schema_validate(test, r, NcDatatypeDescriptor.__name__, role_path)
self.reference_datatype_schema_validate(test, r, NcDatatypeDescriptor.get_descriptor_type(r), role_path)

# Create NcDescriptor dictionary from response array
descriptors = {r["name"]: NcDatatypeDescriptor.factory(r) for r in response}
Expand Down Expand Up @@ -919,16 +922,31 @@ def __init__(self, descriptor_json):
self.type = descriptor_json["type"] # Type: Primitive, Typedef, Struct, Enum
self.constraints = NcParameterConstraints.factory(descriptor_json["constraints"], "datatype") # Optional

@staticmethod
def get_descriptor_type(descriptor_json):
if "fields" in descriptor_json and "parentType" in descriptor_json:
return "NcDatatypeDescriptorStruct"

if "parentType" in descriptor_json and "isSequence" in descriptor_json:
return "NcDatatypeDescriptorTypeDef"

if "items" in descriptor_json:
return "NcDatatypeDescriptorEnum"

return "NcDatatypeDescriptorPrimitive"

@staticmethod
def factory(descriptor_json):
"""Instantiate concrete NcDatatypeDescriptor object"""
if "fields" in descriptor_json and "parentType" in descriptor_json:
descriptor_type = NcDatatypeDescriptor.get_descriptor_type(descriptor_json)

if descriptor_type == "NcDatatypeDescriptorStruct":
return NcDatatypeDescriptorStruct(descriptor_json)

if "parentType" in descriptor_json and "isSequence" in descriptor_json:
if descriptor_type == "NcDatatypeDescriptorTypeDef":
return NcDatatypeDescriptorTypeDef(descriptor_json)

if "items" in descriptor_json:
if descriptor_type == "NcDatatypeDescriptorEnum":
return NcDatatypeDescriptorEnum(descriptor_json)

return NcDatatypeDescriptorPrimitive(descriptor_json)
Expand Down Expand Up @@ -1224,11 +1242,11 @@ def __init__(self, class_id, oid, role, role_path, class_descriptors, datatype_d
self.class_descriptors = class_descriptors
self.datatype_descriptors = datatype_descriptors

def get_control_class(self, class_id, include_inherited):
def get_control_class(self, class_id, include_inherited=True):
class_id_str = ".".join(map(str, class_id))
descriptor = self.class_descriptors[class_id_str]
descriptor = self.class_descriptors.get(class_id_str)

if not include_inherited:
if not include_inherited or not descriptor:
return descriptor

parent_class = class_id[:-1]
Expand All @@ -1246,10 +1264,10 @@ def get_control_class(self, class_id, include_inherited):

return inherited_descriptor

def get_datatype(self, name, include_inherited):
descriptor = self.datatype_descriptors[name]
def get_datatype(self, name, include_inherited=True):
descriptor = self.datatype_descriptors.get(name)

if not include_inherited or descriptor.type != NcDatatypeType.Struct:
if not include_inherited or not descriptor or descriptor.type != NcDatatypeType.Struct:
return descriptor

inherited_descriptor = deepcopy(descriptor)
Expand Down
44 changes: 23 additions & 21 deletions nmostesting/suites/MS0501Test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1146,16 +1146,16 @@ def check_constraint(self, test, constraint, descriptor, test_metadata, role_pat
def do_validate_runtime_constraints_test(self, test, nc_object, class_manager, test_metadata, context=""):
if nc_object.runtime_constraints:
for constraint in nc_object.runtime_constraints:
class_descriptor = \
class_manager.class_descriptors.get(self.ms05_utils.create_class_id_string(nc_object.class_id))
for property_descriptor in class_descriptor.properties:
if property_descriptor.id == constraint.propertyId:
test_metadata.checked = True
self.check_constraint(test,
constraint,
property_descriptor,
test_metadata,
nc_object.role_path)
class_descriptor = class_manager.get_control_class(nc_object.class_id)
if class_descriptor:
for property_descriptor in class_descriptor.properties:
if property_descriptor.id == constraint.propertyId:
test_metadata.checked = True
self.check_constraint(test,
constraint,
property_descriptor,
test_metadata,
nc_object.role_path)

# Recurse through the child blocks
if type(nc_object) is NcBlock:
Expand Down Expand Up @@ -1188,17 +1188,17 @@ def test_ms05_21(self, test):
return test.PASS()

def do_validate_property_constraints_test(self, test, nc_object, class_manager, test_metadata):
class_descriptor = \
class_manager.class_descriptors.get(self.ms05_utils.create_class_id_string(nc_object.class_id))

for property_descriptor in class_descriptor.properties:
if property_descriptor.constraints:
test_metadata.checked = True
self.check_constraint(test,
property_descriptor.constraints,
property_descriptor,
test_metadata,
nc_object.role_path)
class_descriptor = class_manager.get_control_class(nc_object.class_id)

if class_descriptor:
for property_descriptor in class_descriptor.properties:
if property_descriptor.constraints:
test_metadata.checked = True
self.check_constraint(test,
property_descriptor.constraints,
property_descriptor,
test_metadata,
nc_object.role_path)
# Recurse through the child blocks
if type(nc_object) is NcBlock:
for child_object in nc_object.child_objects:
Expand Down Expand Up @@ -1368,6 +1368,8 @@ def _check_constraints(self, test, block, test_metadata):
for child in block.child_objects:
class_descriptor = class_manager.get_control_class(child.class_id, include_inherited=True)

if not class_descriptor:
continue
for property_descriptor in class_descriptor.properties:
if property_descriptor.isReadOnly:
continue
Expand Down
16 changes: 10 additions & 6 deletions nmostesting/suites/MS0502Test.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ def _get_properties(self, test, block, get_constraints=True, get_sequences=False
for child in block.child_objects:
class_descriptor = class_manager.get_control_class(child.class_id, include_inherited=True)

if not class_descriptor:
continue
role_path = self.ms05_utils.create_role_path(block.role_path, child.role)

for property_descriptor in class_descriptor.properties:
Expand Down Expand Up @@ -190,6 +192,8 @@ def _get_methods(self, test, block, get_constraints=False):
for child in block.child_objects:
class_descriptor = class_manager.get_control_class(child.class_id, include_inherited=True)

if not class_descriptor:
continue
# Only test methods on non-standard classes, as the standard classes are already tested elsewhere
if not self.ms05_utils.is_non_standard_class(class_descriptor.classId):
continue
Expand Down Expand Up @@ -431,8 +435,8 @@ def _do_check_property_test(self, test, question, get_constraints=False, get_seq
return test.FAIL(f"{error_msg_base}GetProperty error: {str(method_result.errorMessage)}. "
f"constraints: {str(constraints)}")
if self.ms05_utils.is_error_status(method_result.status):
return test.FAIL(test.FAIL(f"{error_msg_base}GetProperty error: "
"NcMethodResultError MUST be returned on an error."))
return test.FAIL(f"{error_msg_base}GetProperty error: "
"NcMethodResultError MUST be returned on an error.")

original_value = method_result.value
try:
Expand All @@ -455,8 +459,8 @@ def _do_check_property_test(self, test, question, get_constraints=False, get_seq
f"original value: {str(original_value)}, "
f"constraints: {str(constraints)}")
if self.ms05_utils.is_error_status(method_result.status):
return test.FAIL(test.FAIL(f"{error_msg_base}SetProperty error: "
"NcMethodResultError MUST be returned on an error."))
return test.FAIL(f"{error_msg_base}SetProperty error: "
"NcMethodResultError MUST be returned on an error.")

if self.check_property_metadata.error:
# JRT add link to constraints spec
Expand Down Expand Up @@ -562,8 +566,8 @@ def _do_check_readonly_properties(self, test, question, get_sequences=False):
if isinstance(method_result, NcMethodResultError):
return test.FAIL(f"{error_msg_base}GetProperty error:{str(method_result.errorMessage)}")
if self.ms05_utils.is_error_status(method_result.status):
return test.FAIL(test.FAIL(f"{error_msg_base}GetProperty error: "
"NcMethodResultError MUST be returned on an error."))
return test.FAIL(f"{error_msg_base}GetProperty error: "
"NcMethodResultError MUST be returned on an error.")
original_value = method_result.value
# Try setting this value
method_result = self.ms05_utils.set_property(test,
Expand Down

0 comments on commit 2cbb4c0

Please sign in to comment.