Skip to content

Commit aef88b6

Browse files
authored
Add TC_CCTRL_2_1, TC_CCTRL_2_2, TC_CCTRL_2_3 to CI (project-chip#35886)
* Add TC_CCTRL_2_1 to CI * Add TC_CCTRL_2_2 to CI * Fix copy-paste typo * Allow to override test runner YAML options with command line options * Add TC_CCTRL_2_3 to CI * Run tests on CI * Add MCORE.FS to PICS.yaml
1 parent 78f489d commit aef88b6

File tree

11 files changed

+166
-86
lines changed

11 files changed

+166
-86
lines changed

scripts/tests/run_python_test.py

+17-10
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ def process_test_script_output(line, is_stderr):
7171
@click.command()
7272
@click.option("--app", type=click.Path(exists=True), default=None,
7373
help='Path to local application to use, omit to use external apps.')
74-
@click.option("--factoryreset", is_flag=True,
74+
@click.option("--factory-reset/--no-factory-reset", default=None,
7575
help='Remove app config and repl configs (/tmp/chip* and /tmp/repl*) before running the tests.')
76-
@click.option("--factoryreset-app-only", is_flag=True,
76+
@click.option("--factory-reset-app-only/--no-factory-reset-app-only", default=None,
7777
help='Remove app config and repl configs (/tmp/chip* and /tmp/repl*) before running the tests, but not the controller config')
7878
@click.option("--app-args", type=str, default='',
7979
help='The extra arguments passed to the device. Can use placeholders like {SCRIPT_BASE_NAME}')
@@ -90,9 +90,10 @@ def process_test_script_output(line, is_stderr):
9090
help='Script arguments, can use placeholders like {SCRIPT_BASE_NAME}.')
9191
@click.option("--script-gdb", is_flag=True,
9292
help='Run script through gdb')
93-
@click.option("--quiet", is_flag=True, help="Do not print output from passing tests. Use this flag in CI to keep github log sizes manageable.")
93+
@click.option("--quiet/--no-quiet", default=None,
94+
help="Do not print output from passing tests. Use this flag in CI to keep GitHub log size manageable.")
9495
@click.option("--load-from-env", default=None, help="YAML file that contains values for environment variables.")
95-
def main(app: str, factoryreset: bool, factoryreset_app_only: bool, app_args: str,
96+
def main(app: str, factory_reset: bool, factory_reset_app_only: bool, app_args: str,
9697
app_ready_pattern: str, script: str, script_args: str, script_gdb: bool, quiet: bool, load_from_env):
9798
if load_from_env:
9899
reader = MetadataReader(load_from_env)
@@ -106,18 +107,23 @@ def main(app: str, factoryreset: bool, factoryreset_app_only: bool, app_args: st
106107
app_args=app_args,
107108
app_ready_pattern=app_ready_pattern,
108109
script_args=script_args,
109-
factory_reset=factoryreset,
110-
factory_reset_app_only=factoryreset_app_only,
111110
script_gdb=script_gdb,
112-
quiet=quiet
113111
)
114112
]
115113

116114
if not runs:
117-
raise Exception(
118-
"No valid runs were found. Make sure you add runs to your file, see https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md document for reference/example.")
115+
raise click.ClickException(
116+
"No valid runs were found. Make sure you add runs to your file, see "
117+
"https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md document for reference/example.")
119118

120-
coloredlogs.install(level='INFO')
119+
# Override runs Metadata with the command line arguments
120+
for run in runs:
121+
if factory_reset is not None:
122+
run.factory_reset = factory_reset
123+
if factory_reset_app_only is not None:
124+
run.factory_reset_app_only = factory_reset_app_only
125+
if quiet is not None:
126+
run.quiet = quiet
121127

122128
for run in runs:
123129
logging.info("Executing %s %s", run.py_script_path.split('/')[-1], run.run)
@@ -215,4 +221,5 @@ def main_impl(app: str, factory_reset: bool, factory_reset_app_only: bool, app_a
215221

216222

217223
if __name__ == '__main__':
224+
coloredlogs.install(level='INFO')
218225
main(auto_envvar_prefix='CHIP')

src/app/tests/suites/certification/PICS.yaml

+7
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,13 @@ PICS:
339339
"Does commissionee provide a Firmware Information field in the
340340
AttestationResponse?"
341341
id: MCORE.DA.ATTESTELEMENT_FW_INFO
342+
343+
#
344+
# Fabric Synchronization
345+
#
346+
- label: "Does the device implement Fabric Synchronization capabilities?"
347+
id: MCORE.FS
348+
342349
#
343350
#IDM
344351
#

src/app/tests/suites/certification/ci-pics-values

+3
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,9 @@ MCORE.BDX.Sender=0
922922
MCORE.BDX.SynchronousReceiver=0
923923
MCORE.BDX.SynchronousSender=0
924924

925+
# Fabric Synchronization
926+
MCORE.FS=1
927+
925928
# General Diagnostics Cluster
926929

927930
DGGEN.S=1

src/python_testing/TC_CCTRL_2_1.py

+22
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,28 @@
1515
# limitations under the License.
1616
#
1717

18+
# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments
19+
# for details about the block below.
20+
#
21+
# === BEGIN CI TEST ARGUMENTS ===
22+
# test-runner-runs:
23+
# run1:
24+
# app: examples/fabric-admin/scripts/fabric-sync-app.py
25+
# app-args: --app-admin=${FABRIC_ADMIN_APP} --app-bridge=${FABRIC_BRIDGE_APP} --stdin-pipe=dut-fsa-stdin --discriminator=1234
26+
# app-ready-pattern: "Successfully opened pairing window on the device"
27+
# script-args: >
28+
# --PICS src/app/tests/suites/certification/ci-pics-values
29+
# --storage-path admin_storage.json
30+
# --commissioning-method on-network
31+
# --discriminator 1234
32+
# --passcode 20202021
33+
# --endpoint 0
34+
# --trace-to json:${TRACE_TEST_JSON}.json
35+
# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
36+
# factoryreset: true
37+
# quiet: true
38+
# === END CI TEST ARGUMENTS ===
39+
1840
import chip.clusters as Clusters
1941
from matter_testing_support import MatterBaseTest, TestStep, default_matter_test_main, has_cluster, run_if_endpoint_matches
2042
from mobly import asserts

src/python_testing/TC_CCTRL_2_2.py

+56-34
Original file line numberDiff line numberDiff line change
@@ -18,50 +18,74 @@
1818
# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments
1919
# for details about the block below.
2020
#
21-
# TODO: Skip CI for now, we don't have any way to run this. Needs setup. See test_TC_CCTRL.py
21+
# === BEGIN CI TEST ARGUMENTS ===
22+
# test-runner-runs:
23+
# run1:
24+
# app: examples/fabric-admin/scripts/fabric-sync-app.py
25+
# app-args: --app-admin=${FABRIC_ADMIN_APP} --app-bridge=${FABRIC_BRIDGE_APP} --stdin-pipe=dut-fsa-stdin --discriminator=1234
26+
# app-ready-pattern: "Successfully opened pairing window on the device"
27+
# script-args: >
28+
# --PICS src/app/tests/suites/certification/ci-pics-values
29+
# --storage-path admin_storage.json
30+
# --commissioning-method on-network
31+
# --discriminator 1234
32+
# --passcode 20202021
33+
# --endpoint 0
34+
# --string-arg th_server_app_path:${ALL_CLUSTERS_APP}
35+
# --trace-to json:${TRACE_TEST_JSON}.json
36+
# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
37+
# factoryreset: true
38+
# quiet: true
39+
# === END CI TEST ARGUMENTS ===
2240

2341
# This test requires a TH_SERVER application. Please specify with --string-arg th_server_app_path:<path_to_app>
2442

2543
import logging
2644
import os
2745
import random
28-
import signal
29-
import subprocess
46+
import tempfile
3047
import time
31-
import uuid
3248

3349
import chip.clusters as Clusters
3450
from chip import ChipDeviceCtrl
3551
from chip.interaction_model import InteractionModelError, Status
3652
from matter_testing_support import (MatterBaseTest, TestStep, async_test_body, default_matter_test_main, has_cluster,
3753
run_if_endpoint_matches)
3854
from mobly import asserts
55+
from TC_MCORE_FS_1_1 import AppServer
3956

4057

4158
class TC_CCTRL_2_2(MatterBaseTest):
4259

4360
@async_test_body
4461
async def setup_class(self):
4562
super().setup_class()
46-
self.app_process = None
47-
app = self.user_params.get("th_server_app_path", None)
48-
if not app:
49-
asserts.fail('This test requires a TH_SERVER app. Specify app path with --string-arg th_server_app_path:<path_to_app>')
50-
51-
self.kvs = f'kvs_{str(uuid.uuid4())}'
52-
self.port = 5543
53-
discriminator = random.randint(0, 4095)
54-
passcode = 20202021
55-
cmd = [app]
56-
cmd.extend(['--secured-device-port', str(5543)])
57-
cmd.extend(['--discriminator', str(discriminator)])
58-
cmd.extend(['--passcode', str(passcode)])
59-
cmd.extend(['--KVS', self.kvs])
60-
# TODO: Determine if we want these logs cooked or pushed to somewhere else
61-
logging.info("Starting TH_SERVER")
62-
self.app_process = subprocess.Popen(cmd)
63-
logging.info("TH_SERVER started")
64-
time.sleep(3)
63+
64+
self.th_server = None
65+
self.storage = None
66+
67+
th_server_app = self.user_params.get("th_server_app_path", None)
68+
if not th_server_app:
69+
asserts.fail("This test requires a TH_SERVER app. Specify app path with --string-arg th_server_app_path:<path_to_app>")
70+
if not os.path.exists(th_server_app):
71+
asserts.fail(f"The path {th_server_app} does not exist")
72+
73+
# Create a temporary storage directory for keeping KVS files.
74+
self.storage = tempfile.TemporaryDirectory(prefix=self.__class__.__name__)
75+
logging.info("Temporary storage directory: %s", self.storage.name)
76+
77+
self.th_server_port = 5543
78+
self.th_server_discriminator = random.randint(0, 4095)
79+
self.th_server_passcode = 20202021
80+
81+
# Start the TH_SERVER app.
82+
self.th_server = AppServer(
83+
th_server_app,
84+
storage_dir=self.storage.name,
85+
port=self.th_server_port,
86+
discriminator=self.th_server_discriminator,
87+
passcode=self.th_server_passcode)
88+
self.th_server.start()
6589

6690
logging.info("Commissioning from separate fabric")
6791

@@ -71,20 +95,18 @@ async def setup_class(self):
7195
paa_path = str(self.matter_test_config.paa_trust_store_path)
7296
self.TH_server_controller = new_fabric_admin.NewController(nodeId=112233, paaTrustStorePath=paa_path)
7397
self.server_nodeid = 1111
74-
await self.TH_server_controller.CommissionOnNetwork(nodeId=self.server_nodeid, setupPinCode=passcode, filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=discriminator)
98+
await self.TH_server_controller.CommissionOnNetwork(
99+
nodeId=self.server_nodeid,
100+
setupPinCode=self.th_server_passcode,
101+
filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR,
102+
filter=self.th_server_discriminator)
75103
logging.info("Commissioning TH_SERVER complete")
76104

77105
def teardown_class(self):
78-
# In case the th_server_app_path does not exist, then we failed the test
79-
# and there is nothing to remove
80-
if self.app_process is not None:
81-
logging.warning("Stopping app with SIGTERM")
82-
self.app_process.send_signal(signal.SIGTERM.value)
83-
self.app_process.wait()
84-
85-
if os.path.exists(self.kvs):
86-
os.remove(self.kvs)
87-
106+
if self.th_server is not None:
107+
self.th_server.terminate()
108+
if self.storage is not None:
109+
self.storage.cleanup()
88110
super().teardown_class()
89111

90112
def steps_TC_CCTRL_2_2(self) -> list[TestStep]:

src/python_testing/TC_CCTRL_2_3.py

+57-35
Original file line numberDiff line numberDiff line change
@@ -18,50 +18,74 @@
1818
# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments
1919
# for details about the block below.
2020
#
21-
# TODO: Skip CI for now, we don't have any way to run this. Needs setup. See test_TC_CCTRL.py
21+
# === BEGIN CI TEST ARGUMENTS ===
22+
# test-runner-runs:
23+
# run1:
24+
# app: examples/fabric-admin/scripts/fabric-sync-app.py
25+
# app-args: --app-admin=${FABRIC_ADMIN_APP} --app-bridge=${FABRIC_BRIDGE_APP} --stdin-pipe=dut-fsa-stdin --discriminator=1234
26+
# app-ready-pattern: "Successfully opened pairing window on the device"
27+
# script-args: >
28+
# --PICS src/app/tests/suites/certification/ci-pics-values
29+
# --storage-path admin_storage.json
30+
# --commissioning-method on-network
31+
# --discriminator 1234
32+
# --passcode 20202021
33+
# --endpoint 0
34+
# --string-arg th_server_app_path:${ALL_CLUSTERS_APP}
35+
# --trace-to json:${TRACE_TEST_JSON}.json
36+
# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
37+
# factoryreset: true
38+
# quiet: true
39+
# === END CI TEST ARGUMENTS ===
2240

2341
# This test requires a TH_SERVER application. Please specify with --string-arg th_server_app_path:<path_to_app>
2442

2543
import logging
2644
import os
2745
import random
28-
import signal
29-
import subprocess
46+
import tempfile
3047
import time
31-
import uuid
3248

3349
import chip.clusters as Clusters
3450
from chip import ChipDeviceCtrl
3551
from chip.interaction_model import InteractionModelError, Status
3652
from matter_testing_support import (MatterBaseTest, TestStep, async_test_body, default_matter_test_main, has_cluster,
3753
run_if_endpoint_matches)
3854
from mobly import asserts
55+
from TC_MCORE_FS_1_1 import AppServer
3956

4057

4158
class TC_CCTRL_2_3(MatterBaseTest):
4259

4360
@async_test_body
4461
async def setup_class(self):
4562
super().setup_class()
46-
self.app_process = None
47-
app = self.user_params.get("th_server_app_path", None)
48-
if not app:
49-
asserts.fail('This test requires a TH_SERVER app. Specify app path with --string-arg th_server_app_path:<path_to_app>')
50-
51-
self.kvs = f'kvs_{str(uuid.uuid4())}'
52-
self.port = 5543
53-
discriminator = random.randint(0, 4095)
54-
passcode = 20202021
55-
cmd = [app]
56-
cmd.extend(['--secured-device-port', str(5543)])
57-
cmd.extend(['--discriminator', str(discriminator)])
58-
cmd.extend(['--passcode', str(passcode)])
59-
cmd.extend(['--KVS', self.kvs])
60-
# TODO: Determine if we want these logs cooked or pushed to somewhere else
61-
logging.info("Starting TH_SERVER")
62-
self.app_process = subprocess.Popen(cmd)
63-
logging.info("TH_SERVER started")
64-
time.sleep(3)
63+
64+
self.th_server = None
65+
self.storage = None
66+
67+
th_server_app = self.user_params.get("th_server_app_path", None)
68+
if not th_server_app:
69+
asserts.fail("This test requires a TH_SERVER app. Specify app path with --string-arg th_server_app_path:<path_to_app>")
70+
if not os.path.exists(th_server_app):
71+
asserts.fail(f"The path {th_server_app} does not exist")
72+
73+
# Create a temporary storage directory for keeping KVS files.
74+
self.storage = tempfile.TemporaryDirectory(prefix=self.__class__.__name__)
75+
logging.info("Temporary storage directory: %s", self.storage.name)
76+
77+
self.th_server_port = 5543
78+
self.th_server_discriminator = random.randint(0, 4095)
79+
self.th_server_passcode = 20202021
80+
81+
# Start the TH_SERVER app.
82+
self.th_server = AppServer(
83+
th_server_app,
84+
storage_dir=self.storage.name,
85+
port=self.th_server_port,
86+
discriminator=self.th_server_discriminator,
87+
passcode=self.th_server_passcode)
88+
self.th_server.start()
6589

6690
logging.info("Commissioning from separate fabric")
6791

@@ -71,20 +95,18 @@ async def setup_class(self):
7195
paa_path = str(self.matter_test_config.paa_trust_store_path)
7296
self.TH_server_controller = new_fabric_admin.NewController(nodeId=112233, paaTrustStorePath=paa_path)
7397
self.server_nodeid = 1111
74-
await self.TH_server_controller.CommissionOnNetwork(nodeId=self.server_nodeid, setupPinCode=passcode, filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=discriminator)
98+
await self.TH_server_controller.CommissionOnNetwork(
99+
nodeId=self.server_nodeid,
100+
setupPinCode=self.th_server_passcode,
101+
filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR,
102+
filter=self.th_server_discriminator)
75103
logging.info("Commissioning TH_SERVER complete")
76104

77105
def teardown_class(self):
78-
# In case the th_server_app_path does not exist, then we failed the test
79-
# and there is nothing to remove
80-
if self.app_process is not None:
81-
logging.warning("Stopping app with SIGTERM")
82-
self.app_process.send_signal(signal.SIGTERM.value)
83-
self.app_process.wait()
84-
85-
if os.path.exists(self.kvs):
86-
os.remove(self.kvs)
87-
106+
if self.th_server is not None:
107+
self.th_server.terminate()
108+
if self.storage is not None:
109+
self.storage.cleanup()
88110
super().teardown_class()
89111

90112
def steps_TC_CCTRL_2_3(self) -> list[TestStep]:
@@ -172,7 +194,7 @@ async def test_TC_CCTRL_2_3(self):
172194
await self.send_single_cmd(cmd, dev_ctrl=self.TH_server_controller, node_id=self.server_nodeid, endpoint=0, timedRequestTimeoutMs=5000)
173195

174196
self.step(11)
175-
time.sleep(30)
197+
time.sleep(5 if self.is_pics_sdk_ci_only else 30)
176198

177199
self.step(12)
178200
th_server_fabrics_new = await self.read_single_attribute_check_success(cluster=Clusters.OperationalCredentials, attribute=Clusters.OperationalCredentials.Attributes.Fabrics, dev_ctrl=self.TH_server_controller, node_id=self.server_nodeid, endpoint=0, fabric_filtered=False)

src/python_testing/TC_MCORE_FS_1_1.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ async def setup_class(self):
9595
self.th_server_discriminator = random.randint(0, 4095)
9696
self.th_server_passcode = 20202021
9797

98-
# Start the TH_SERVER_NO_UID app.
98+
# Start the TH_SERVER app.
9999
self.th_server = AppServer(
100100
th_server_app,
101101
storage_dir=self.storage.name,

src/python_testing/TC_MCORE_FS_1_2.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ async def setup_class(self):
110110
discriminator=3840,
111111
passcode=20202021)
112112

113-
# Start the TH_SERVER_NO_UID app.
113+
# Start the TH_SERVER app.
114114
self.th_server = AppServer(
115115
th_server_app,
116116
storage_dir=self.storage.name,

0 commit comments

Comments
 (0)