Skip to content

Commit 802e38f

Browse files
arkqyyzhong-g
authored andcommitted
Add ECOINFO_2_1 and ECOINFO_2_2 to CI (project-chip#35810)
* Make Subprocess wrapper class even more generic * Add ECOINFO_2_1 to CI * Add ECOINFO_2_2 to CI * Rewrite TC_MCORE_FS CI arguments to YAML * [Testing] Convenience property for checking PICS_SDK_CI_ONLY * Guard CI prerequisites setup with PICS SDK CI * Fix typo
1 parent 3bc233d commit 802e38f

File tree

9 files changed

+352
-97
lines changed

9 files changed

+352
-97
lines changed

src/python_testing/TC_ECOINFO_2_1.py

+101-13
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,104 @@
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+
# script-args: >
27+
# --PICS src/app/tests/suites/certification/ci-pics-values
28+
# --storage-path admin_storage.json
29+
# --commissioning-method on-network
30+
# --discriminator 1234
31+
# --passcode 20202021
32+
# --string-arg th_server_app_path:${ALL_CLUSTERS_APP} dut_fsa_stdin_pipe:dut-fsa-stdin
33+
# --trace-to json:${TRACE_TEST_JSON}.json
34+
# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
35+
# script-start-delay: 5
36+
# factoryreset: true
37+
# quiet: false
38+
# === END CI TEST ARGUMENTS ===
39+
40+
import asyncio
41+
import logging
42+
import os
43+
import random
44+
import tempfile
45+
1846
import chip.clusters as Clusters
1947
from chip.clusters.Types import NullValue
2048
from chip.interaction_model import Status
2149
from chip.tlv import uint
2250
from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main, type_matches
2351
from mobly import asserts
52+
from TC_MCORE_FS_1_1 import AppServer
2453

2554

2655
class TC_ECOINFO_2_1(MatterBaseTest):
2756

57+
@async_test_body
58+
async def setup_class(self):
59+
super().setup_class()
60+
61+
self.th_server = None
62+
self.storage = None
63+
64+
if self.is_pics_sdk_ci_only:
65+
await self._setup_ci_prerequisites()
66+
67+
def teardown_class(self):
68+
if self.th_server is not None:
69+
self.th_server.terminate()
70+
if self.storage is not None:
71+
self.storage.cleanup()
72+
super().teardown_class()
73+
74+
async def _setup_ci_prerequisites(self):
75+
asserts.assert_true(self.is_pics_sdk_ci_only, "This method is only for PICS SDK CI")
76+
77+
th_server_app = self.user_params.get("th_server_app_path", None)
78+
if not th_server_app:
79+
asserts.fail("CI setup requires a TH_SERVER app. Specify app path with --string-arg th_server_app_path:<path_to_app>")
80+
if not os.path.exists(th_server_app):
81+
asserts.fail(f"The path {th_server_app} does not exist")
82+
83+
# Get the named pipe path for the DUT_FSA app input from the user params.
84+
dut_fsa_stdin_pipe = self.user_params.get("dut_fsa_stdin_pipe")
85+
if not dut_fsa_stdin_pipe:
86+
asserts.fail("CI setup requires --string-arg dut_fsa_stdin_pipe:<path_to_pipe>")
87+
self.dut_fsa_stdin = open(dut_fsa_stdin_pipe, "w")
88+
89+
# Create a temporary storage directory for keeping KVS files.
90+
self.storage = tempfile.TemporaryDirectory(prefix=self.__class__.__name__)
91+
logging.info("Temporary storage directory: %s", self.storage.name)
92+
93+
self.th_server_port = 5544
94+
self.th_server_discriminator = random.randint(0, 4095)
95+
self.th_server_passcode = 20202021
96+
97+
# Start the server app.
98+
self.th_server = AppServer(
99+
th_server_app,
100+
storage_dir=self.storage.name,
101+
port=self.th_server_port,
102+
discriminator=self.th_server_discriminator,
103+
passcode=self.th_server_passcode)
104+
self.th_server.start()
105+
106+
# Add some server to the DUT_FSA's Aggregator/Bridge.
107+
self.dut_fsa_stdin.write(f"pairing onnetwork 2 {self.th_server_passcode}\n")
108+
self.dut_fsa_stdin.flush()
109+
# Wait for the commissioning to complete.
110+
await asyncio.sleep(5)
111+
28112
def _validate_device_directory(self, current_fabric_index, device_directory):
29113
for device in device_directory:
30114
if current_fabric_index != device.fabricIndex:
31-
# Fabric sensitve field still exist in python, just that they have default values
115+
# Fabric sensitive field still exist in python, just that they have default values
32116
asserts.assert_equal(device.deviceName, None, "Unexpected value in deviceName")
33117
asserts.assert_equal(device.deviceNameLastEdit, None, "Unexpected value in deviceNameLastEdit")
34118
asserts.assert_equal(device.bridgedEndpoint, 0, "Unexpected value in bridgedEndpoint")
@@ -85,7 +169,7 @@ def _validate_device_directory(self, current_fabric_index, device_directory):
85169
def _validate_location_directory(self, current_fabric_index, location_directory):
86170
for location in location_directory:
87171
if current_fabric_index != location.fabricIndex:
88-
# Fabric sensitve field still exist in python, just that they have default values
172+
# Fabric sensitive field still exist in python, just that they have default values
89173
asserts.assert_equal(location.uniqueLocationID, "", "Unexpected value in uniqueLocationID")
90174
asserts.assert_equal(location.locationDescriptor.locationName, "",
91175
"Unexpected value in locationDescriptor.locationName")
@@ -120,30 +204,34 @@ def _validate_location_directory(self, current_fabric_index, location_directory)
120204
asserts.assert_greater(location.locationDescriptorLastEdit, 0, "LocationDescriptorLastEdit must be non-zero")
121205

122206
def steps_TC_ECOINFO_2_1(self) -> list[TestStep]:
123-
steps = [TestStep(1, "Identify endpoints with Ecosystem Information Cluster", is_commissioning=True),
124-
TestStep(2, "Reading DeviceDirectory Attribute"),
125-
TestStep(3, "Reading LocationDirectory Attribute"),
126-
TestStep(4, "Try Writing to DeviceDirectory Attribute"),
127-
TestStep(5, "Try Writing to LocationDirectory Attribute"),
128-
TestStep(6, "Repeating steps 2 to 5 for each endpoint identified in step 1")]
129-
return steps
207+
return [
208+
TestStep(0, "Commission DUT if not done", is_commissioning=True),
209+
TestStep(1, "Identify endpoints with Ecosystem Information Cluster"),
210+
TestStep(2, "Reading DeviceDirectory Attribute"),
211+
TestStep(3, "Reading LocationDirectory Attribute"),
212+
TestStep(4, "Try Writing to DeviceDirectory Attribute"),
213+
TestStep(5, "Try Writing to LocationDirectory Attribute"),
214+
TestStep(6, "Repeating steps 2 to 5 for each endpoint identified in step 1"),
215+
]
130216

131217
@async_test_body
132218
async def test_TC_ECOINFO_2_1(self):
133219
dev_ctrl = self.default_controller
134220
dut_node_id = self.dut_node_id
135221

136-
self.print_step(0, "Commissioning, already done")
222+
# Commissioning - done
223+
self.step(0)
137224

138-
pause_for_pre_condition = self.user_params.get("pause_for_pre_condition", False)
139-
if pause_for_pre_condition:
225+
if not self.is_pics_sdk_ci_only:
140226
self.wait_for_user_input(
141-
"Paused test to allow for manufacturer to satisfy precondition where one or more bridged devices of a supported type is connected to DUT")
227+
"Paused test to allow for manufacturer to satisfy precondition where "
228+
"one or more bridged devices of a supported type is connected to DUT")
142229

143230
current_fabric_index = await self.read_single_attribute_check_success(cluster=Clusters.OperationalCredentials, attribute=Clusters.OperationalCredentials.Attributes.CurrentFabricIndex)
144231
self.step(1)
145232
endpoint_wild_card_read = await dev_ctrl.ReadAttribute(dut_node_id, [(Clusters.EcosystemInformation.Attributes.ClusterRevision)])
146233
list_of_endpoints = list(endpoint_wild_card_read.keys())
234+
asserts.assert_greater(len(list_of_endpoints), 0, "Expecting at least one endpoint with Ecosystem Information Cluster")
147235

148236
for idx, cluster_endpoint in enumerate(list_of_endpoints):
149237
if idx == 0:

src/python_testing/TC_ECOINFO_2_2.py

+112-18
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,110 @@
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+
# script-args: >
27+
# --PICS src/app/tests/suites/certification/ci-pics-values
28+
# --storage-path admin_storage.json
29+
# --commissioning-method on-network
30+
# --discriminator 1234
31+
# --passcode 20202021
32+
# --string-arg th_server_app_path:${ALL_CLUSTERS_APP} dut_fsa_stdin_pipe:dut-fsa-stdin
33+
# --trace-to json:${TRACE_TEST_JSON}.json
34+
# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
35+
# script-start-delay: 5
36+
# factoryreset: true
37+
# quiet: false
38+
# === END CI TEST ARGUMENTS ===
39+
40+
import asyncio
41+
import logging
42+
import os
43+
import random
44+
import tempfile
45+
1846
import chip.clusters as Clusters
1947
from chip.interaction_model import Status
2048
from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main
2149
from mobly import asserts
50+
from TC_MCORE_FS_1_1 import AppServer
2251

2352
_DEVICE_TYPE_AGGREGGATOR = 0x000E
2453

2554

2655
class TC_ECOINFO_2_2(MatterBaseTest):
2756

57+
def setup_class(self):
58+
super().setup_class()
59+
60+
self.th_server = None
61+
self.storage = None
62+
63+
if self.is_pics_sdk_ci_only:
64+
self._setup_ci_prerequisites()
65+
66+
def teardown_class(self):
67+
if self.th_server is not None:
68+
self.th_server.terminate()
69+
if self.storage is not None:
70+
self.storage.cleanup()
71+
super().teardown_class()
72+
73+
def _setup_ci_prerequisites(self):
74+
asserts.assert_true(self.is_pics_sdk_ci_only, "This method is only for PICS SDK CI")
75+
76+
th_server_app = self.user_params.get("th_server_app_path", None)
77+
if not th_server_app:
78+
asserts.fail("CI setup requires a TH_SERVER app. Specify app path with --string-arg th_server_app_path:<path_to_app>")
79+
if not os.path.exists(th_server_app):
80+
asserts.fail(f"The path {th_server_app} does not exist")
81+
82+
# Get the named pipe path for the DUT_FSA app input from the user params.
83+
dut_fsa_stdin_pipe = self.user_params.get("dut_fsa_stdin_pipe")
84+
if not dut_fsa_stdin_pipe:
85+
asserts.fail("CI setup requires --string-arg dut_fsa_stdin_pipe:<path_to_pipe>")
86+
self.dut_fsa_stdin = open(dut_fsa_stdin_pipe, "w")
87+
88+
# Create a temporary storage directory for keeping KVS files.
89+
self.storage = tempfile.TemporaryDirectory(prefix=self.__class__.__name__)
90+
logging.info("Temporary storage directory: %s", self.storage.name)
91+
92+
self.th_server_port = 5544
93+
self.th_server_discriminator = random.randint(0, 4095)
94+
self.th_server_passcode = 20202021
95+
96+
# Start the server app.
97+
self.th_server = AppServer(
98+
th_server_app,
99+
storage_dir=self.storage.name,
100+
port=self.th_server_port,
101+
discriminator=self.th_server_discriminator,
102+
passcode=self.th_server_passcode)
103+
self.th_server.start()
104+
28105
def steps_TC_ECOINFO_2_2(self) -> list[TestStep]:
29-
steps = [TestStep(1, "Prepare", is_commissioning=True),
30-
TestStep("1a", "Read root endpoint's PartsList"),
31-
TestStep("1b", "For each endpoint in 1a read DeviceType list confirming aggregator endpoint exists"),
32-
TestStep(2, "Add a bridged device"),
33-
TestStep("2a", "(Manual Step) Add a bridged device using method indicated by the manufacturer"),
34-
TestStep("2b", "Read root endpoint's PartsList, validate exactly one endpoint added"),
35-
TestStep("2c", "On newly added endpoint detected in 2b read DeviceDirectory Ecosystem Information Attribute and validate success"),
36-
TestStep("2d", "On newly added endpoint detected in 2b read LocationDirectory Ecosystem Information Attribute and validate success"),
37-
TestStep(3, "Remove bridged device"),
38-
TestStep("3a", "(Manual Step) Removed bridged device added in step 2a using method indicated by the manufacturer"),
39-
TestStep("3b", "Verify that PartsList equals what was read in 1a"),
40-
TestStep("3c", "On endpoint detected in 2b, read DeviceDirectory Ecosystem Information Attribute and validate failure"),
41-
TestStep("3d", "On endpoint detected in 2b, read LocationDirectory Ecosystem Information Attribute and validate failure")]
42-
43-
return steps
106+
return [
107+
TestStep(0, "Commission DUT if not done", is_commissioning=True),
108+
TestStep(1, "Prepare"),
109+
TestStep("1a", "Read root endpoint's PartsList"),
110+
TestStep("1b", "For each endpoint in 1a read DeviceType list confirming aggregator endpoint exists"),
111+
TestStep(2, "Add a bridged device"),
112+
TestStep("2a", "(Manual Step) Add a bridged device using method indicated by the manufacturer"),
113+
TestStep("2b", "Read root endpoint's PartsList, validate exactly one endpoint added"),
114+
TestStep("2c", "On newly added endpoint detected in 2b read DeviceDirectory Ecosystem Information Attribute and validate success"),
115+
TestStep("2d", "On newly added endpoint detected in 2b read LocationDirectory Ecosystem Information Attribute and validate success"),
116+
TestStep(3, "Remove bridged device"),
117+
TestStep("3a", "(Manual Step) Removed bridged device added in step 2a using method indicated by the manufacturer"),
118+
TestStep("3b", "Verify that PartsList equals what was read in 1a"),
119+
TestStep("3c", "On endpoint detected in 2b, read DeviceDirectory Ecosystem Information Attribute and validate failure"),
120+
TestStep("3d", "On endpoint detected in 2b, read LocationDirectory Ecosystem Information Attribute and validate failure"),
121+
]
44122

45123
# This test has some manual steps, so we need a longer timeout. Test typically runs under 1 mins so 3 mins should
46124
# be enough time for test to run
@@ -53,7 +131,9 @@ async def test_TC_ECOINFO_2_2(self):
53131
dev_ctrl = self.default_controller
54132
dut_node_id = self.dut_node_id
55133

56-
self.print_step(0, "Commissioning, already done")
134+
# Commissioning - done
135+
self.step(0)
136+
57137
self.step(1)
58138
self.step("1a")
59139
root_node_endpoint = 0
@@ -74,7 +154,14 @@ async def test_TC_ECOINFO_2_2(self):
74154

75155
self.step(2)
76156
self.step("2a")
77-
self.wait_for_user_input(prompt_msg="Add a bridged device using method indicated by the manufacturer")
157+
if not self.is_pics_sdk_ci_only:
158+
self.wait_for_user_input("Add a bridged device using method indicated by the manufacturer")
159+
else:
160+
# Add some server to the DUT_FSA's Aggregator/Bridge.
161+
self.dut_fsa_stdin.write(f"pairing onnetwork 2 {self.th_server_passcode}\n")
162+
self.dut_fsa_stdin.flush()
163+
# Wait for the commissioning to complete.
164+
await asyncio.sleep(5)
78165

79166
self.step("2b")
80167
root_part_list_step_2 = await dev_ctrl.ReadAttribute(dut_node_id, [(root_node_endpoint, Clusters.Descriptor.Attributes.PartsList)])
@@ -106,7 +193,14 @@ async def test_TC_ECOINFO_2_2(self):
106193

107194
self.step(3)
108195
self.step("3a")
109-
self.wait_for_user_input(prompt_msg="Removed bridged device added in step 2a using method indicated by the manufacturer")
196+
if not self.is_pics_sdk_ci_only:
197+
self.wait_for_user_input("Removed bridged device added in step 2a using method indicated by the manufacturer")
198+
else:
199+
# Remove previously added server from the DUT_FSA's Aggregator/Bridge.
200+
self.dut_fsa_stdin.write("pairing unpair 2\n")
201+
self.dut_fsa_stdin.flush()
202+
# Wait for the command to complete.
203+
await asyncio.sleep(2)
110204

111205
self.step("3b")
112206
root_part_list_step_3 = await dev_ctrl.ReadAttribute(dut_node_id, [(root_node_endpoint, Clusters.Descriptor.Attributes.PartsList)])

src/python_testing/TC_MCORE_FS_1_1.py

+18-9
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,22 @@
2121
# for details about the block below.
2222
#
2323
# === BEGIN CI TEST ARGUMENTS ===
24-
# test-runner-runs: run1
25-
# test-runner-run/run1/app: examples/fabric-admin/scripts/fabric-sync-app.py
26-
# test-runner-run/run1/app-args: --app-admin=${FABRIC_ADMIN_APP} --app-bridge=${FABRIC_BRIDGE_APP} --stdin-pipe=dut-fsa-stdin --discriminator=1234
27-
# test-runner-run/run1/factoryreset: true
28-
# test-runner-run/run1/script-args: --PICS src/app/tests/suites/certification/ci-pics-values --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --string-arg th_server_app_path:${ALL_CLUSTERS_APP} --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
29-
# test-runner-run/run1/script-start-delay: 5
30-
# test-runner-run/run1/quiet: true
24+
# test-runner-runs:
25+
# run1:
26+
# app: examples/fabric-admin/scripts/fabric-sync-app.py
27+
# app-args: --app-admin=${FABRIC_ADMIN_APP} --app-bridge=${FABRIC_BRIDGE_APP} --stdin-pipe=dut-fsa-stdin --discriminator=1234
28+
# script-args: >
29+
# --PICS src/app/tests/suites/certification/ci-pics-values
30+
# --storage-path admin_storage.json
31+
# --commissioning-method on-network
32+
# --discriminator 1234
33+
# --passcode 20202021
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+
# script-start-delay: 5
38+
# factoryreset: true
39+
# quiet: false
3140
# === END CI TEST ARGUMENTS ===
3241

3342
import logging
@@ -47,7 +56,7 @@ class AppServer(Subprocess):
4756
"""Wrapper class for starting an application server in a subprocess."""
4857

4958
# Prefix for log messages from the application server.
50-
PREFIX = "[SERVER]"
59+
PREFIX = b"[SERVER]"
5160

5261
def __init__(self, app: str, storage_dir: str, discriminator: int, passcode: int, port: int = 5540):
5362
storage_kvs_dir = tempfile.mkstemp(dir=storage_dir, prefix="kvs-app-")[1]
@@ -56,7 +65,7 @@ def __init__(self, app: str, storage_dir: str, discriminator: int, passcode: int
5665
'--secured-device-port', str(port),
5766
"--discriminator", str(discriminator),
5867
"--passcode", str(passcode),
59-
prefix=self.PREFIX)
68+
output_cb=lambda line, is_stderr: self.PREFIX + line)
6069

6170
def start(self):
6271
# Start process and block until it prints the expected output.

0 commit comments

Comments
 (0)