Skip to content

Commit 38a065e

Browse files
authored
Merge branch 'master' into add-dem-example-code-for-1-4
2 parents b8d55ce + 4ec643c commit 38a065e

15 files changed

+337
-75
lines changed

.github/actions/bootstrap/action.yaml

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ runs:
2626
# because the bootstrapped Pigweed environment contains absolute paths.
2727
echo "Calculating bootstrap cache key for '$PWD'"
2828
FILES_HASH="${{ hashFiles('scripts/setup/*', 'third_party/pigweed/**') }}"
29-
FINAL_HASH="$(echo "$PWD:$FILES_HASH" | shasum -a 256 | cut -d' ' -f1)"
29+
OS_HASH="$(md5sum /etc/lsb-release | cut -d' ' -f1)"
30+
PYTHON_HASH="$(python --version | md5sum | cut -d' ' -f1)"
31+
FINAL_HASH="$(echo "$PWD:$FILES_HASH:$OS_HASH:$PYTHON_HASH" | shasum -a 256 | cut -d' ' -f1)"
3032
echo key="${RUNNER_OS}-${RUNNER_ARCH}-${{ inputs.platform }}-${FINAL_HASH}" | tee -a "$GITHUB_OUTPUT"
3133
3234
# Split caches across backends

.github/workflows/examples-asr.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
if: github.actor != 'restyled-io[bot]'
3737

3838
container:
39-
image: ghcr.io/project-chip/chip-build-asr:54
39+
image: ghcr.io/project-chip/chip-build-asr:65
4040
options: --user root
4141

4242
steps:

.github/workflows/examples-efr32.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
if: github.actor != 'restyled-io[bot]'
4141

4242
container:
43-
image: ghcr.io/project-chip/chip-build-efr32:56
43+
image: ghcr.io/project-chip/chip-build-efr32:65
4444
volumes:
4545
- "/tmp/bloat_reports:/tmp/bloat_reports"
4646
steps:

.github/workflows/examples-mbed.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ jobs:
4242
if: github.actor != 'restyled-io[bot]'
4343

4444
container:
45-
image: ghcr.io/project-chip/chip-build-mbed-os:54
45+
image: ghcr.io/project-chip/chip-build-mbed-os:65
4646
volumes:
4747
- "/tmp/bloat_reports:/tmp/bloat_reports"
4848

.github/workflows/examples-nuttx.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
if: github.actor != 'restyled-io[bot]'
3636

3737
container:
38-
image: ghcr.io/project-chip/chip-build-nuttx:54
38+
image: ghcr.io/project-chip/chip-build-nuttx:65
3939
volumes:
4040
- "/tmp/bloat_reports:/tmp/bloat_reports"
4141
steps:

.github/workflows/examples-nxp.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
if: github.actor != 'restyled-io[bot]'
4040

4141
container:
42-
image: ghcr.io/project-chip/chip-build-k32w:54
42+
image: ghcr.io/project-chip/chip-build-k32w:65
4343
volumes:
4444
- "/tmp/bloat_reports:/tmp/bloat_reports"
4545
steps:
@@ -104,7 +104,7 @@ jobs:
104104
if: github.actor != 'restyled-io[bot]'
105105

106106
container:
107-
image: ghcr.io/project-chip/chip-build-nxp-zephyr:64
107+
image: ghcr.io/project-chip/chip-build-nxp-zephyr:65
108108

109109
steps:
110110
- name: Checkout

.github/workflows/examples-openiotsdk.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
if: github.actor != 'restyled-io[bot]'
4141

4242
container:
43-
image: ghcr.io/project-chip/chip-build-openiotsdk:54
43+
image: ghcr.io/project-chip/chip-build-openiotsdk:65
4444
volumes:
4545
- "/tmp/bloat_reports:/tmp/bloat_reports"
4646
options: --privileged

.github/workflows/examples-rw61x.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
if: github.actor != 'restyled-io[bot]'
4040

4141
container:
42-
image: ghcr.io/project-chip/chip-build-rw61x:54
42+
image: ghcr.io/project-chip/chip-build-rw61x:65
4343
volumes:
4444
- "/tmp/bloat_reports:/tmp/bloat_reports"
4545
steps:

.github/workflows/examples-telink.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
if: github.actor != 'restyled-io[bot]'
3939

4040
container:
41-
image: ghcr.io/project-chip/chip-build-telink:57
41+
image: ghcr.io/project-chip/chip-build-telink:65
4242
volumes:
4343
- "/tmp/bloat_reports:/tmp/bloat_reports"
4444

.github/workflows/examples-tizen.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
if: github.actor != 'restyled-io[bot]'
3737

3838
container:
39-
image: ghcr.io/project-chip/chip-build-tizen:54
39+
image: ghcr.io/project-chip/chip-build-tizen:65
4040
options: --user root
4141
volumes:
4242
- "/tmp/bloat_reports:/tmp/bloat_reports"

.github/workflows/minimal-build.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
runs-on: ubuntu-latest
3434

3535
container:
36-
image: ghcr.io/project-chip/chip-build-minimal:54
36+
image: ghcr.io/project-chip/chip-build-minimal:65
3737

3838
steps:
3939
- name: Checkout
@@ -55,7 +55,7 @@ jobs:
5555
runs-on: ubuntu-latest
5656

5757
container:
58-
image: ghcr.io/project-chip/chip-build-minimal:54
58+
image: ghcr.io/project-chip/chip-build-minimal:65
5959

6060
steps:
6161
- name: Checkout

.github/workflows/tests.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,7 @@ jobs:
582582
scripts/run_in_python_env.sh out/venv './scripts/tests/TestTimeSyncTrustedTimeSourceRunner.py'
583583
scripts/run_in_python_env.sh out/venv './src/python_testing/test_testing/test_TC_ICDM_2_1.py'
584584
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestIdChecks.py'
585+
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestSpecParsingDeviceType.py'
585586
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/TestConformanceSupport.py'
586587
scripts/run_in_python_env.sh out/venv 'python3 ./src/python_testing/test_testing/test_IDM_10_4.py'
587588
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#
2+
# Copyright (c) 2024 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+
import xml.etree.ElementTree as ElementTree
18+
19+
from jinja2 import Template
20+
from matter_testing_support import MatterBaseTest, default_matter_test_main
21+
from mobly import asserts
22+
from spec_parsing_support import build_xml_device_types, parse_single_device_type
23+
24+
25+
class TestSpecParsingDeviceType(MatterBaseTest):
26+
27+
# This just tests that the current spec can be parsed without failures
28+
def test_spec_device_parsing(self):
29+
device_types, problems = build_xml_device_types()
30+
self.problems += problems
31+
for id, d in device_types.items():
32+
print(str(d))
33+
34+
def setup_class(self):
35+
self.device_type_id = 0xBBEF
36+
self.revision = 2
37+
self.classification_class = "simple"
38+
self.classification_scope = "endpoint"
39+
self.clusters = {0x0003: "Identify", 0x0004: "Groups"}
40+
41+
# Conformance support tests the different types of conformance for clusters, so here we just want to ensure that we're correctly parsing the XML into python
42+
# adds the same attributes and features to every cluster. This is fine for testing.
43+
self.template = Template("""<deviceType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="types types.xsd devicetype devicetype.xsd" id="{{ device_type_id }}" name="Test Device Type" revision="{{ revision }}">
44+
<revisionHistory>
45+
{% for i in range(revision) %}
46+
<revision revision="{{ i }}" summary="Rev"/>
47+
{% endfor %}
48+
</revisionHistory>
49+
<classification {% if classification_class %} class="{{ classification_class }}" {% endif %} {% if classification_scope %} scope="{{ classification_scope }}" {% endif %}/>
50+
<conditions/>
51+
<clusters>
52+
{% for k,v in clusters.items() %}
53+
<cluster id="{{ k }}" name="{{ v }}" side="server">
54+
<mandatoryConform/>
55+
</cluster>
56+
{% endfor %}
57+
</clusters>
58+
</deviceType>""")
59+
60+
def test_device_type_clusters(self):
61+
xml = self.template.render(device_type_id=self.device_type_id, revision=self.revision, classification_class=self.classification_class,
62+
classification_scope=self.classification_scope, clusters=self.clusters)
63+
et = ElementTree.fromstring(xml)
64+
device_type, problems = parse_single_device_type(et)
65+
asserts.assert_equal(len(problems), 0, "Unexpected problems parsing device type conformance")
66+
asserts.assert_equal(len(device_type.keys()), 1, "Unexpected number of device types returned")
67+
asserts.assert_true(self.device_type_id in device_type.keys(), "device type id not found in returned data")
68+
asserts.assert_equal(device_type[self.device_type_id].revision, self.revision, "Unexpected revision")
69+
asserts.assert_equal(len(device_type[self.device_type_id].server_clusters),
70+
len(self.clusters), "Unexpected number of clusters")
71+
for id, name in self.clusters.items():
72+
asserts.assert_equal(device_type[self.device_type_id].server_clusters[id].name, name, "Incorrect cluster name")
73+
asserts.assert_equal(str(device_type[self.device_type_id].server_clusters[id].conformance),
74+
'M', 'Incorrect cluster conformance')
75+
76+
def test_no_clusters(self):
77+
clusters = {}
78+
xml = self.template.render(device_type_id=self.device_type_id, revision=self.revision, classification_class=self.classification_class,
79+
classification_scope=self.classification_scope, clusters=clusters)
80+
et = ElementTree.fromstring(xml)
81+
device_type, problems = parse_single_device_type(et)
82+
asserts.assert_equal(len(problems), 0, "Unexpected problems parsing device type conformance")
83+
asserts.assert_equal(len(device_type.keys()), 1, "Unexpected number of device types returned")
84+
asserts.assert_true(self.device_type_id in device_type.keys(), "device type id not found in returned data")
85+
asserts.assert_equal(device_type[self.device_type_id].revision, self.revision, "Unexpected revision")
86+
asserts.assert_equal(len(device_type[self.device_type_id].server_clusters), len(clusters), "Unexpected number of clusters")
87+
88+
def test_bad_device_type_id(self):
89+
xml = self.template.render(device_type_id="", revision=self.revision, classification_class=self.classification_class,
90+
classification_scope=self.classification_scope, clusters=self.clusters)
91+
et = ElementTree.fromstring(xml)
92+
device_type, problems = parse_single_device_type(et)
93+
asserts.assert_equal(len(problems), 1, "Device with blank ID did not generate a problem notice")
94+
95+
def test_bad_class(self):
96+
xml = self.template.render(device_type_id=self.device_type_id, revision=self.revision, classification_class="",
97+
classification_scope=self.classification_scope, clusters=self.clusters)
98+
et = ElementTree.fromstring(xml)
99+
device_type, problems = parse_single_device_type(et)
100+
asserts.assert_equal(len(problems), 1, "Device with no class did not generate a problem notice")
101+
102+
def test_bad_scope(self):
103+
xml = self.template.render(device_type_id=self.device_type_id, revision=self.revision, classification_class=self.classification_class,
104+
classification_scope="", clusters=self.clusters)
105+
et = ElementTree.fromstring(xml)
106+
device_type, problems = parse_single_device_type(et)
107+
asserts.assert_equal(len(problems), 1, "Device with no scope did not generate a problem notice")
108+
109+
110+
if __name__ == "__main__":
111+
default_matter_test_main()

src/python_testing/matter_testing_support.py

+29-31
Original file line numberDiff line numberDiff line change
@@ -460,14 +460,17 @@ class CustomCommissioningParameters:
460460

461461

462462
@dataclass
463-
class ProblemLocation:
463+
class ClusterPathLocation:
464+
endpoint_id: int
465+
cluster_id: int
466+
464467
def __str__(self):
465-
return "UNKNOWN"
468+
return (f'\n Endpoint: {self.endpoint_id},'
469+
f'\n Cluster: {cluster_id_str(self.cluster_id)}')
466470

467471

468472
@dataclass
469-
class AttributePathLocation(ProblemLocation):
470-
endpoint_id: int
473+
class AttributePathLocation(ClusterPathLocation):
471474
cluster_id: Optional[int] = None
472475
attribute_id: Optional[int] = None
473476

@@ -485,55 +488,50 @@ def as_string(self, mapper: ClusterMapper):
485488
return desc
486489

487490
def __str__(self):
488-
return (f'\n Endpoint: {self.endpoint_id},'
489-
f'\n Cluster: {cluster_id_str(self.cluster_id)},'
490-
f'\n Attribute:{id_str(self.attribute_id)}')
491+
return (f'{super().__str__()}'
492+
f'\n Attribute:{id_str(self.attribute_id)}')
491493

492494

493495
@dataclass
494-
class EventPathLocation(ProblemLocation):
495-
endpoint_id: int
496-
cluster_id: int
496+
class EventPathLocation(ClusterPathLocation):
497497
event_id: int
498498

499499
def __str__(self):
500-
return (f'\n Endpoint: {self.endpoint_id},'
501-
f'\n Cluster: {cluster_id_str(self.cluster_id)},'
502-
f'\n Event: {id_str(self.event_id)}')
500+
return (f'{super().__str__()}'
501+
f'\n Event: {id_str(self.event_id)}')
503502

504503

505504
@dataclass
506-
class CommandPathLocation(ProblemLocation):
507-
endpoint_id: int
508-
cluster_id: int
505+
class CommandPathLocation(ClusterPathLocation):
509506
command_id: int
510507

511508
def __str__(self):
512-
return (f'\n Endpoint: {self.endpoint_id},'
513-
f'\n Cluster: {cluster_id_str(self.cluster_id)},'
514-
f'\n Command: {id_str(self.command_id)}')
509+
return (f'{super().__str__()}'
510+
f'\n Command: {id_str(self.command_id)}')
515511

516512

517513
@dataclass
518-
class ClusterPathLocation(ProblemLocation):
519-
endpoint_id: int
520-
cluster_id: int
514+
class FeaturePathLocation(ClusterPathLocation):
515+
feature_code: str
521516

522517
def __str__(self):
523-
return (f'\n Endpoint: {self.endpoint_id},'
524-
f'\n Cluster: {cluster_id_str(self.cluster_id)}')
518+
return (f'{super().__str__()}'
519+
f'\n Feature: {self.feature_code}')
525520

526521

527522
@dataclass
528-
class FeaturePathLocation(ProblemLocation):
529-
endpoint_id: int
530-
cluster_id: int
531-
feature_code: str
523+
class DeviceTypePathLocation:
524+
device_type_id: int
525+
cluster_id: Optional[int] = None
532526

533527
def __str__(self):
534-
return (f'\n Endpoint: {self.endpoint_id},'
535-
f'\n Cluster: {cluster_id_str(self.cluster_id)},'
536-
f'\n Feature: {self.feature_code}')
528+
msg = f'\n DeviceType: {self.device_type_id}'
529+
if self.cluster_id:
530+
msg += f'\n ClusterID: {self.cluster_id}'
531+
return msg
532+
533+
534+
ProblemLocation = typing.Union[ClusterPathLocation, DeviceTypePathLocation]
537535

538536
# ProblemSeverity is not using StrEnum, but rather Enum, since StrEnum only
539537
# appeared in 3.11. To make it JSON serializable easily, multiple inheritance

0 commit comments

Comments
 (0)