Skip to content

Commit 702a7fe

Browse files
committed
Partial functionality implemented, but remaining steps (TODO) until the border router is configured
1 parent 4593aef commit 702a7fe

File tree

1 file changed

+355
-0
lines changed

1 file changed

+355
-0
lines changed

src/python_testing/TC_CNET_4_12.py

+355
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,355 @@
1+
#
2+
# Copyright (c) 2025 Project CHIP Authors
3+
# All rights reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
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: ${ALL_CLUSTERS_APP}
25+
# app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json
26+
# script-args: >
27+
# --storage-path admin_storage.json
28+
# --commissioning-method on-network
29+
# --discriminator 1234
30+
# --passcode 20202021
31+
# --PICS src/app/tests/suites/certification/ci-pics-values
32+
# --string-arg PIXIT.CNET.THREAD_1ST_OPERATIONALDATASET:${THREAD_1ST}
33+
# --string-arg PIXIT.CNET.THREAD_2ND_OPERATIONALDATASET:${THREAD_2ND}
34+
# --trace-to json:${TRACE_TEST_JSON}.json
35+
# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
36+
# factory-reset: true
37+
# quiet: true
38+
# === END CI TEST ARGUMENTS ===
39+
40+
import logging
41+
42+
import chip.clusters as Clusters
43+
from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main
44+
from mobly import asserts
45+
46+
logger = logging.getLogger(__name__)
47+
48+
49+
class TC_CNET_4_12(MatterBaseTest):
50+
51+
CLUSTER_CNET = Clusters.NetworkCommissioning
52+
CLUSTER_DESC = Clusters.Descriptor
53+
CLUSTER_CGEN = Clusters.GeneralCommissioning
54+
failsafe_expiration_seconds = 900
55+
default_network_id = 'Thread'
56+
57+
def def_TC_CNET_4_12(self):
58+
return '[TC-CNET-4.12] [Thread] Verification for ConnectNetwork Command [DUT-Server]'
59+
60+
def pics_TC_CNET_4_12(self):
61+
"""Return the PICS definitions associated with this test."""
62+
pics = [
63+
"CNET.S"
64+
]
65+
return pics
66+
67+
def steps_TC_CNET_4_12(self) -> list[TestStep]:
68+
steps = [
69+
TestStep('precondition-1', 'DUT supports CNET.S.F01(TH)'),
70+
TestStep('precondition-2', 'DUT has a Network Commissioning cluster on endpoint PIXIT.CNET.ENDPOINT_THREAD with FeatureMap attribute of 2', is_commissioning=True),
71+
TestStep('precondition-3', 'DUT is commissioned on PIXIT.CNET.THREAD_1ST_OPERATIONALDATASET'),
72+
TestStep('precondition-4', 'TH has can communicate to two valid thread PANs: PIXIT.CNET.THREAD_1ST_OPERATIONALDATASET and PIXIT.CNET.THREAD_2ND_OPERATIONALDATASET'),
73+
TestStep('precondition-5', 'XPANID of PIXIT.CNET.THREAD_1ST_OPERATIONALDATASET is saved as th_xpan and the XPANID of PIXIT.CNET.THREAD_2ND_OPERATIONALDATASET is saved as th_xpan1'),
74+
TestStep(1, 'TH sends ArmFailSafe command to the DUT with ExpiryLengthSeconds set to 900'),
75+
TestStep(2, 'TH reads Networks attribute from the DUT and saves the number of entries as NumNetworks'),
76+
TestStep(3, 'TH saves the index of the Networks list entry from step 2 as Userth_netidx'),
77+
TestStep(4, '''TH sends RemoveNetwork Command to the DUT
78+
with NetworkID field set to th_xpan
79+
and Breadcrumb field set to 1'''),
80+
TestStep(5, '''TH sends AddOrUpdateThreadNetwork command to the DUT
81+
with operational dataset field set to PIXIT.CNET.THREAD_2ND_OPERATIONALDATASET
82+
and Breadcrumb field set to 1'''),
83+
TestStep(6, 'TH reads Networks attribute from the DUT'),
84+
TestStep(7, '''TH sends ConnectNetwork command to the DUT
85+
with NetworkID field set to PIXIT.CNET.THREAD_2ND_OPERATIONALDATASET
86+
and Breadcrumb field set to 2'''),
87+
TestStep(8, 'TH discovers and connects to DUT on the PIXIT.CNET.THREAD_2ND_OPERATIONALDATASET operational network'),
88+
TestStep(9, 'TH reads Breadcrumb attribute from the General Commissioning cluster of the DUT'),
89+
TestStep(10, 'TH sends ArmFailSafe command to the DUT with ExpiryLengthSeconds set to 0'),
90+
TestStep(11, 'TH ensures it can communicate on PIXIT.CNET.THREAD_1ST_OPERATIONALDATASET'),
91+
TestStep(12, 'TH discovers and connects to DUT on the PIXIT.CNET.THREAD_1ST_OPERATIONALDATASET operational network'),
92+
TestStep(13, 'TH sends ArmFailSafe command to the DUT with ExpiryLengthSeconds set to 900'),
93+
TestStep(14, '''TH sends RemoveNetwork Command to the DUT with NetworkID field set to th_xpan
94+
and Breadcrumb field set to 1'''),
95+
TestStep(15, '''TH sends AddOrUpdateThreadNetwork command to the DUT
96+
with the OperationalDataset field set to PIXIT.CNET.THREAD_2ND_OPERATIONALDATASET
97+
and Breadcrumb field set to 1'''),
98+
TestStep(16, '''TH sends ConnectNetwork command to the DUT
99+
with NetworkID field set to the extended PAN ID of PIXIT.CNET.THREAD_2ND_OPERATIONALDATASET
100+
and Breadcrumb field set to 3'''),
101+
TestStep(17, 'TH discovers and connects to DUT on the PIXIT.CNET.THREAD_2ND_OPERATIONALDATASET operational network'),
102+
TestStep(18, 'TH reads Breadcrumb attribute from the General Commissioning cluster of the DUT'),
103+
TestStep(19, 'TH sends the CommissioningComplete command to the DUT'),
104+
TestStep(20, 'TH reads Networks attribute from the DUT')
105+
]
106+
return steps
107+
108+
@async_test_body
109+
async def test_TC_CNET_4_12(self):
110+
111+
if self.is_pics_sdk_ci_only:
112+
logger.info('Test is not running in CI.')
113+
self.skip_all_remaining_steps('precondition-1')
114+
return
115+
116+
# Pre-Conditions
117+
self.step('precondition-1')
118+
self.step('precondition-2')
119+
# By running this test from the terminal, it commissions the device.
120+
logger.info('Pre-Conditions #1: DUT has a Network Commissioning cluster on endpoint PIXIT.CNET.ENDPOINT_THREAD ')
121+
122+
# The FeatureMap attribute value is 2
123+
feature_map = await self.read_single_attribute_check_success(
124+
cluster=self.CLUSTER_CNET,
125+
attribute=self.CLUSTER_CNET.Attributes.FeatureMap)
126+
asserts.assert_true(feature_map == 2,
127+
msg="Verify that feature_map is equal to 1")
128+
logger.info(f'Pre-Conditions #3: The FeatureMap attribute value is: {feature_map}')
129+
130+
self.step('precondition-3')
131+
# TODO: Implement precondition-3
132+
self.step('precondition-4')
133+
# TODO: Implement precondition-4
134+
self.step('precondition-5')
135+
# TODO: Implement precondition-5
136+
th_xpan = self.user_params.get('PIXIT.CNET.THREAD_1ST_OPERATIONALDATASET', self.default_network_id)
137+
th_xpan_1 = self.user_params.get('PIXIT.CNET.THREAD_2ND_OPERATIONALDATASET', self.default_network_id)
138+
139+
# Steps
140+
141+
self.step(1)
142+
cmd = Clusters.GeneralCommissioning.Commands.ArmFailSafe(expiryLengthSeconds=self.failsafe_expiration_seconds, breadcrumb=1)
143+
resp = await self.send_single_cmd(
144+
dev_ctrl=self.default_controller,
145+
node_id=self.dut_node_id,
146+
cmd=cmd
147+
)
148+
# Verify that the DUT responds with ArmFailSafeResponse with ErrorCode as 'OK'(0)
149+
asserts.assert_equal(resp.errorCode, Clusters.GeneralCommissioning.Enums.CommissioningErrorEnum.kOk,
150+
"Failure status returned from arm failsafe")
151+
logger.info(f'Step #1b - ArmFailSafeResponse with ErrorCode as OK({resp.errorCode})')
152+
153+
self.step(2)
154+
networks = await self.read_single_attribute_check_success(
155+
cluster=Clusters.NetworkCommissioning,
156+
attribute=Clusters.NetworkCommissioning.Attributes.Networks
157+
)
158+
logger.info(f'Step #2: Networks attribute: {networks}')
159+
160+
num_networks = len(networks)
161+
logger.info(f'Step #2: Number of Networks entries (NumNetworks): {num_networks}')
162+
asserts.assert_true(num_networks > 0, "Error: No networks found")
163+
164+
# TODO: Implement proper validation to verify the the Networks attribute "NetworkID" and "Connected"
165+
for cnet in networks:
166+
if cnet.networkID.decode('utf-8') == th_xpan and cnet.connected:
167+
network_found = True
168+
break
169+
logger.info(f'Step #2: Found network with ID {th_xpan} and connected={network_found}.')
170+
asserts.assert_true(
171+
network_found, f"Error: Network with ID {th_xpan} and connected=True not found.")
172+
173+
self.step(3)
174+
# TODO: Implement proper validation to verify the the Networks index
175+
userth_netidx = None
176+
for index, network in enumerate(networks):
177+
if network['networkID'] == th_xpan:
178+
userth_netidx = index
179+
break
180+
asserts.assert_true(userth_netidx is not None, "")
181+
logger.info(f'Step #3: Networks attribute: {userth_netidx}')
182+
183+
self.step(4)
184+
cmd = Clusters.NetworkCommissioning.Commands.RemoveNetwork(networkID=th_xpan, breadcrumb=1)
185+
resp = await self.send_single_cmd(
186+
dev_ctrl=self.default_controller,
187+
node_id=self.dut_node_id,
188+
cmd=cmd
189+
)
190+
network_index = resp.networkIndex
191+
logger.info(f'Step #4: RemoveNetwork Status is success ({resp.networkingStatus})')
192+
logger.info(f'Step #4: Network index: ({network_index})')
193+
194+
# Verify that the DUT responds with Remove Network with NetworkingStatus as 'Success'(0)
195+
asserts.assert_equal(resp.networkingStatus, Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatusEnum.kSuccess,
196+
"Failure status returned from ReordeRemove Network")
197+
asserts.assert_equal(network_index, 0, "The network index is not as expected.")
198+
199+
self.step(5)
200+
cmd = Clusters.NetworkCommissioning.Commands.AddOrUpdateThreadNetwork(operationalDataset=th_xpan_1, breadcrumb=1)
201+
resp = await self.send_single_cmd(
202+
dev_ctrl=self.default_controller,
203+
node_id=self.dut_node_id,
204+
cmd=cmd
205+
)
206+
logger.info(f'Step #5: AddOrUpdateThreadNetwork Status is success ({resp.networkingStatus})')
207+
# Verify that the DUT responds with AddThreadNetwork with NetworkingStatus as 'Success'(0)
208+
asserts.assert_equal(resp.networkingStatus, Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatusEnum.kSuccess,
209+
"Failure status returned from AddThreadNetwork")
210+
debug_text = resp.debugText
211+
# TODO: Check if None is part of the validation
212+
asserts.assert_true(debug_text is None or debug_text == '' or len(debug_text) <= 512,
213+
"debugText must be None, empty or have a maximum length of 512 characters.")
214+
215+
self.step(6)
216+
networks = await self.read_single_attribute_check_success(
217+
cluster=Clusters.NetworkCommissioning,
218+
attribute=Clusters.NetworkCommissioning.Attributes.Networks
219+
)
220+
logger.info(f'Step #6: Networks attribute: {networks}')
221+
# TODO; Implement the Verify that the Networks attribute list has an entry NetworkID=th_xpan, Connected=FALSE
222+
# TODO: Why th_xpan? should be th_xpan_1
223+
224+
self.step(7)
225+
cmd = Clusters.NetworkCommissioning.Commands.ConnectNetwork(operationalDataset=th_xpan_1, breadcrumb=2)
226+
resp = await self.send_single_cmd(
227+
dev_ctrl=self.default_controller,
228+
node_id=self.dut_node_id,
229+
cmd=cmd
230+
)
231+
logger.info(f'Step #7: ConnectNetwork Status is success ({resp.networkingStatus})')
232+
# Verify that the DUT responds with AddThreadNConnectNetworketwork with NetworkingStatus as 'Success'(0)
233+
asserts.assert_equal(resp.networkingStatus, Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatusEnum.kSuccess,
234+
"Failure status returned from ConnectNetwork")
235+
236+
self.step(8)
237+
# TODO: Verify that the TH successfully connects to the DUT from previous step
238+
239+
self.step(9)
240+
breadcrumb_info = await self.read_single_attribute_check_success(
241+
cluster=Clusters.GeneralCommissioning,
242+
attribute=Clusters.GeneralCommissioning.Attributes.Breadcrumb
243+
)
244+
logger.info(f'Step #9: Breadcrumb attribute: {breadcrumb_info}')
245+
asserts.assert_equal(breadcrumb_info, 1,
246+
"The Breadcrumb attribute is not 1")
247+
248+
self.step(10)
249+
cmd = Clusters.GeneralCommissioning.Commands.ArmFailSafe(expiryLengthSeconds=0)
250+
resp = await self.send_single_cmd(
251+
dev_ctrl=self.default_controller,
252+
node_id=self.dut_node_id,
253+
cmd=cmd
254+
)
255+
# Verify that the DUT responds with ArmFailSafeResponse with ErrorCode as 'OK'(0)
256+
asserts.assert_equal(resp.errorCode, Clusters.GeneralCommissioning.Enums.CommissioningErrorEnum.kOk,
257+
"Failure status returned from arm failsafe")
258+
logger.info(f'Step #10 - ArmFailSafeResponse with ErrorCode as OK({resp.errorCode})')
259+
260+
self.step(11)
261+
# TODO: Verify that TH can communicate on th_xpan
262+
263+
self.step(12)
264+
# TODO: Verify that TH can discover and connect on th_xpan
265+
266+
self.step(13)
267+
cmd = Clusters.GeneralCommissioning.Commands.ArmFailSafe(expiryLengthSeconds=self.failsafe_expiration_seconds)
268+
resp = await self.send_single_cmd(
269+
dev_ctrl=self.default_controller,
270+
node_id=self.dut_node_id,
271+
cmd=cmd
272+
)
273+
# Verify that the DUT responds with ArmFailSafeResponse with ErrorCode as 'OK'(0)
274+
asserts.assert_equal(resp.errorCode, Clusters.GeneralCommissioning.Enums.CommissioningErrorEnum.kOk,
275+
"Failure status returned from arm failsafe")
276+
logger.info(f'Step #1b - ArmFailSafeResponse with ErrorCode as OK({resp.errorCode})')
277+
278+
self.step(14)
279+
cmd = Clusters.NetworkCommissioning.Commands.RemoveNetwork(networkID=th_xpan, breadcrumb=1)
280+
resp = await self.send_single_cmd(
281+
dev_ctrl=self.default_controller,
282+
node_id=self.dut_node_id,
283+
cmd=cmd
284+
)
285+
network_index = resp.networkIndex
286+
logger.info(f'Step #14: RemoveNetwork Status is success ({resp.networkingStatus})')
287+
logger.info(f'Step #14: Network index: ({network_index})')
288+
289+
# Verify that the DUT responds with Remove Network with NetworkingStatus as 'Success'(0)
290+
asserts.assert_equal(resp.networkingStatus, Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatusEnum.kSuccess,
291+
"Failure status returned from ReordeRemove Network")
292+
asserts.assert_equal(network_index, 0, "The network index is not as expected.")
293+
294+
self.step(15)
295+
cmd = Clusters.NetworkCommissioning.Commands.AddOrUpdateThreadNetwork(operationalDataset=th_xpan_1, breadcrumb=3)
296+
resp = await self.send_single_cmd(
297+
dev_ctrl=self.default_controller,
298+
node_id=self.dut_node_id,
299+
cmd=cmd
300+
)
301+
logger.info(f'Step #15: AddOrUpdateThreadNetwork Status is success ({resp.networkingStatus})')
302+
# Verify that the DUT responds with AddThreadNetwork with NetworkingStatus as 'Success'(0)
303+
asserts.assert_equal(resp.networkingStatus, Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatusEnum.kSuccess,
304+
"Failure status returned from AddThreadNetwork")
305+
debug_text = resp.debugText
306+
# TODO: Check if None is part of the validation
307+
asserts.assert_true(debug_text is None or debug_text == '' or len(debug_text) <= 512,
308+
"debugText must be None, empty or have a maximum length of 512 characters.")
309+
310+
self.step(16)
311+
cmd = Clusters.NetworkCommissioning.Commands.ConnectNetwork(operationalDataset=th_xpan_1, breadcrumb=3)
312+
resp = await self.send_single_cmd(
313+
dev_ctrl=self.default_controller,
314+
node_id=self.dut_node_id,
315+
cmd=cmd
316+
)
317+
logger.info(f'Step #16: ConnectNetwork Status is success ({resp.networkingStatus})')
318+
# Verify that the DUT responds with AddThreadNConnectNetworketwork with NetworkingStatus as 'Success'(0)
319+
asserts.assert_equal(resp.networkingStatus, Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatusEnum.kSuccess,
320+
"Failure status returned from ConnectNetwork")
321+
322+
self.step(17)
323+
# TODO: Verify that the TH successfully connects to the DUT from previous step
324+
325+
self.step(18)
326+
breadcrumb_info = await self.read_single_attribute_check_success(
327+
cluster=Clusters.GeneralCommissioning,
328+
attribute=Clusters.GeneralCommissioning.Attributes.Breadcrumb
329+
)
330+
logger.info(f'Step #18: Breadcrumb attribute: {breadcrumb_info}')
331+
asserts.assert_equal(breadcrumb_info, 3,
332+
"The Breadcrumb attribute is not 3")
333+
334+
self.step(19)
335+
# TODO: Implement TH sends the CommissioningComplete and CommissioningCompleteResponse with the ErrorCode OK (0)
336+
337+
self.step(20)
338+
networks = await self.read_single_attribute_check_success(
339+
cluster=Clusters.NetworkCommissioning,
340+
attribute=Clusters.NetworkCommissioning.Attributes.Networks
341+
)
342+
logger.info(f'Step #2: Networks attribute: {networks}')
343+
344+
# TODO: Implement proper validation to verify the the Networks attribute "NetworkID" and "Connected"
345+
for cnet in networks:
346+
if cnet.networkID.decode('utf-8') == th_xpan_1 and cnet.connected:
347+
network_found = True
348+
break
349+
logger.info(f'Step #2: Found network with ID {th_xpan_1} and connected={network_found}.')
350+
asserts.assert_true(
351+
network_found, f"Error: Network with ID {th_xpan_1} and connected=True not found.")
352+
353+
354+
if __name__ == "__main__":
355+
default_matter_test_main()

0 commit comments

Comments
 (0)