@@ -109,7 +109,7 @@ def query_device_model(self, test):
109
109
self.device_model_metadata set on Device Model validation error.
110
110
NMOSTestException raised if unable to query Device Model """
111
111
if not self .device_model :
112
- self .device_model = self ._nc_object_factory (
112
+ self .device_model = self .create_device_model (
113
113
test ,
114
114
StandardClassIds .NCBLOCK .value ,
115
115
self .ROOT_BLOCK_OID ,
@@ -302,33 +302,45 @@ def get_datatype_schema(self, test, type_name):
302
302
303
303
return self .datatype_schemas .get (type_name )
304
304
305
- def validate_reference_datatype_schema (self , test , payload , datatype_name , context = "" ):
305
+ def queried_datatype_schema_validate (self , test , payload , datatype_name , context = "" ):
306
+ """Validate payload against datatype schema queried from Node under Test Class Manager"""
307
+ datatype_schema = self .get_datatype_schema (test , datatype_name )
308
+ self ._validate_schema (test , payload , datatype_schema , f"{ context } { datatype_name } " )
309
+
310
+ def reference_datatype_schema_validate (self , test , payload , datatype_name , context = "" ):
306
311
"""Validate payload against reference datatype schema"""
307
312
context += f"{ datatype_name } : "
308
- self .validate_schema (test , payload , self .reference_datatype_schemas [datatype_name ], context )
313
+ self ._validate_schema (test , payload , self .reference_datatype_schemas [datatype_name ],
314
+ f"{ context } { datatype_name } " )
309
315
310
- def validate_schema (self , test , payload , schema , context = "" ):
311
- """Delegates to validate. Raises NMOSTestExceptions on error"""
316
+ def _validate_schema (self , test , payload , schema , context = "" ):
317
+ """Delegates to jsonschema validate. Raises NMOSTestExceptions on error"""
312
318
if not schema :
313
- raise NMOSTestException (test .FAIL ("Missing schema. Possible unknown type: " + context ))
319
+ raise NMOSTestException (test .FAIL (f "Missing schema. Possible unknown type: { context } " ))
314
320
try :
315
321
# Validate the JSON schema is correct
316
322
checker = FormatChecker (["ipv4" , "ipv6" , "uri" ])
317
323
validate (payload , schema , format_checker = checker )
318
324
except ValidationError as e :
319
- raise NMOSTestException (test .FAIL (context + " Schema validation error: " + e .message ))
325
+ raise NMOSTestException (test .FAIL (f" { context } Schema validation error: { e .message } " ))
320
326
except SchemaError as e :
321
- raise NMOSTestException (test .FAIL (context + " Schema error: " + e .message ))
327
+ raise NMOSTestException (test .FAIL (f" { context } Schema error: { e .message } " ))
322
328
323
329
return
324
330
325
331
def validate_descriptor (self , test , reference , descriptor , context = "" ):
326
332
"""Validate descriptor against reference NcDescriptor. Raises NMOSTestException on error"""
333
+ def sort_key (e ):
334
+ if isinstance (e , NcDescriptor ):
335
+ return e .name
336
+ else :
337
+ return e ["name" ]
338
+
327
339
non_normative_keys = ["description" ]
328
340
329
341
# Compare disctionaries
330
342
if isinstance (reference , dict ):
331
- # Remove the json field if present
343
+ # NcDescriptor objects have a json field that caches the json used to construct it
332
344
reference .pop ("json" , None )
333
345
334
346
reference_keys = set (reference .keys ())
@@ -338,35 +350,32 @@ def validate_descriptor(self, test, reference, descriptor, context=""):
338
350
key_diff = (set (reference_keys ) | set (descriptor_keys )) - (set (reference_keys ) & set (descriptor_keys ))
339
351
if len (key_diff ) > 0 :
340
352
error_description = "Missing keys " if set (key_diff ) <= set (reference_keys ) else "Additional keys "
341
- raise NMOSTestException (test .FAIL (context + error_description + str (key_diff )))
353
+ raise NMOSTestException (test .FAIL (f" { context } { error_description } { str (key_diff )} " ))
342
354
for key in reference_keys :
343
- # Make sure to ignore non normative keys, but not fields that use those keys
344
- if key in non_normative_keys and not isinstance ( reference [ key ], dict ) :
355
+ # Ignore keys that contain non- normative information
356
+ if key in non_normative_keys :
345
357
continue
346
358
self .validate_descriptor (test , reference [key ], descriptor [key ], context = context + key + "->" )
347
- # We can't guarantee ordering of lists, so convert reference and descriptor to dictionaries
359
+ # Compare lists
348
360
elif isinstance (reference , list ):
349
361
if len (reference ) != len (descriptor ):
350
- raise NMOSTestException (test .FAIL (context + " List unexpected length. Expected: "
351
- + str (len (reference )) + " , actual: " + str (len (descriptor ))))
362
+ raise NMOSTestException (test .FAIL (f" { context } List unexpected length. Expected: "
363
+ f" { str (len (reference ))} , actual: { str (len (descriptor ))} " ))
352
364
if len (reference ) > 0 :
365
+ # If comparing lists of objects or dicts then sort by name first.
366
+ # Primitive arrays are unsorted as position is assumed to be important e.g. classId
353
367
if isinstance (reference [0 ], (dict , NcDescriptor )):
354
- # Convert to dict and validate
355
- if isinstance (reference [0 ], NcDescriptor ):
356
- references = {item .name : item .__dict__ for item in reference }
357
- else :
358
- references = {item ["name" ]: item for item in reference }
359
- descriptors = {item ["name" ]: item for item in descriptor }
360
- self .validate_descriptor (test , references , descriptors , context )
361
- else :
362
- for refvalue , value in zip (reference , descriptor ):
363
- self .validate_descriptor (test , refvalue , value , context )
368
+ reference .sort (key = sort_key )
369
+ descriptor .sort (key = sort_key )
370
+ for refvalue , value in zip (reference , descriptor ):
371
+ self .validate_descriptor (test , refvalue , value , context )
364
372
# If the reference is an object then convert to a dict before comparison
365
373
elif isinstance (reference , (NcDescriptor , NcElementId )):
366
374
self .validate_descriptor (test , reference .__dict__ , descriptor , context )
375
+ # Compare primitives and primitive arrays directly
367
376
elif reference != descriptor :
368
- raise NMOSTestException (test .FAIL (context + " Expected value: "
369
- + str (reference ) + " , actual value: " + str (descriptor )))
377
+ raise NMOSTestException (test .FAIL (f" { context } Expected value: "
378
+ f" { str (reference )} , actual value: { str (descriptor )} " ))
370
379
return
371
380
372
381
def _get_class_manager_datatype_descriptors (self , test , class_manager_oid , role_path ):
@@ -376,9 +385,9 @@ def _get_class_manager_datatype_descriptors(self, test, class_manager_oid, role_
376
385
if not response :
377
386
return None
378
387
379
- # Validate descriptors
388
+ # Validate descriptors against schema
380
389
for r in response :
381
- self .validate_reference_datatype_schema (test , r , NcDatatypeDescriptor .__name__ ,
390
+ self .reference_datatype_schema_validate (test , r , NcDatatypeDescriptor .__name__ ,
382
391
"/" .join ([str (r ) for r in role_path ]))
383
392
384
393
# Create NcDescriptor dictionary from response array
@@ -395,16 +404,16 @@ def _get_class_manager_class_descriptors(self, test, class_manager_oid, role_pat
395
404
396
405
# Validate descriptors
397
406
for r in response :
398
- self .validate_reference_datatype_schema (test , r , NcClassDescriptor .__name__ ,
407
+ self .reference_datatype_schema_validate (test , r , NcClassDescriptor .__name__ ,
399
408
"/" .join ([str (r ) for r in role_path ]))
400
409
401
410
# Create NcClassDescriptor dictionary from response array
402
411
def key_lambda (classId ): return "." .join (map (str , classId ))
403
412
descriptors = {key_lambda (r .get ("classId" )): NcClassDescriptor (r ) for r in response }
404
413
return descriptors
405
414
406
- def _nc_object_factory (self , test , class_id , oid , role , _role_path = None ):
407
- """Create NcObject or NcBlock based on class_id """
415
+ def create_device_model (self , test , class_id , oid , role , _role_path = None ):
416
+ """Recursively create Device Model hierarchy """
408
417
# will set self.device_model_error to True if problems encountered
409
418
if _role_path is None :
410
419
role_path = []
@@ -423,26 +432,25 @@ def _nc_object_factory(self, test, class_id, oid, role, _role_path=None):
423
432
oid = oid , role_path = role_path )
424
433
425
434
if member_descriptors is None :
426
- raise NMOSTestException (test .FAIL ("Unable to get members for object: oid={}, role Path={} "
427
- . format ( str (oid ), str (role_path )) ))
435
+ raise NMOSTestException (test .FAIL ("Unable to get members for object: "
436
+ f"oid= { str (oid )} , role Path= { str (role_path )} " ))
428
437
429
438
block_member_descriptors = []
430
439
for m in member_descriptors :
431
- self .validate_reference_datatype_schema (test , m , NcBlockMemberDescriptor .__name__ ,
440
+ self .reference_datatype_schema_validate (test , m , NcBlockMemberDescriptor .__name__ ,
432
441
"/" .join ([str (r ) for r in role_path ]))
433
442
block_member_descriptors .append (NcBlockMemberDescriptor (m ))
434
443
435
444
nc_block = NcBlock (class_id , oid , role , role_path , block_member_descriptors , runtime_constraints )
436
445
437
446
for m in member_descriptors :
438
- child_object = self ._nc_object_factory (test , m ["classId" ], m ["oid" ], m ["role" ], role_path )
447
+ child_object = self .create_device_model (test , m ["classId" ], m ["oid" ], m ["role" ], role_path )
439
448
if child_object :
440
449
nc_block .add_child_object (child_object )
441
450
442
451
return nc_block
443
452
else :
444
- # Check to determine if this is a Class Manager
445
- if len (class_id ) > 2 and class_id [0 ] == 1 and class_id [1 ] == 3 and class_id [2 ] == 2 :
453
+ if self .is_class_manager (class_id ):
446
454
class_descriptors = self ._get_class_manager_class_descriptors (
447
455
test , class_manager_oid = oid , role_path = role_path )
448
456
@@ -460,7 +468,7 @@ def _nc_object_factory(self, test, class_id, oid, role, _role_path=None):
460
468
return NcObject (class_id , oid , role , role_path , runtime_constraints )
461
469
462
470
except NMOSTestException as e :
463
- raise NMOSTestException (test .FAIL ("Error in Device Model " + role + ": " + str (e .args [0 ].detail )))
471
+ raise NMOSTestException (test .FAIL (f "Error in Device Model { role } : { str (e .args [0 ].detail )} " ))
464
472
465
473
def _get_object_by_class_id (self , test , class_id ):
466
474
device_model = self .query_device_model (test )
@@ -516,11 +524,15 @@ def is_non_standard_class(self, class_id):
516
524
return len ([v for v in dropwhile (lambda x : x > 0 , class_id )]) > 1
517
525
518
526
def is_manager (self , class_id ):
519
- """ Check class id to determine if this is a manager """
527
+ """ Check class id to determine if this is a manager class_id """
520
528
return len (class_id ) > 1 and class_id [0 ] == 1 and class_id [1 ] == 3
521
529
530
+ def is_class_manager (self , class_id ):
531
+ """ Check class id to determine is this is a class manager class_id """
532
+ return len (class_id ) > 2 and class_id [0 ] == 1 and class_id [1 ] == 3 and class_id [2 ] == 2
533
+
522
534
def is_block (self , class_id ):
523
- """ Check class id to determine if this is a block """
535
+ """ Check class id to determine if this is a block class_id """
524
536
return len (class_id ) > 1 and class_id [0 ] == 1 and class_id [1 ] == 1
525
537
526
538
def resolve_datatype (self , test , datatype ):
0 commit comments