36
36
import logging
37
37
import os
38
38
import random
39
- import subprocess
40
- import sys
41
39
import tempfile
42
- import threading
43
40
44
41
import chip .clusters as Clusters
45
42
from chip import ChipDeviceCtrl
46
43
from chip .interaction_model import Status
44
+ from chip .testing .tasks import Subprocess
47
45
from matter_testing_support import MatterBaseTest , TestStep , async_test_body , default_matter_test_main , type_matches
48
46
from mobly import asserts
49
47
50
- # TODO: Make this class more generic. Issue #35348
51
-
52
-
53
- class Subprocess (threading .Thread ):
54
-
55
- def __init__ (self , args : list = [], stdout_cb = None , tag = "" , ** kw ):
56
- super ().__init__ (** kw )
57
- self .tag = f"[{ tag } ] " if tag else ""
58
- self .stdout_cb = stdout_cb
59
- self .args = args
60
-
61
- def forward_f (self , f_in , f_out ):
62
- while True :
63
- line = f_in .readline ()
64
- if not line :
65
- break
66
- f_out .write (f"{ self .tag } { line } " )
67
- f_out .flush ()
68
- if self .stdout_cb is not None :
69
- self .stdout_cb (line )
70
-
71
- def run (self ):
72
- logging .info ("RUN: %s" , " " .join (self .args ))
73
- self .p = subprocess .Popen (self .args , errors = "ignore" , stdin = subprocess .PIPE ,
74
- stdout = subprocess .PIPE , stderr = subprocess .PIPE )
75
- # Forward stdout and stderr with a tag attached.
76
- forwarding_stdout_thread = threading .Thread (target = self .forward_f , args = [self .p .stdout , sys .stdout ])
77
- forwarding_stdout_thread .start ()
78
- forwarding_stderr_thread = threading .Thread (target = self .forward_f , args = [self .p .stderr , sys .stderr ])
79
- forwarding_stderr_thread .start ()
80
- # Wait for the process to finish.
81
- self .p .wait ()
82
- forwarding_stdout_thread .join ()
83
- forwarding_stderr_thread .join ()
84
-
85
- def stop (self ):
86
- self .p .terminate ()
87
- self .join ()
88
-
89
48
90
49
class FabricSyncApp :
91
50
92
- def _process_admin_output (self , line ):
93
- if self .wait_for_text_text is not None and self .wait_for_text_text in line :
94
- self .wait_for_text_event .set ()
95
-
96
- def wait_for_text (self , timeout = 30 ):
97
- if not self .wait_for_text_event .wait (timeout = timeout ):
98
- raise Exception (f"Timeout waiting for text: { self .wait_for_text_text } " )
99
- self .wait_for_text_event .clear ()
100
- self .wait_for_text_text = None
101
-
102
51
def __init__ (self , fabric_sync_app_path , fabric_admin_app_path , fabric_bridge_app_path ,
103
52
storage_dir , fabric_name = None , node_id = None , vendor_id = None ,
104
53
paa_trust_store_path = None , bridge_port = None , bridge_discriminator = None ,
105
54
bridge_passcode = None ):
106
-
107
- self .wait_for_text_event = threading .Event ()
108
- self .wait_for_text_text = None
109
-
110
- args = [fabric_sync_app_path ]
111
- args .append (f"--app-admin={ fabric_admin_app_path } " )
112
- args .append (f"--app-bridge={ fabric_bridge_app_path } " )
113
- # Override default ports, so it will be possible to run
114
- # our TH_FSA alongside the DUT_FSA during CI testing.
115
- args .append ("--app-admin-rpc-port=44000" )
116
- args .append ("--app-bridge-rpc-port=44001" )
117
- # Keep the storage directory in a temporary location.
118
- args .append (f"--storage-dir={ storage_dir } " )
55
+ args = [
56
+ f"--app-admin={ fabric_admin_app_path } " ,
57
+ f"--app-bridge={ fabric_bridge_app_path } " ,
58
+ # Override default ports, so it will be possible to run
59
+ # our TH_FSA alongside the DUT_FSA during CI testing.
60
+ "--app-admin-rpc-port=44000" ,
61
+ "--app-bridge-rpc-port=44001" ,
62
+ # Keep the storage directory in a temporary location.
63
+ f"--storage-dir={ storage_dir } " ,
64
+ ]
119
65
if paa_trust_store_path is not None :
120
66
args .append (f"--paa-trust-store-path={ paa_trust_store_path } " )
121
67
if fabric_name is not None :
@@ -127,55 +73,38 @@ def __init__(self, fabric_sync_app_path, fabric_admin_app_path, fabric_bridge_ap
127
73
args .append (f"--discriminator={ bridge_discriminator } " )
128
74
args .append (f"--passcode={ bridge_passcode } " )
129
75
130
- self .fabric_sync_app = Subprocess (args , stdout_cb = self ._process_admin_output )
131
- self .wait_for_text_text = "Successfully opened pairing window on the device"
132
- self .fabric_sync_app .start ()
76
+ self .fabric_sync_app = Subprocess (fabric_sync_app_path , * args )
133
77
134
- # Wait for the fabric-sync-app to be ready.
135
- self .wait_for_text ()
78
+ def start (self ):
79
+ # Start process and block until it prints the expected output.
80
+ self .fabric_sync_app .start (expected_output = "Successfully opened pairing window on the device" )
136
81
137
- def commission_on_network (self , node_id , setup_pin_code = None , filter_type = None , filter = None ):
138
- self .wait_for_text_text = f"Commissioning complete for node ID { node_id :#018x} : success"
139
- # Send the commissioning command to the admin.
140
- self .fabric_sync_app .p .stdin .write (f"pairing onnetwork { node_id } { setup_pin_code } \n " )
141
- self .fabric_sync_app .p .stdin .flush ()
142
- # Wait for success message.
143
- self .wait_for_text ()
82
+ def terminate (self ):
83
+ self .fabric_sync_app .terminate ()
144
84
145
- def stop (self ):
146
- self .fabric_sync_app .stop ()
85
+ def commission_on_network (self , node_id , setup_pin_code = None , filter_type = None , filter = None ):
86
+ self .fabric_sync_app .send (
87
+ f"pairing onnetwork { node_id } { setup_pin_code } " ,
88
+ expected_output = f"Commissioning complete for node ID { node_id :#018x} : success" )
147
89
148
90
149
91
class AppServer :
150
92
151
- def _process_admin_output (self , line ):
152
- if self .wait_for_text_text is not None and self .wait_for_text_text in line :
153
- self .wait_for_text_event .set ()
154
-
155
- def wait_for_text (self , timeout = 30 ):
156
- if not self .wait_for_text_event .wait (timeout = timeout ):
157
- raise Exception (f"Timeout waiting for text: { self .wait_for_text_text } " )
158
- self .wait_for_text_event .clear ()
159
- self .wait_for_text_text = None
160
-
161
93
def __init__ (self , app , storage_dir , port = None , discriminator = None , passcode = None ):
162
- self .wait_for_text_event = threading .Event ()
163
- self .wait_for_text_text = None
164
-
165
- args = [app ]
166
- args .extend (["--KVS" , tempfile .mkstemp (dir = storage_dir , prefix = "kvs-app-" )[1 ]])
94
+ args = [
95
+ "--KVS" , tempfile .mkstemp (dir = storage_dir , prefix = "kvs-app-" )[1 ],
96
+ ]
167
97
args .extend (['--secured-device-port' , str (port )])
168
98
args .extend (["--discriminator" , str (discriminator )])
169
99
args .extend (["--passcode" , str (passcode )])
170
- self .app = Subprocess (args , stdout_cb = self ._process_admin_output , tag = "SERVER" )
171
- self .wait_for_text_text = "Server initialization complete"
172
- self .app .start ()
100
+ self .app = Subprocess (app , * args , prefix = "[SERVER]" )
173
101
174
- # Wait for the server-app to be ready.
175
- self .wait_for_text ()
102
+ def start (self ):
103
+ # Start process and block until it prints the expected output.
104
+ self .app .start (expected_output = "Server initialization complete" )
176
105
177
- def stop (self ):
178
- self .app .stop ()
106
+ def terminate (self ):
107
+ self .app .terminate ()
179
108
180
109
181
110
class TC_MCORE_FS_1_4 (MatterBaseTest ):
@@ -237,6 +166,7 @@ def setup_class(self):
237
166
bridge_discriminator = self .th_fsa_bridge_discriminator ,
238
167
bridge_passcode = self .th_fsa_bridge_passcode ,
239
168
vendor_id = 0xFFF1 )
169
+ self .th_fsa_controller .start ()
240
170
241
171
# Get the named pipe path for the DUT_FSA app input from the user params.
242
172
dut_fsa_stdin_pipe = self .user_params .get ("dut_fsa_stdin_pipe" , None )
@@ -254,12 +184,13 @@ def setup_class(self):
254
184
port = self .th_server_port ,
255
185
discriminator = self .th_server_discriminator ,
256
186
passcode = self .th_server_passcode )
187
+ self .th_server .start ()
257
188
258
189
def teardown_class (self ):
259
190
if self .th_fsa_controller is not None :
260
- self .th_fsa_controller .stop ()
191
+ self .th_fsa_controller .terminate ()
261
192
if self .th_server is not None :
262
- self .th_server .stop ()
193
+ self .th_server .terminate ()
263
194
if self .storage is not None :
264
195
self .storage .cleanup ()
265
196
super ().teardown_class ()
0 commit comments