Skip to content

Commit 1604924

Browse files
cecillerestyled-commitstcarmelveilleux
authored andcommitted
TC-IDM-XX: try both PASE and CASE for basic comp tests. (#35109)
* TC-IDM-XX: try both PASE and CASE for basic comp tests. These tests are disruptive at cert because they could run over PASE or CASE and defaulted to PASE. This meant that they couldn't be run in sequence with other tests that require commissioning. Because the PASE setup required a qr or manual code, it also meant that these needed special parameters to run. Now: - can use discriminator and passcode for PASE connection - device tries both PASE and CASE and runs over whichever works first. * fix CI * Restyled by whitespace * Restyled by clang-format * Fix tests for NO DUT (unit and mock) * Update src/python_testing/basic_composition_support.py Co-authored-by: Tennessee Carmel-Veilleux <[email protected]> * Null terminate and add outbuf size * Restyled by clang-format * typo --------- Co-authored-by: Restyled.io <[email protected]> Co-authored-by: Tennessee Carmel-Veilleux <[email protected]>
1 parent 7146ef2 commit 1604924

File tree

6 files changed

+147
-36
lines changed

6 files changed

+147
-36
lines changed

.github/workflows/tests.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -515,8 +515,6 @@ jobs:
515515
mkdir -p out/trace_data
516516
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/controller/python/test/test_scripts/mobile-device-test.py'
517517
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/execute_python_tests.py --env-file /tmp/test_env.yaml --search-directory src/python_testing'
518-
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --script "src/python_testing/TestMatterTestingSupport.py" --script-args "--trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"'
519-
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --script "src/python_testing/TestSpecParsingSupport.py" --script-args "--trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"'
520518
scripts/run_in_python_env.sh out/venv './scripts/tests/TestTimeSyncTrustedTimeSourceRunner.py'
521519
scripts/run_in_python_env.sh out/venv './src/python_testing/test_testing/test_TC_ICDM_2_1.py'
522520
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestIdChecks.py'
@@ -528,6 +526,8 @@ jobs:
528526
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_IDM_10_4.py'
529527
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_TC_SC_7_1.py'
530528
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/TestDecorators.py'
529+
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestMatterTestingSupport.py'
530+
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestSpecParsingSupport.py'
531531
532532
533533
- name: Uploading core files

src/controller/python/ChipDeviceController-Discovery.cpp

+31
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,16 @@
2323
*
2424
*/
2525

26+
#include <string>
27+
2628
#include <controller/CHIPDeviceController.h>
2729
#include <controller/python/chip/native/PyChipError.h>
2830
#include <json/json.h>
2931
#include <lib/core/CHIPError.h>
3032
#include <lib/core/TLV.h>
3133
#include <lib/dnssd/Resolver.h>
34+
#include <setup_payload/ManualSetupPayloadGenerator.h>
35+
#include <setup_payload/SetupPayload.h>
3236

3337
using namespace chip;
3438

@@ -186,4 +190,31 @@ bool pychip_DeviceController_GetIPForDiscoveredDevice(Controller::DeviceCommissi
186190
}
187191
return false;
188192
}
193+
194+
PyChipError pychip_CreateManualCode(uint16_t longDiscriminator, uint32_t passcode, char * manualCodeBuffer, size_t inBufSize,
195+
size_t * outBufSize)
196+
{
197+
SetupPayload payload;
198+
SetupDiscriminator discriminator;
199+
discriminator.SetLongValue(longDiscriminator);
200+
payload.discriminator = discriminator;
201+
payload.setUpPINCode = passcode;
202+
std::string setupManualCode;
203+
204+
*outBufSize = 0;
205+
CHIP_ERROR err = ManualSetupPayloadGenerator(payload).payloadDecimalStringRepresentation(setupManualCode);
206+
if (err == CHIP_NO_ERROR)
207+
{
208+
MutableCharSpan span(manualCodeBuffer, inBufSize);
209+
// Plus 1 so we copy the null terminator
210+
CopyCharSpanToMutableCharSpan(CharSpan(setupManualCode.c_str(), setupManualCode.length() + 1), span);
211+
*outBufSize = span.size();
212+
if (*outBufSize == 0)
213+
{
214+
err = CHIP_ERROR_NO_MEMORY;
215+
}
216+
}
217+
218+
return ToPyChipError(err);
219+
}
189220
}

src/controller/python/chip/ChipDeviceCtrl.py

+16
Original file line numberDiff line numberDiff line change
@@ -1711,6 +1711,19 @@ def InitGroupTestingData(self):
17111711
self.devCtrl)
17121712
).raise_on_error()
17131713

1714+
def CreateManualCode(self, discriminator: int, passcode: int) -> str:
1715+
""" Creates a standard flow manual code from the given discriminator and passcode."""
1716+
# 64 bytes is WAY more than required, but let's be safe
1717+
in_size = 64
1718+
out_size = c_size_t(0)
1719+
buf = create_string_buffer(in_size)
1720+
self._ChipStack.Call(
1721+
lambda: self._dmLib.pychip_CreateManualCode(discriminator, passcode, buf, in_size, pointer(out_size))
1722+
).raise_on_error()
1723+
if out_size.value == 0 or out_size.value > in_size:
1724+
raise MemoryError("Invalid output size for manual code")
1725+
return buf.value.decode()
1726+
17141727
# ----- Private Members -----
17151728
def _InitLib(self):
17161729
if self._dmLib is None:
@@ -1938,6 +1951,9 @@ def _InitLib(self):
19381951
self._dmLib.pychip_DeviceProxy_GetRemoteSessionParameters.restype = PyChipError
19391952
self._dmLib.pychip_DeviceProxy_GetRemoteSessionParameters.argtypes = [c_void_p, c_char_p]
19401953

1954+
self._dmLib.pychip_CreateManualCode.restype = PyChipError
1955+
self._dmLib.pychip_CreateManualCode.argtypes = [c_uint16, c_uint32, c_char_p, c_size_t, POINTER(c_size_t)]
1956+
19411957

19421958
class ChipDeviceController(ChipDeviceControllerBase):
19431959
''' The ChipDeviceCommissioner binding, named as ChipDeviceController

src/python_testing/TC_DeviceBasicComposition.py

+45-1
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,58 @@
1919
# for details about the block below.
2020
#
2121
# === BEGIN CI TEST ARGUMENTS ===
22-
# test-runner-runs: run1
22+
# test-runner-runs: run1 run2 run3 run4 run5 run6 run7
2323
# test-runner-run/run1/app: ${ALL_CLUSTERS_APP}
2424
# test-runner-run/run1/factoryreset: True
2525
# test-runner-run/run1/quiet: True
2626
# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json
2727
# test-runner-run/run1/script-args: --storage-path admin_storage.json --manual-code 10054912339 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
28+
#
29+
# test-runner-run/run2/app: ${CHIP_LOCK_APP}
30+
# test-runner-run/run2/factoryreset: True
31+
# test-runner-run/run2/quiet: True
32+
# test-runner-run/run2/app-args: --discriminator 1234 --KVS kvs1
33+
# test-runner-run/run2/script-args: --storage-path admin_storage.json --manual-code 10054912339
34+
#
35+
# test-runner-run/run3/app: ${CHIP_LOCK_APP}
36+
# test-runner-run/run3/factoryreset: True
37+
# test-runner-run/run3/quiet: True
38+
# test-runner-run/run3/app-args: --discriminator 1234 --KVS kvs1
39+
# test-runner-run/run3/script-args: --storage-path admin_storage.json --qr-code MT:-24J0Q1212-10648G00
40+
#
41+
# test-runner-run/run4/app: ${CHIP_LOCK_APP}
42+
# test-runner-run/run4/factoryreset: True
43+
# test-runner-run/run4/quiet: True
44+
# test-runner-run/run4/app-args: --discriminator 1234 --KVS kvs1
45+
# test-runner-run/run4/script-args: --storage-path admin_storage.json --discriminator 1234 --passcode 20202021
46+
#
47+
# test-runner-run/run5/app: ${CHIP_LOCK_APP}
48+
# test-runner-run/run5/factoryreset: True
49+
# test-runner-run/run5/quiet: True
50+
# test-runner-run/run5/app-args: --discriminator 1234 --KVS kvs1
51+
# test-runner-run/run5/script-args: --storage-path admin_storage.json --manual-code 10054912339 --commissioning-method on-network
52+
#
53+
# test-runner-run/run6/app: ${CHIP_LOCK_APP}
54+
# test-runner-run/run6/factoryreset: True
55+
# test-runner-run/run6/quiet: True
56+
# test-runner-run/run6/app-args: --discriminator 1234 --KVS kvs1
57+
# test-runner-run/run6/script-args: --storage-path admin_storage.json --qr-code MT:-24J0Q1212-10648G00 --commissioning-method on-network
58+
#
59+
# test-runner-run/run7/app: ${CHIP_LOCK_APP}
60+
# test-runner-run/run7/factoryreset: True
61+
# test-runner-run/run7/quiet: True
62+
# test-runner-run/run7/app-args: --discriminator 1234 --KVS kvs1
63+
# test-runner-run/run7/script-args: --storage-path admin_storage.json --discriminator 1234 --passcode 20202021 --commissioning-method on-network
2864
# === END CI TEST ARGUMENTS ===
2965

66+
# Run 1: runs through all tests
67+
# Run 2: tests PASE connection using manual code (12.1 only)
68+
# Run 3: tests PASE connection using QR code (12.1 only)
69+
# Run 4: tests PASE connection using discriminator and passcode (12.1 only)
70+
# Run 5: Tests CASE connection using manual code (12.1 only)
71+
# Run 6: Tests CASE connection using QR code (12.1 only)
72+
# Run 7: Tests CASE connection using manual discriminator and passcode (12.1 only)
73+
3074
import logging
3175
from dataclasses import dataclass
3276
from typing import Any, Callable

src/python_testing/basic_composition_support.py

+33-15
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# limitations under the License.
1616
#
1717

18-
18+
import asyncio
1919
import base64
2020
import copy
2121
import json
@@ -98,12 +98,15 @@ def ConvertValue(value) -> Any:
9898

9999

100100
class BasicCompositionTests:
101-
async def connect_over_pase(self, dev_ctrl):
102-
asserts.assert_true(self.matter_test_config.qr_code_content == [] or self.matter_test_config.manual_code == [],
103-
"Cannot have both QR and manual code specified")
104-
setupCode = self.matter_test_config.qr_code_content + self.matter_test_config.manual_code
105-
asserts.assert_equal(len(setupCode), 1, "Require one of either --qr-code or --manual-code.")
106-
await dev_ctrl.FindOrEstablishPASESession(setupCode[0], self.dut_node_id)
101+
def get_code(self, dev_ctrl):
102+
created_codes = []
103+
for idx, discriminator in enumerate(self.matter_test_config.discriminators):
104+
created_codes.append(dev_ctrl.CreateManualCode(discriminator, self.matter_test_config.setup_passcodes[idx]))
105+
106+
setup_codes = self.matter_test_config.qr_code_content + self.matter_test_config.manual_code + created_codes
107+
asserts.assert_equal(len(setup_codes), 1,
108+
"Require exactly one of either --qr-code, --manual-code or (--discriminator and --passcode).")
109+
return setup_codes[0]
107110

108111
def dump_wildcard(self, dump_device_composition_path: typing.Optional[str]) -> tuple[str, str]:
109112
""" Dumps a json and a txt file of the attribute wildcard for this device if the dump_device_composition_path is supplied.
@@ -120,19 +123,34 @@ def dump_wildcard(self, dump_device_composition_path: typing.Optional[str]) -> t
120123
pprint(self.endpoints, outfile, indent=1, width=200, compact=True)
121124
return (json_dump_string, pformat(self.endpoints, indent=1, width=200, compact=True))
122125

123-
async def setup_class_helper(self, default_to_pase: bool = True):
126+
async def setup_class_helper(self, allow_pase: bool = True):
124127
dev_ctrl = self.default_controller
125128
self.problems = []
126129

127-
do_test_over_pase = self.user_params.get("use_pase_only", default_to_pase)
128130
dump_device_composition_path: Optional[str] = self.user_params.get("dump_device_composition_path", None)
129131

130-
if do_test_over_pase:
131-
await self.connect_over_pase(dev_ctrl)
132-
node_id = self.dut_node_id
133-
else:
134-
# Using the already commissioned node
135-
node_id = self.dut_node_id
132+
node_id = self.dut_node_id
133+
134+
task_list = []
135+
if allow_pase:
136+
setup_code = self.get_code(dev_ctrl)
137+
pase_future = dev_ctrl.EstablishPASESession(setup_code, self.dut_node_id)
138+
task_list.append(asyncio.create_task(pase_future))
139+
140+
case_future = dev_ctrl.GetConnectedDevice(nodeid=node_id, allowPASE=False)
141+
task_list.append(asyncio.create_task(case_future))
142+
143+
for task in task_list:
144+
asyncio.ensure_future(task)
145+
146+
done, pending = await asyncio.wait(task_list, return_when=asyncio.FIRST_COMPLETED)
147+
148+
for task in pending:
149+
try:
150+
task.cancel()
151+
await task
152+
except asyncio.CancelledError:
153+
pass
136154

137155
wildcard_read = (await dev_ctrl.Read(node_id, [()]))
138156

src/python_testing/matter_testing_support.py

+20-18
Original file line numberDiff line numberDiff line change
@@ -629,8 +629,8 @@ class MatterTestConfig:
629629
app_pid: int = 0
630630

631631
commissioning_method: Optional[str] = None
632-
discriminators: Optional[List[int]] = None
633-
setup_passcodes: Optional[List[int]] = None
632+
discriminators: List[int] = field(default_factory=list)
633+
setup_passcodes: List[int] = field(default_factory=list)
634634
commissionee_ip_address_just_for_testing: Optional[str] = None
635635
# By default, we start with maximized cert chains, as required for RR-1.1.
636636
# This allows cert tests to be run without re-commissioning for RR-1.1.
@@ -646,7 +646,7 @@ class MatterTestConfig:
646646
pics: dict[bool, str] = field(default_factory=dict)
647647

648648
# Node ID for basic DUT
649-
dut_node_ids: Optional[List[int]] = None
649+
dut_node_ids: List[int] = field(default_factory=list)
650650
# Node ID to use for controller/commissioner
651651
controller_node_id: int = _DEFAULT_CONTROLLER_NODE_ID
652652
# CAT Tags for default controller/commissioner
@@ -1730,19 +1730,8 @@ def populate_commissioning_args(args: argparse.Namespace, config: MatterTestConf
17301730

17311731
config.qr_code_content.extend(args.qr_code)
17321732
config.manual_code.extend(args.manual_code)
1733-
1734-
if args.commissioning_method is None:
1735-
return True
1736-
1737-
if args.discriminators == [] and (args.qr_code == [] and args.manual_code == []):
1738-
print("error: Missing --discriminator when no --qr-code/--manual-code present!")
1739-
return False
1740-
config.discriminators = args.discriminators
1741-
1742-
if args.passcodes == [] and (args.qr_code == [] and args.manual_code == []):
1743-
print("error: Missing --passcode when no --qr-code/--manual-code present!")
1744-
return False
1745-
config.setup_passcodes = args.passcodes
1733+
config.discriminators.extend(args.discriminators)
1734+
config.setup_passcodes.extend(args.passcodes)
17461735

17471736
if args.qr_code != [] and args.manual_code != []:
17481737
print("error: Cannot have both --qr-code and --manual-code present!")
@@ -1759,9 +1748,11 @@ def populate_commissioning_args(args: argparse.Namespace, config: MatterTestConf
17591748
return False
17601749

17611750
if len(config.dut_node_ids) < len(device_descriptors):
1762-
missing = len(device_descriptors) - len(config.dut_node_ids)
17631751
# We generate new node IDs sequentially from the last one seen for all
17641752
# missing NodeIDs when commissioning many nodes at once.
1753+
if not config.dut_node_ids:
1754+
config.dut_node_ids = [_DEFAULT_DUT_NODE_ID]
1755+
missing = len(device_descriptors) - len(config.dut_node_ids)
17651756
for i in range(missing):
17661757
config.dut_node_ids.append(config.dut_node_ids[-1] + 1)
17671758

@@ -1773,6 +1764,17 @@ def populate_commissioning_args(args: argparse.Namespace, config: MatterTestConf
17731764
print("error: Duplicate value in discriminator list")
17741765
return False
17751766

1767+
if args.commissioning_method is None:
1768+
return True
1769+
1770+
if args.discriminators == [] and (args.qr_code == [] and args.manual_code == []):
1771+
print("error: Missing --discriminator when no --qr-code/--manual-code present!")
1772+
return False
1773+
1774+
if args.passcodes == [] and (args.qr_code == [] and args.manual_code == []):
1775+
print("error: Missing --passcode when no --qr-code/--manual-code present!")
1776+
return False
1777+
17761778
if config.commissioning_method == "ble-wifi":
17771779
if args.wifi_ssid is None:
17781780
print("error: missing --wifi-ssid <SSID> for --commissioning-method ble-wifi!")
@@ -1869,7 +1871,7 @@ def parse_matter_test_args(argv: Optional[List[str]] = None) -> MatterTestConfig
18691871
default=_DEFAULT_CONTROLLER_NODE_ID,
18701872
help='NodeID to use for initial/default controller (default: %d)' % _DEFAULT_CONTROLLER_NODE_ID)
18711873
basic_group.add_argument('-n', '--dut-node-id', '--nodeId', type=int_decimal_or_hex,
1872-
metavar='NODE_ID', dest='dut_node_ids', default=[_DEFAULT_DUT_NODE_ID],
1874+
metavar='NODE_ID', dest='dut_node_ids', default=[],
18731875
help='Node ID for primary DUT communication, '
18741876
'and NodeID to assign if commissioning (default: %d)' % _DEFAULT_DUT_NODE_ID, nargs="+")
18751877
basic_group.add_argument('--endpoint', type=int, default=0, help="Endpoint under test")

0 commit comments

Comments
 (0)