Skip to content

Commit 37efad3

Browse files
swan-amazongmarcosb
authored andcommitted
test: Implement TC acknowledgement test cases (project-chip#37015)
* test: Implement TC acknowledgement test cases 2.5-2.8 Implements test cases for Terms & Conditions acknowledgement verification: - TC-*-2.5: SetTCAcknowledgements verification - TC-*-2.6: CommissioningComplete with no terms accepted - TC-*-2.7: CommissioningComplete with invalid terms - TC-*-2.8: TCAcknowledgements reset after Factory Reset Remaining test cases to be implemented in follow-up changes: - TC-*-2.9: Reset after fabric removal - TC-*-2.10: Required terms validation - TC-*-2.11: Post-commission updates Testing: Test cases implemented according to test plan specifications. Each test verifies specific TC acknowledgement behaviors. * test: Implement remaining TC acknowledgement test cases 2.9-2.11 Implements remaining test cases for Terms & Conditions acknowledgement verification: - TC-*-2.9: TCAcknowledgements reset after fabric removal - TC-*-2.10: Required terms validation - TC-*-2.11: Post-commission TC updates This completes the test coverage for TC acknowledgement verification, following up on the previous implementation of test cases 2.5-2.8. The new test cases verify: - TC state after fabric removal - Protection of required terms - Ability to update TC version and acknowledgements post-commissioning Testing: Test cases implemented according to test plan specifications. Each test verifies specific TC acknowledgement behaviors. * wip * feat(testing): Add PICS guard for Terms & Conditions test steps - Wrap test steps in PICS guard checks for "CGEN.S" and "CGEN.S.F00(TC)" - Ensure test steps only execute when Terms & Conditions feature flag is enabled - Maintain test logic while adding conditional execution based on PICS support * Added PICS and PIXIT definitions to src/app/tests/suites/certification/PICS.yaml * refactor: Extract commission_devices functionality into reusable methods Move commissioning logic from CommissionDeviceTest class into standalone functions to improve code reusability. Introduce CommissioningInfo dataclass to encapsulate commissioning parameters and add commission_device/ commission_devices helper functions. The changes: - Move SetupPayloadInfo dataclass to module level - Add new CommissioningInfo dataclass for commissioning parameters - Extract commission_device and commission_devices as standalone async functions - Add commission_devices method to MatterBaseTest class - Maintain backward compatibility with existing usage in CommissionDeviceTest * Addressed PR comments * Improve test assertions and manual testing steps - Use Matter-specific type assertions in TC_CGEN_2_5 - Update TC_CGEN_2_8 manual testing flow and prompts * restyle * refactor: simplify CGEN feature identifier from CGEN.S.F00(TC) to CGEN.S.F00 - Update PICS.yaml and ci-pics-values to use simplified feature ID - Update Python test files to use new feature identifier - Improve error messaging when commissioning feature check fails - Affected test files: TC_CGEN_2_5 through TC_CGEN_2_11 * Update TC_CGEN_2_5 to use Nullable type for TCUpdateDeadline validation * Add type validation for TCAcceptedVersion and TCAcknowledgements
1 parent a6114f9 commit 37efad3

File tree

14 files changed

+1303
-87
lines changed

14 files changed

+1303
-87
lines changed

.github/workflows/tests.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ jobs:
503503
--target linux-x64-fabric-bridge-rpc-ipv6only-no-ble-no-wifi-clang \
504504
--target linux-x64-fabric-sync-ipv6only-no-ble-no-wifi-clang \
505505
--target linux-x64-light-data-model-no-unique-id-ipv6only-no-ble-no-wifi-clang \
506+
--target linux-x64-terms-and-conditions \
506507
--target linux-x64-python-bindings \
507508
build \
508509
--copy-artifacts-to objdir-clone \
@@ -521,6 +522,7 @@ jobs:
521522
echo "FABRIC_BRIDGE_APP: out/linux-x64-fabric-bridge-rpc-ipv6only-no-ble-no-wifi-clang/fabric-bridge-app" >> /tmp/test_env.yaml
522523
echo "FABRIC_SYNC_APP: out/linux-x64-fabric-sync-ipv6only-no-ble-no-wifi-clang/fabric-sync" >> /tmp/test_env.yaml
523524
echo "LIGHTING_APP_NO_UNIQUE_ID: out/linux-x64-light-data-model-no-unique-id-ipv6only-no-ble-no-wifi-clang/chip-lighting-app" >> /tmp/test_env.yaml
525+
echo "TERMS_AND_CONDITIONS_APP: out/linux-x64-terms-and-conditions/chip-terms-and-conditions-app" >> /tmp/test_env.yaml
524526
echo "TRACE_APP: out/trace_data/app-{SCRIPT_BASE_NAME}" >> /tmp/test_env.yaml
525527
echo "TRACE_TEST_JSON: out/trace_data/test-{SCRIPT_BASE_NAME}" >> /tmp/test_env.yaml
526528
echo "TRACE_TEST_PERFETTO: out/trace_data/test-{SCRIPT_BASE_NAME}" >> /tmp/test_env.yaml

integrations/docker/images/chip-cert-bins/Dockerfile

+5
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ RUN case ${TARGETPLATFORM} in \
168168
--target linux-x64-fabric-admin-rpc-ipv6only \
169169
--target linux-x64-light-data-model-no-unique-id-ipv6only \
170170
--target linux-x64-network-manager-ipv6only \
171+
--target linux-x64-terms-and-conditions \
171172
build \
172173
&& mv out/linux-x64-chip-tool-ipv6only-platform-mdns/chip-tool out/chip-tool \
173174
&& mv out/linux-x64-shell-ipv6only-platform-mdns/chip-shell out/chip-shell \
@@ -192,6 +193,7 @@ RUN case ${TARGETPLATFORM} in \
192193
&& mv out/linux-x64-fabric-admin-rpc-ipv6only/fabric-admin out/fabric-admin \
193194
&& mv out/linux-x64-light-data-model-no-unique-id-ipv6only/chip-lighting-app out/chip-lighting-data-model-no-unique-id-app \
194195
&& mv out/linux-x64-network-manager-ipv6only/matter-network-manager-app out/matter-network-manager-app \
196+
&& mv out/linux-x64-terms-and-conditions/chip-terms-and-conditions-app out/chip-terms-and-conditions-app \
195197
;; \
196198
"linux/arm64")\
197199
set -x \
@@ -220,6 +222,7 @@ RUN case ${TARGETPLATFORM} in \
220222
--target linux-arm64-fabric-admin-rpc-ipv6only \
221223
--target linux-arm64-light-data-model-no-unique-id-ipv6only \
222224
--target linux-arm64-network-manager-ipv6only \
225+
--target linux-arm64-terms-and-conditions \
223226
build \
224227
&& mv out/linux-arm64-chip-tool-ipv6only-platform-mdns/chip-tool out/chip-tool \
225228
&& mv out/linux-arm64-shell-ipv6only-platform-mdns/chip-shell out/chip-shell \
@@ -244,6 +247,7 @@ RUN case ${TARGETPLATFORM} in \
244247
&& mv out/linux-arm64-fabric-admin-rpc-ipv6only/fabric-admin out/fabric-admin \
245248
&& mv out/linux-arm64-light-data-model-no-unique-id-ipv6only/chip-lighting-app out/chip-lighting-data-model-no-unique-id-app \
246249
&& mv out/linux-arm64-network-manager-ipv6only/matter-network-manager-app out/matter-network-manager-app \
250+
&& mv out/linux-arm64-terms-and-conditions/chip-terms-and-conditions-app out/chip-terms-and-conditions-app \
247251
;; \
248252
*) ;; \
249253
esac
@@ -283,6 +287,7 @@ COPY --from=chip-build-cert-bins /root/connectedhomeip/out/fabric-bridge-app app
283287
COPY --from=chip-build-cert-bins /root/connectedhomeip/out/fabric-admin apps/fabric-admin
284288
COPY --from=chip-build-cert-bins /root/connectedhomeip/out/chip-lighting-data-model-no-unique-id-app apps/chip-lighting-data-model-no-unique-id-app
285289
COPY --from=chip-build-cert-bins /root/connectedhomeip/out/matter-network-manager-app apps/matter-network-manager-app
290+
COPY --from=chip-build-cert-bins /root/connectedhomeip/out/chip-terms-and-conditions-app apps/chip-terms-and-conditions-app
286291

287292
# Create symbolic links for now since this allows users to use existing configurations
288293
# for running just `app-name` instead of `apps/app-name`

scripts/build/builders/host.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,8 @@ def OutputNames(self):
282282
yield 'water-leak-detector-app'
283283
yield 'water-leak-detector-app.map'
284284
elif self == HostApp.TERMS_AND_CONDITIONS:
285-
yield 'terms-and-conditions-app'
286-
yield 'terms-and-conditions-app.map'
285+
yield 'chip-terms-and-conditions-app'
286+
yield 'chip-terms-and-conditions-app.map'
287287
else:
288288
raise Exception('Unknown app type: %r' % self)
289289

scripts/tests/local.py

+1
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@ def as_runner(path):
387387
FABRIC_SYNC_APP: {
388388
as_runner(f'out/{target_prefix}-fabric-sync-no-ble-no-wifi-ipv6only-clang-boringssl/fabric-sync')}
389389
LIGHTING_APP_NO_UNIQUE_ID: {as_runner(f'out/{target_prefix}-light-data-model-no-unique-id-ipv6only-no-ble-no-wifi-clang/chip-lighting-app')}
390+
TERMS_AND_CONDITIONS_APP: {as_runner(f'out/{target_prefix}-terms-and-conditions/chip-terms-and-conditions-app')}
390391
TRACE_APP: out/trace_data/app-{{SCRIPT_BASE_NAME}}
391392
TRACE_TEST_JSON: out/trace_data/test-{{SCRIPT_BASE_NAME}}
392393
TRACE_TEST_PERFETTO: out/trace_data/test-{{SCRIPT_BASE_NAME}}

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

+17
Original file line numberDiff line numberDiff line change
@@ -3632,6 +3632,23 @@ PICS:
36323632
CommissioningCompleteResponse command?"
36333633
id: CGEN.S.C05.Tx
36343634

3635+
#
3636+
# server / features
3637+
#
3638+
- label:
3639+
"Does the device implement the General Commissioning cluster's terms
3640+
and conditions feature?"
3641+
id: CGEN.S.F00
3642+
3643+
- label: "The device's failsafe expiration limit."
3644+
id: PIXIT.CGEN.FailsafeExpiryLengthSeconds
3645+
3646+
- label: "The device's required terms and conditions acknowledgements."
3647+
id: PIXIT.CGEN.RequiredTCAcknowledgements
3648+
3649+
- label: "The device's required minimum terms and conditions revision."
3650+
id: PIXIT.CGEN.TCRevision
3651+
36353652
# General Diagnostics Cluster Test Plan
36363653
- label:
36373654
"Does the device implement the General Diagnostics cluster as a

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

+8-1
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,7 @@ SWTCH.C.AO-READ=0
729729
SWTCH.C.AO-WRITE=0
730730

731731
# General Commissioning Cluster
732+
CGEN.C=1
732733
CGEN.S=1
733734
CGEN.S.A0000=1
734735
CGEN.S.A0001=1
@@ -742,7 +743,13 @@ CGEN.S.C03.Tx=1
742743
CGEN.S.C04.Rsp=1
743744
CGEN.S.C05.Tx=1
744745

745-
CGEN.C=1
746+
#Feature
747+
CGEN.S.F00=1
748+
749+
#PIXIT
750+
PIXIT.CGEN.FailsafeExpiryLengthSeconds=0
751+
PIXIT.CGEN.RequiredTCAcknowledgements=1
752+
PIXIT.CGEN.TCRevision=1
746753

747754
# LAUNDRY WASHER MODE CLUSTER
748755
LWM.S=1

src/python_testing/TC_CGEN_2_10.py

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
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+
# === BEGIN CI TEST ARGUMENTS ===
19+
# test-runner-runs:
20+
# run1:
21+
# app: ${TERMS_AND_CONDITIONS_APP}
22+
# app-args: >
23+
# --tc-min-required-version 1
24+
# --tc-required-acknowledgements 1
25+
# --custom-flow 2
26+
# --capabilities 6
27+
# script-args:
28+
# --PICS src/app/tests/suites/certification/ci-pics-values
29+
# --in-test-commissioning-method on-network
30+
# --tc-version-to-simulate 1
31+
# --tc-user-response-to-simulate 1
32+
# --qr-code MT:-24J0AFN00KA0648G00
33+
# --trace-to json:log
34+
# factoryreset: True
35+
# quiet: True
36+
# === END CI TEST ARGUMENTS ===
37+
38+
import chip.clusters as Clusters
39+
from chip import ChipDeviceCtrl
40+
from chip.commissioning import ROOT_ENDPOINT_ID
41+
from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main
42+
from mobly import asserts
43+
44+
45+
class TC_CGEN_2_10(MatterBaseTest):
46+
def desc_TC_CGEN_2_10(self) -> str:
47+
return "[TC-CGEN-2.10] Verification that required terms can't be unset from TCAcknowledgements with SetTCAcknowledgements [DUT as Server]"
48+
49+
def pics_TC_CGEN_2_10(self) -> list[str]:
50+
""" This function returns a list of PICS for this test case that must be True for the test to be run"""
51+
return ["CGEN.S", "CGEN.S.F00"]
52+
53+
def steps_TC_CGEN_2_10(self) -> list[TestStep]:
54+
return [
55+
TestStep(0, description="", expectation="", is_commissioning=False),
56+
TestStep(1, "TH reads from the DUT the attribute TCAcceptedVersion. Store the value as acceptedVersion."),
57+
TestStep(2, "TH reads from the DUT the attribute TCAcknowledgements. Store the value as userAcknowledgements."),
58+
TestStep(3, "TH Sends the SetTCAcknowledgements command to the DUT with the fields set as follows:\n* TCVersion: 0\n* TCUserResponse: 65535"),
59+
TestStep(4, "TH reads from the DUT the attribute TCAcceptedVersion."),
60+
TestStep(5, "TH reads from the DUT the attribute TCAcknowledgements."),
61+
TestStep(6, "TH Sends the SetTCAcknowledgements command to the DUT with the fields set as follows:\n* TCVersion: acceptedVersion + 1\n* TCUserResponse: 0"),
62+
TestStep(7, "TH reads from the DUT the attribute TCAcceptedVersion."),
63+
TestStep(8, "TH reads from the DUT the attribute TCAcknowledgements."),
64+
]
65+
66+
@async_test_body
67+
async def test_TC_CGEN_2_10(self):
68+
commissioner: ChipDeviceCtrl.ChipDeviceController = self.default_controller
69+
70+
self.step(0)
71+
if not self.check_pics("CGEN.S.F00"):
72+
asserts.skip('Root endpoint does not support the [commissioning] feature under test')
73+
return
74+
75+
# Step 1: Begin commissioning with PASE and failsafe
76+
commissioner.SetSkipCommissioningComplete(True)
77+
self.matter_test_config.commissioning_method = self.matter_test_config.in_test_commissioning_method
78+
self.matter_test_config.tc_version_to_simulate = None
79+
self.matter_test_config.tc_user_response_to_simulate = None
80+
await self.commission_devices()
81+
82+
# Step 1: Read TCAcceptedVersion
83+
self.step(1)
84+
response = await commissioner.ReadAttribute(nodeid=self.dut_node_id, attributes=[(ROOT_ENDPOINT_ID, Clusters.GeneralCommissioning.Attributes.TCAcceptedVersion)])
85+
accepted_version = response[ROOT_ENDPOINT_ID][Clusters.GeneralCommissioning][Clusters.GeneralCommissioning.Attributes.TCAcceptedVersion]
86+
87+
# Step 2: Read TCAcknowledgements
88+
self.step(2)
89+
response = await commissioner.ReadAttribute(nodeid=self.dut_node_id, attributes=[(ROOT_ENDPOINT_ID, Clusters.GeneralCommissioning.Attributes.TCAcknowledgements)])
90+
user_acknowledgements = response[ROOT_ENDPOINT_ID][Clusters.GeneralCommissioning][Clusters.GeneralCommissioning.Attributes.TCAcknowledgements]
91+
92+
# Step 3: Send SetTCAcknowledgements with invalid version
93+
self.step(3)
94+
response = await commissioner.SendCommand(
95+
nodeid=self.dut_node_id,
96+
endpoint=ROOT_ENDPOINT_ID,
97+
payload=Clusters.GeneralCommissioning.Commands.SetTCAcknowledgements(TCVersion=0, TCUserResponse=65535),
98+
)
99+
100+
# Verify TCMinVersionNotMet error
101+
asserts.assert_equal(
102+
response.errorCode,
103+
Clusters.GeneralCommissioning.Enums.CommissioningErrorEnum.kTCMinVersionNotMet,
104+
"Expected TCMinVersionNotMet error",
105+
)
106+
107+
# Step 4: Verify TCAcceptedVersion unchanged
108+
self.step(4)
109+
response = await commissioner.ReadAttribute(nodeid=self.dut_node_id, attributes=[(ROOT_ENDPOINT_ID, Clusters.GeneralCommissioning.Attributes.TCAcceptedVersion)])
110+
current_version = response[ROOT_ENDPOINT_ID][Clusters.GeneralCommissioning][Clusters.GeneralCommissioning.Attributes.TCAcceptedVersion]
111+
asserts.assert_equal(current_version, accepted_version, "TCAcceptedVersion changed unexpectedly")
112+
113+
# Step 5: Verify TCAcknowledgements unchanged
114+
self.step(5)
115+
response = await commissioner.ReadAttribute(nodeid=self.dut_node_id, attributes=[(ROOT_ENDPOINT_ID, Clusters.GeneralCommissioning.Attributes.TCAcknowledgements)])
116+
current_acknowledgements = response[ROOT_ENDPOINT_ID][Clusters.GeneralCommissioning][Clusters.GeneralCommissioning.Attributes.TCAcknowledgements]
117+
asserts.assert_equal(current_acknowledgements, user_acknowledgements, "TCAcknowledgements changed unexpectedly")
118+
119+
# Step 6: Send SetTCAcknowledgements with invalid response
120+
self.step(6)
121+
response = await commissioner.SendCommand(
122+
nodeid=self.dut_node_id,
123+
endpoint=ROOT_ENDPOINT_ID,
124+
payload=Clusters.GeneralCommissioning.Commands.SetTCAcknowledgements(
125+
TCVersion=accepted_version + 1, TCUserResponse=0
126+
),
127+
)
128+
129+
# Verify RequiredTCNotAccepted error
130+
asserts.assert_equal(
131+
response.errorCode,
132+
Clusters.GeneralCommissioning.Enums.CommissioningErrorEnum.kRequiredTCNotAccepted,
133+
"Expected RequiredTCNotAccepted error",
134+
)
135+
136+
# Step 7: Verify TCAcceptedVersion still unchanged
137+
self.step(7)
138+
response = await commissioner.ReadAttribute(nodeid=self.dut_node_id, attributes=[(ROOT_ENDPOINT_ID, Clusters.GeneralCommissioning.Attributes.TCAcceptedVersion)])
139+
current_version = response[ROOT_ENDPOINT_ID][Clusters.GeneralCommissioning][Clusters.GeneralCommissioning.Attributes.TCAcceptedVersion]
140+
asserts.assert_equal(current_version, accepted_version, "TCAcceptedVersion changed unexpectedly after second attempt")
141+
142+
# Step 8: Verify TCAcknowledgements still unchanged
143+
self.step(8)
144+
response = await commissioner.ReadAttribute(nodeid=self.dut_node_id, attributes=[(ROOT_ENDPOINT_ID, Clusters.GeneralCommissioning.Attributes.TCAcknowledgements)])
145+
current_acknowledgements = response[ROOT_ENDPOINT_ID][Clusters.GeneralCommissioning][Clusters.GeneralCommissioning.Attributes.TCAcknowledgements]
146+
asserts.assert_equal(
147+
current_acknowledgements,
148+
user_acknowledgements,
149+
"TCAcknowledgements changed unexpectedly after second attempt",
150+
)
151+
152+
153+
if __name__ == "__main__":
154+
default_matter_test_main()

0 commit comments

Comments
 (0)