20
20
import queue
21
21
from abc import ABC , abstractmethod
22
22
from dataclasses import dataclass , field
23
- from enum import Enum
23
+ from enum import Enum , IntEnum
24
24
25
25
import chip .interaction_model
26
26
import chip .yaml .format_converter as Converter
@@ -39,6 +39,12 @@ class _ActionStatus(Enum):
39
39
ERROR = 'error'
40
40
41
41
42
+ class _TestFabricId (IntEnum ):
43
+ ALPHA = 1 ,
44
+ BETA = 2 ,
45
+ GAMMA = 3
46
+
47
+
42
48
@dataclass
43
49
class _ActionResult :
44
50
status : _ActionStatus
@@ -68,13 +74,18 @@ class _ExecutionContext:
68
74
class BaseAction (ABC ):
69
75
'''Interface for a single YAML action that is to be executed.'''
70
76
71
- def __init__ (self , label ):
77
+ def __init__ (self , label , identity ):
72
78
self ._label = label
79
+ self ._identity = identity
73
80
74
81
@property
75
82
def label (self ):
76
83
return self ._label
77
84
85
+ @property
86
+ def identity (self ):
87
+ return self ._identity
88
+
78
89
@abstractmethod
79
90
def run_action (self , dev_ctrl : ChipDeviceCtrl ) -> _ActionResult :
80
91
pass
@@ -95,9 +106,10 @@ def __init__(self, test_step, cluster: str, context: _ExecutionContext):
95
106
action to perform for this write attribute.
96
107
UnexpectedParsingError: Raised if there is an unexpected parsing error.
97
108
'''
98
- super ().__init__ (test_step .label )
109
+ super ().__init__ (test_step .label , test_step . identity )
99
110
self ._command_name = stringcase .pascalcase (test_step .command )
100
111
self ._cluster = cluster
112
+ self ._interation_timeout_ms = test_step .timed_interaction_timeout_ms
101
113
self ._request_object = None
102
114
self ._expected_response_object = None
103
115
self ._endpoint = test_step .endpoint
@@ -128,8 +140,9 @@ def __init__(self, test_step, cluster: str, context: _ExecutionContext):
128
140
129
141
def run_action (self , dev_ctrl : ChipDeviceCtrl ) -> _ActionResult :
130
142
try :
131
- resp = asyncio .run (dev_ctrl .SendCommand (self ._node_id , self ._endpoint ,
132
- self ._request_object ))
143
+ resp = asyncio .run (dev_ctrl .SendCommand (
144
+ self ._node_id , self ._endpoint , self ._request_object ,
145
+ timedRequestTimeoutMs = self ._interation_timeout_ms ))
133
146
except chip .interaction_model .InteractionModelError as error :
134
147
return _ActionResult (status = _ActionStatus .ERROR , response = error )
135
148
@@ -152,13 +165,17 @@ def __init__(self, test_step, cluster: str, context: _ExecutionContext):
152
165
action to perform for this read attribute.
153
166
UnexpectedParsingError: Raised if there is an unexpected parsing error.
154
167
'''
155
- super ().__init__ (test_step .label )
168
+ super ().__init__ (test_step .label , test_step . identity )
156
169
self ._attribute_name = stringcase .pascalcase (test_step .attribute )
157
170
self ._cluster = cluster
158
171
self ._endpoint = test_step .endpoint
159
172
self ._node_id = test_step .node_id
160
173
self ._cluster_object = None
161
174
self ._request_object = None
175
+ self ._fabric_filtered = True
176
+
177
+ if test_step .fabric_filtered is not None :
178
+ self ._fabric_filtered = test_step .fabric_filtered
162
179
163
180
self ._possibly_unsupported = bool (test_step .optional )
164
181
@@ -185,7 +202,8 @@ def __init__(self, test_step, cluster: str, context: _ExecutionContext):
185
202
def run_action (self , dev_ctrl : ChipDeviceCtrl ) -> _ActionResult :
186
203
try :
187
204
raw_resp = asyncio .run (dev_ctrl .ReadAttribute (self ._node_id ,
188
- [(self ._endpoint , self ._request_object )]))
205
+ [(self ._endpoint , self ._request_object )],
206
+ fabricFiltered = self ._fabric_filtered ))
189
207
except chip .interaction_model .InteractionModelError as error :
190
208
return _ActionResult (status = _ActionStatus .ERROR , response = error )
191
209
@@ -215,7 +233,7 @@ class WaitForCommissioneeAction(BaseAction):
215
233
''' Wait for commissionee action to be executed.'''
216
234
217
235
def __init__ (self , test_step ):
218
- super ().__init__ (test_step .label )
236
+ super ().__init__ (test_step .label , test_step . identity )
219
237
self ._node_id = test_step .node_id
220
238
self ._expire_existing_session = False
221
239
# This is the default when no timeout is provided.
@@ -337,7 +355,7 @@ def __init__(self, test_step, cluster: str, context: _ExecutionContext):
337
355
action to perform for this write attribute.
338
356
UnexpectedParsingError: Raised if there is an unexpected parsing error.
339
357
'''
340
- super ().__init__ (test_step .label )
358
+ super ().__init__ (test_step .label , test_step . identity )
341
359
self ._attribute_name = stringcase .pascalcase (test_step .attribute )
342
360
self ._cluster = cluster
343
361
self ._endpoint = test_step .endpoint
@@ -398,7 +416,7 @@ def __init__(self, test_step, context: _ExecutionContext):
398
416
Raises:
399
417
UnexpectedParsingError: Raised if the expected queue does not exist.
400
418
'''
401
- super ().__init__ (test_step .label )
419
+ super ().__init__ (test_step .label , test_step . identity )
402
420
self ._attribute_name = stringcase .pascalcase (test_step .attribute )
403
421
self ._output_queue = context .subscription_callback_result_queue .get (self ._attribute_name ,
404
422
None )
@@ -417,16 +435,50 @@ def run_action(self, dev_ctrl: ChipDeviceCtrl) -> _ActionResult:
417
435
return item .result
418
436
419
437
438
+ class CommissionerCommandAction (BaseAction ):
439
+ '''Single Commissioner Command action to be executed.'''
440
+
441
+ def __init__ (self , test_step ):
442
+ '''Converts 'test_step' to commissioner command action.
443
+
444
+ Args:
445
+ 'test_step': Step containing information required to run wait for report action.
446
+ Raises:
447
+ UnexpectedParsingError: Raised if the expected queue does not exist.
448
+ '''
449
+ super ().__init__ (test_step .label , test_step .identity )
450
+ if test_step .command != 'PairWithCode' :
451
+ raise UnexpectedParsingError (f'Unexpected CommisionerCommand { test_step .command } ' )
452
+
453
+ args = test_step .arguments ['values' ]
454
+ request_data_as_dict = Converter .convert_list_of_name_value_pair_to_dict (args )
455
+ self ._setup_payload = request_data_as_dict ['payload' ]
456
+ self ._node_id = request_data_as_dict ['nodeId' ]
457
+
458
+ def run_action (self , dev_ctrl : ChipDeviceCtrl ) -> _ActionResult :
459
+ resp = dev_ctrl .CommissionWithCode (self ._setup_payload , self ._node_id )
460
+
461
+ if resp :
462
+ return _ActionResult (status = _ActionStatus .SUCCESS , response = None )
463
+ else :
464
+ return _ActionResult (status = _ActionStatus .ERROR , response = None )
465
+
466
+
420
467
class ReplTestRunner :
421
468
'''Test runner to encode/decode values from YAML test Parser for executing the TestStep.
422
469
423
470
Uses ChipDeviceCtrl from chip-repl to execute parsed YAML TestSteps.
424
471
'''
425
472
426
- def __init__ (self , test_spec_definition , dev_ctrl ):
473
+ def __init__ (self , test_spec_definition , certificate_authority_manager ):
427
474
self ._test_spec_definition = test_spec_definition
428
- self ._dev_ctrl = dev_ctrl
429
475
self ._context = _ExecutionContext (data_model_lookup = PreDefinedDataModelLookup ())
476
+ self ._certificate_authority_manager = certificate_authority_manager
477
+ self ._dev_ctrls = {}
478
+
479
+ ca_list = certificate_authority_manager .activeCaList
480
+ dev_ctrl = ca_list [0 ].adminList [0 ].NewController ()
481
+ self ._dev_ctrls ['alpha' ] = dev_ctrl
430
482
431
483
def _invoke_action_factory (self , test_step , cluster : str ):
432
484
'''Creates cluster invoke action command from TestStep.
@@ -513,12 +565,21 @@ def _wait_for_report_action_factory(self, test_step):
513
565
# propogated.
514
566
return None
515
567
568
+ def _commissioner_command_action_factory (self , test_step ):
569
+ try :
570
+ return CommissionerCommandAction (test_step )
571
+ except ParsingError :
572
+ return None
573
+
516
574
def encode (self , request ) -> BaseAction :
517
575
action = None
518
576
cluster = request .cluster .replace (' ' , '' ).replace ('/' , '' )
519
577
command = request .command
578
+ if cluster == 'CommissionerCommands' :
579
+ return self ._commissioner_command_action_factory (request )
520
580
# Some of the tests contain 'cluster over-rides' that refer to a different
521
581
# cluster than that specified in 'config'.
582
+
522
583
if cluster == 'DelayCommands' and command == 'WaitForCommissionee' :
523
584
action = self ._wait_for_commissionee_action_factory (request )
524
585
elif command == 'writeAttribute' :
@@ -588,8 +649,33 @@ def decode(self, result: _ActionResult):
588
649
589
650
return decoded_response
590
651
652
+ def _get_fabric_id (self , id ):
653
+ return _TestFabricId [id .upper ()].value
654
+
655
+ def _get_dev_ctrl (self , action : BaseAction ):
656
+ if action .identity is not None :
657
+ dev_ctrl = self ._dev_ctrls .get (action .identity , None )
658
+ if dev_ctrl is None :
659
+ fabric_id = self ._get_fabric_id (action .identity )
660
+ certificate_authority = self ._certificate_authority_manager .activeCaList [0 ]
661
+ fabric = None
662
+ for existing_admin in certificate_authority .adminList :
663
+ if existing_admin .fabricId == fabric_id :
664
+ fabric = existing_admin
665
+
666
+ if fabric is None :
667
+ fabric = certificate_authority .NewFabricAdmin (vendorId = 0xFFF1 ,
668
+ fabricId = fabric_id )
669
+ dev_ctrl = fabric .NewController ()
670
+ self ._dev_ctrls [action .identity ] = dev_ctrl
671
+ else :
672
+ dev_ctrl = self ._dev_ctrls ['alpha' ]
673
+
674
+ return dev_ctrl
675
+
591
676
def execute (self , action : BaseAction ):
592
- return action .run_action (self ._dev_ctrl )
677
+ dev_ctrl = self ._get_dev_ctrl (action )
678
+ return action .run_action (dev_ctrl )
593
679
594
680
def shutdown (self ):
595
681
for subscription in self ._context .subscriptions :
0 commit comments