Skip to content

Commit 2fc5ce8

Browse files
cecillegemini-code-assist[bot]restyled-commits
authored
TC-DD-16,17: Add, replace DD-1.12,14.14 (project-chip#41159)
* check * check * allow both manual and qr codes to be sent in * Stop device from advertising right away * check * actually open the commissioning window on pipe action * fix up test * add one expectation step * Apply suggestions from code review Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Apply suggestions from code review * Restyled by clang-format * Restyled by autopep8 * linter * Change var name per code review * I somehow mixed up the exact codes I'm testing Well, at least I know the test works... --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Restyled.io <[email protected]>
1 parent 5d09b88 commit 2fc5ce8

File tree

8 files changed

+197
-11
lines changed

8 files changed

+197
-11
lines changed

examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,10 @@ void AllClustersAppCommandHandler::HandleCommand(intptr_t context)
590590

591591
self->OnSoilMoistureChange(endpoint, soilMoistureMeasuredValue);
592592
}
593+
else if (name == "UserIntentCommissioningStart")
594+
{
595+
Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow();
596+
}
593597
else
594598
{
595599
ChipLogError(NotSpecified, "Unhandled command '%s': this should never happen", name.c_str());

examples/platform/linux/AppMain.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,11 @@ void ChipLinuxAppMainLoop(AppMainLoopImplementation * impl)
912912
initParams.accessRestrictionProvider = exampleAccessRestrictionProvider.get();
913913
#endif
914914

915+
if (LinuxDeviceOptions::GetInstance().payload.commissioningFlow == CommissioningFlow::kUserActionRequired)
916+
{
917+
initParams.advertiseCommissionableIfNoFabrics = false;
918+
}
919+
915920
// Init ZCL Data Model and CHIP App Server
916921
CHIP_ERROR err = Server::GetInstance().Init(initParams);
917922
if (err != CHIP_NO_ERROR)

src/app/server/Server.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,11 +362,9 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams)
362362
DeviceLayer::ConnectivityMgr().SetBLEAdvertisingEnabled(false);
363363
#endif
364364
}
365-
else
365+
else if (initParams.advertiseCommissionableIfNoFabrics)
366366
{
367-
#if CHIP_DEVICE_CONFIG_ENABLE_PAIRING_AUTOSTART
368367
SuccessOrExit(err = mCommissioningWindowManager.OpenBasicCommissioningWindow(initParams.discoveryTimeout));
369-
#endif
370368
}
371369

372370
// TODO @bzbarsky-apple @cecille Move to examples

src/app/server/Server.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ struct ServerInitParams
207207
// data model it wants to use. Backwards-compatibility can use `CodegenDataModelProviderInstance`
208208
// for ember/zap-generated models.
209209
chip::app::DataModel::Provider * dataModelProvider = nullptr;
210+
211+
bool advertiseCommissionableIfNoFabrics = CHIP_DEVICE_CONFIG_ENABLE_PAIRING_AUTOSTART;
210212
};
211213

212214
/**
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
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: >
26+
# --discriminator 1234
27+
# --KVS kvs1
28+
# --custom-flow 1
29+
# --app-pipe /tmp/tmp_pipe
30+
# --trace-to json:${TRACE_APP}.json
31+
# script-args: >
32+
# --storage-path admin_storage.json
33+
# --manual-code 500549123365521327696
34+
# --qr-code MT:-24J0MH312-10648G00
35+
# --PICS src/app/tests/suites/certification/ci-pics-values
36+
# --app-pipe /tmp/tmp_pipe
37+
# --trace-to json:${TRACE_TEST_JSON}.json
38+
# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
39+
# factory-reset: true
40+
# quiet: true
41+
# run2:
42+
# app: ${ALL_CLUSTERS_APP}
43+
# app-args: >
44+
# --discriminator 1234
45+
# --KVS kvs1
46+
# --custom-flow 0
47+
# --app-pipe /tmp/tmp_pipe
48+
# --trace-to json:${TRACE_APP}.json
49+
# script-args: >
50+
# --storage-path admin_storage.json
51+
# --manual-code 10054912339
52+
# --qr-code MT:-24J0Q1212-10648G00
53+
# --PICS src/app/tests/suites/certification/ci-pics-values
54+
# --app-pipe /tmp/tmp_pipe
55+
# --trace-to json:${TRACE_TEST_JSON}.json
56+
# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
57+
# factory-reset: true
58+
# quiet: true
59+
# === END CI TEST ARGUMENTS ===
60+
#
61+
# Test runs twice - once with standard flow, once with user-intent flow
62+
63+
from mobly import asserts
64+
65+
import matter.discovery
66+
from matter.setup_payload import SetupPayload
67+
from matter.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main
68+
69+
# TODO: set the default timeout to be larger because there's a manual step
70+
# TODO: force the linux app to actually not advertise when it's in user intent mode and add a trigger to start advertising
71+
72+
73+
class TC_DD_1_16_17(MatterBaseTest):
74+
''' TC-DD-1.16 and TC-DD-1.17
75+
76+
TC-DD-1.16
77+
This is the test for the QR code. This test does NOT include a request to put the device into commissionable
78+
mode since devices with a QR code will necessarily include a manual code as well per the spec guidelines. This
79+
test DOES include a check that user-intent commissioning flow devices do not advertise on startup.
80+
81+
TC-DD-1.17
82+
This is the test for manual code. This test includes a request to put the custom and user-intent
83+
commissioning devices into a commissionable mode because this test needs to be run for every device
84+
with a setup code. This test does NOT verify that user-intent commissioning flow devices do not
85+
advertise out of box because they cannot be differentiated from custom commissioning flow devices
86+
from the manual pairing code. That check is done in TC-DD-1.16
87+
'''
88+
89+
def steps_TC_DD_1_16(self):
90+
return [TestStep(1, "TH parses the QR code"),
91+
TestStep(2, "If the Custom Flow field is set to 0, this device uses standard flow. Verify that the DUT is advertising as Commissionable",
92+
"Device is advertising as commissionable"),
93+
TestStep(3, "If the Custom Flow field is set to 1, this device uses user-intent flow. Verify that the DUT is NOT advertising as Commissionable",
94+
"Device is not advertising as commissionable")
95+
]
96+
97+
def pics_TC_DD_1_16(self):
98+
return ['MCORE.DD.QR']
99+
100+
@async_test_body
101+
async def test_TC_DD_1_16(self):
102+
103+
self.step(1)
104+
asserts.assert_true(self.matter_test_config.qr_code_content,
105+
"This test needs to be run with the qr setup code.")
106+
parsed = SetupPayload().ParseQrCode(self.matter_test_config.qr_code_content[0])
107+
108+
self.step(2)
109+
if parsed.commissioning_flow == 0:
110+
# Standard commissioning flow - this should be advertising
111+
await self.ensure_advertising(filter_type=matter.discovery.FilterType.LONG_DISCRIMINATOR, filter=parsed.long_discriminator)
112+
else:
113+
self.mark_current_step_skipped()
114+
115+
self.step(3)
116+
if parsed.commissioning_flow == 1:
117+
responses = await self.default_controller.DiscoverCommissionableNodes(filterType=matter.discovery.FilterType.LONG_DISCRIMINATOR, filter=parsed.long_discriminator)
118+
asserts.assert_equal(responses, [], "Device with user-intent commissioning flow should not be advertising")
119+
else:
120+
self.mark_current_step_skipped()
121+
122+
def pics_TC_DD_1_17(self):
123+
return ['MCORE.DD.MANUAL_PC']
124+
125+
def steps_TC_DD_1_17(self):
126+
return [TestStep(1, "TH parses the manual code"),
127+
TestStep(2, "If the VID_PID_PRESENT field is set to 0, this device uses standard flow. Verify that the DUT is advertising as Commissionable",
128+
"Device is advertising as commissionable"),
129+
TestStep(3, "If the device uses custom flow or user-intent commissioning, ask the tester to place the device into commissionable mode"),
130+
TestStep(4, "If the device uses custom flow or user-intent commissioning, verify that the DUT is advertising as commissionable",
131+
"Device is advertising as commissionable"),
132+
]
133+
134+
async def ensure_advertising(self, filter_type: matter.discovery.FilterType, filter: int):
135+
responses = await self.default_controller.DiscoverCommissionableNodes(filterType=filter_type, filter=filter, stopOnFirst=True)
136+
asserts.assert_greater_equal(len(responses), 1, "Device should be advertising as commissionable")
137+
138+
@async_test_body
139+
async def test_TC_DD_1_17(self):
140+
self.step(1)
141+
asserts.assert_true(self.matter_test_config.manual_code,
142+
"This test needs to be run with the manual setup code.")
143+
parsed = SetupPayload().ParseManualPairingCode(self.matter_test_config.manual_code[0])
144+
has_product_id = int(parsed.product_id) != 0
145+
has_vendor_id = int(parsed.vendor_id) != 0
146+
asserts.assert_equal(has_product_id, has_vendor_id, "Product and vendor ID must either both be included or both be omitted")
147+
standard_flow = not has_vendor_id and not has_product_id
148+
149+
self.step(2)
150+
if standard_flow:
151+
# Device should be advertising
152+
await self.ensure_advertising(filter_type=matter.discovery.FilterType.SHORT_DISCRIMINATOR, filter=parsed.short_discriminator)
153+
else:
154+
self.mark_current_step_skipped()
155+
156+
self.step(3)
157+
if not standard_flow:
158+
if self.is_pics_sdk_ci_only:
159+
command_dict = {"Name": 'UserIntentCommissioningStart'}
160+
self.write_to_app_pipe(command_dict)
161+
else:
162+
self.wait_for_user_input("Please perform any actions needed to put the device into commissionable mode")
163+
else:
164+
self.mark_current_step_skipped()
165+
166+
self.step(4)
167+
if not standard_flow:
168+
await self.ensure_advertising(filter_type=matter.discovery.FilterType.SHORT_DISCRIMINATOR, filter=parsed.short_discriminator)
169+
else:
170+
self.mark_current_step_skipped()
171+
172+
173+
if __name__ == "__main__":
174+
default_matter_test_main()

src/python_testing/matter_testing_infrastructure/matter/testing/basic_composition.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,8 @@ def get_code(self, dev_ctrl):
157157
setup_codes = self.matter_test_config.qr_code_content + self.matter_test_config.manual_code + created_codes
158158
if not setup_codes:
159159
return None
160-
asserts.assert_equal(len(setup_codes), 1,
161-
"Require exactly one of either --qr-code, --manual-code or (--discriminator and --passcode).")
160+
asserts.assert_greater_equal(len(setup_codes), 1,
161+
"Require at least one of either --qr-code, --manual-code or (--discriminator and --passcode).")
162162
return setup_codes[0]
163163

164164
def dump_wildcard(self, dump_device_composition_path: typing.Optional[str]) -> tuple[str, str]:

src/python_testing/matter_testing_infrastructure/matter/testing/commissioning.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,16 @@ def get_setup_payload_info_config(matter_test_config: Any) -> List[SetupPayloadI
260260
except ChipStackError: # chipstack-ok: This disables ChipStackError linter check. Can not use 'with' because it is not expected to fail
261261
asserts.fail(f"QR code '{qr_code} failed to parse properly as a Matter setup code.")
262262

263+
manual_code_equivalents = [(s.long_discriminator >> 8, s.setup_passcode) for s in setup_payloads]
263264
for manual_code in matter_test_config.manual_code:
264265
try:
265-
setup_payloads.append(SetupPayload().ParseManualPairingCode(manual_code))
266+
# Remove any duplicate codes - where the discriminator and passcode match a previously added QR code.
267+
# This lets testers pass in the QR and equivalent manual code in order to run
268+
# the DD tests with a single set of parameters
269+
temp_payload = SetupPayload().ParseManualPairingCode(manual_code)
270+
if (temp_payload.short_discriminator, temp_payload.setup_passcode) in manual_code_equivalents:
271+
continue
272+
setup_payloads.append(temp_payload)
266273
except ChipStackError: # chipstack-ok: This disables ChipStackError linter check. Can not use 'with' because it is not expected to fail
267274
asserts.fail(
268275
f"Manual code code '{manual_code}' failed to parse properly as a Matter setup code. Check that all digits are correct and length is 11 or 21 characters.")

src/python_testing/matter_testing_infrastructure/matter/testing/runner.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -643,10 +643,6 @@ def populate_commissioning_args(args: argparse.Namespace, config) -> bool:
643643
config.discriminators.extend(args.discriminators)
644644
config.setup_passcodes.extend(args.passcodes)
645645

646-
if args.qr_code != [] and args.manual_code != []:
647-
print("error: Cannot have both --qr-code and --manual-code present!")
648-
return False
649-
650646
if len(config.discriminators) != len(config.setup_passcodes):
651647
print("error: supplied number of discriminators does not match number of passcodes")
652648
return False
@@ -862,7 +858,7 @@ def parse_matter_test_args(argv: Optional[List[str]] = None):
862858

863859
commission_group.add_argument('--tc-user-response-to-simulate', type=int, help="Terms and conditions acknowledgements")
864860

865-
code_group = parser.add_mutually_exclusive_group(required=False)
861+
code_group = parser.add_argument_group(title="Setup codes")
866862

867863
code_group.add_argument('-q', '--qr-code', type=str,
868864
metavar="QR_CODE", default=[], help="QR setup code content (overrides passcode and discriminator)", nargs="+")

0 commit comments

Comments
 (0)