Skip to content

Commit 8a0fc4d

Browse files
tersalrestyled-commitscecilleandy31415
authored
TC-TCCM Python Migration (#36767)
* Add base test and TCCM implementation * Refactor and comments * Rearrange code and reduce dependencies * Restyled by isort * Adding modebase_cluster_check to not_automated test list * Change endpoint for CI * Change CI arguments * Update src/python_testing/modebase_cluster_check.py Co-authored-by: C Freeman <cecille@google.com> * Add name changes * Restyled by autopep8 * Fix typo in comments * Update src/python_testing/modebase_cluster_check.py Co-authored-by: Andrei Litvin <andy314@gmail.com> * Update src/python_testing/modebase_cluster_check.py Co-authored-by: Andrei Litvin <andy314@gmail.com> * Update src/python_testing/modebase_cluster_check.py Co-authored-by: Andrei Litvin <andy314@gmail.com> * Restyled by autopep8 * Add removed assert --------- Co-authored-by: Restyled.io <commits@restyled.io> Co-authored-by: C Freeman <cecille@google.com> Co-authored-by: Andrei Litvin <andy314@gmail.com>
1 parent 211d33a commit 8a0fc4d

File tree

3 files changed

+268
-0
lines changed

3 files changed

+268
-0
lines changed

src/python_testing/TC_TCCM_1_2.py

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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+
18+
# === BEGIN CI TEST ARGUMENTS ===
19+
# test-runner-runs:
20+
# run1:
21+
# app: ${ALL_CLUSTERS_APP}
22+
# app-args: >
23+
# --discriminator 1234
24+
# --KVS kvs1
25+
# --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+
# --endpoint 1
32+
# --trace-to json:${TRACE_TEST_JSON}.json
33+
# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
34+
# factory-reset: true
35+
# quiet: true
36+
# === END CI TEST ARGUMENTS ===
37+
38+
39+
import chip.clusters as Clusters
40+
from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main
41+
from modebase_cluster_check import ModeBaseClusterChecks
42+
43+
CLUSTER = Clusters.RefrigeratorAndTemperatureControlledCabinetMode
44+
45+
46+
class TC_TCCM_1_2(MatterBaseTest, ModeBaseClusterChecks):
47+
48+
def __init__(self, *args):
49+
MatterBaseTest.__init__(self, *args)
50+
ModeBaseClusterChecks.__init__(self,
51+
modebase_derived_cluster=CLUSTER)
52+
53+
def desc_TC_TCCM_1_2(self) -> str:
54+
return "[TC-TCCM-1.2] Cluster attributes with DUT as Server"
55+
56+
def steps_TC_TCCM_1_2(self) -> list[TestStep]:
57+
steps = [
58+
TestStep(1, "Commissioning, already done", is_commissioning=True),
59+
TestStep(2, "TH reads from the DUT the SupportedModes attribute."),
60+
TestStep(3, "TH reads from the DUT the CurrentMode attribute."),
61+
TestStep(4, "TH reads from the DUT the OnMode attribute."),
62+
TestStep(5, "TH reads from the DUT the StartUpMode attribute.")
63+
]
64+
return steps
65+
66+
def pics_TC_TCCM_1_2(self) -> list[str]:
67+
pics = [
68+
"TCCM.S"
69+
]
70+
return pics
71+
72+
@async_test_body
73+
async def test_TC_TCCM_1_2(self):
74+
75+
# Setup common mode check
76+
endpoint = self.get_endpoint(default=1)
77+
78+
self.step(1)
79+
80+
self.step(2)
81+
# Verify common checks for Mode Base as described in the TC-TCCM-1.2
82+
supported_modes = await self.check_supported_modes_and_labels(endpoint=endpoint)
83+
# According to the spec, there should be at least one RapidCool or RapidFreeze tag in
84+
# the ones supported.
85+
additional_tags = [CLUSTER.Enums.ModeTag.kRapidCool,
86+
CLUSTER.Enums.ModeTag.kRapidFreeze]
87+
self.check_tags_in_lists(supported_modes=supported_modes, required_tags=additional_tags)
88+
89+
self.step(3)
90+
# Verify that the CurrentMode attribute has a valid value.
91+
mode = self.cluster.Attributes.CurrentMode
92+
await self.read_and_check_mode(endpoint=endpoint, mode=mode, supported_modes=supported_modes)
93+
94+
self.step(4)
95+
# Verify that the OnMode attribute has a valid value or null.
96+
mode = self.cluster.Attributes.OnMode
97+
await self.read_and_check_mode(endpoint=endpoint, mode=mode,
98+
supported_modes=supported_modes, is_nullable=True)
99+
100+
self.step(5)
101+
# Verify that the StartUpMode has a valid value or null
102+
mode = self.cluster.Attributes.StartUpMode
103+
await self.read_and_check_mode(endpoint=endpoint, mode=mode,
104+
supported_modes=supported_modes, is_nullable=True)
105+
106+
107+
if __name__ == "__main__":
108+
default_matter_test_main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#
2+
# Copyright (c) 2023 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+
import logging
19+
20+
from chip.clusters.Types import NullValue
21+
from mobly import asserts
22+
23+
logger = logging.getLogger(__name__)
24+
25+
# Maximum value for ModeTags according to specs is 16bits.
26+
MAX_MODE_TAG = 0xFFFF
27+
# According to specs, the specific MfgTags should be defined in the range 0x8000 - 0xBFFF
28+
START_MFGTAGS_RANGE = 0x8000
29+
END_MFGTAGS_RANGE = 0xBFFF
30+
31+
32+
class ModeBaseClusterChecks:
33+
""" Class that holds the common Mode checks between TCs
34+
35+
Several TCs have similar checks in place for functionality that is common among them.
36+
This class holds most of this common functionality to avoid duplicating code with the same validations.
37+
38+
Link to spec:
39+
https://github.com/CHIP-Specifications/chip-test-plans/blob/master/src/cluster/modebase_common.adoc
40+
41+
42+
Attributes:
43+
modebase_derived_cluster: A reference to the cluster to be tested, it should be a derived from the Mode Base cluster.
44+
"""
45+
46+
def __init__(self, modebase_derived_cluster):
47+
self.mode_tags = [tag.value for tag in modebase_derived_cluster.Enums.ModeTag]
48+
self.cluster = modebase_derived_cluster
49+
self.attributes = modebase_derived_cluster.Attributes
50+
51+
async def check_supported_modes_and_labels(self, endpoint):
52+
""" Verifies the device supported modes and labels.
53+
54+
Checks that the SupportedModes attribute has the expected structure and values like:
55+
- Between 2 and 255 entries.
56+
- The Mode values of all entries are unique.
57+
- The Label values of all entries are unique.
58+
59+
Args:
60+
endpoint: The endpoint used for the requests to the cluster.
61+
62+
Returns:
63+
A list of ModeOptionStruct supported by the cluster.
64+
"""
65+
# Get the supported modes
66+
supported_modes = await self.read_single_attribute_check_success(endpoint=endpoint,
67+
cluster=self.cluster,
68+
attribute=self.attributes.SupportedModes)
69+
70+
# Check if the list of supported modes is larger than 2
71+
asserts.assert_greater_equal(len(supported_modes), 2, "SupportedModes must have at least 2 entries!")
72+
# Check that supported modes are less than 255
73+
asserts.assert_less_equal(len(supported_modes), 255, "SupportedModes must have at most 255 entries!")
74+
75+
# Check for repeated labels or modes
76+
labels = set()
77+
modes = set()
78+
for mode_option_struct in supported_modes:
79+
# Verify that the modes in all ModeOptionStruct in SupportedModes are unique.
80+
if mode_option_struct.mode in modes:
81+
asserts.fail("SupportedModes can't have repeated Mode values")
82+
else:
83+
modes.add(mode_option_struct.mode)
84+
# Verify that the labels in all ModeOptionStruct in SupportedModes are unique.
85+
if mode_option_struct.label in labels:
86+
asserts.fail("SupportedModes can't have repeated Label values")
87+
else:
88+
labels.add(mode_option_struct.label)
89+
90+
return supported_modes
91+
92+
def check_tags_in_lists(self, supported_modes, required_tags=None):
93+
""" Validates the ModeTags values.
94+
95+
This function evaluates the ModeTags of each ModeOptionStruct:
96+
- Should have at least one tag.
97+
- Should be maximum 16bits in size.
98+
- Should be a Mfg tag or one of the supported ones (either common or specific).
99+
- Should have at least one common or specific tag.
100+
- If defined, verify that at least one of the "required_tags" exists.
101+
102+
Args:
103+
supported_modes: A list of ModeOptionStruct.
104+
required_tags: List of tags that are required according to the cluster spec.
105+
"""
106+
# Verify the ModeTags on each ModeOptionStruct
107+
for mode_option_struct in supported_modes:
108+
# Shuld have at least one entry
109+
if len(mode_option_struct.modeTags) == 0:
110+
asserts.fail("The ModeTags field should have at least one entry.")
111+
112+
# Check each ModelTag
113+
at_least_one_common_or_derived = False
114+
for tag in mode_option_struct.modeTags:
115+
# Value should not larger than 16bits
116+
if not (0 <= tag.value <= MAX_MODE_TAG):
117+
asserts.fail("Tag should not be larger than 16bits.")
118+
119+
# Check if is tag is common, derived or mfg.
120+
is_mfg = (START_MFGTAGS_RANGE <= tag.value <= END_MFGTAGS_RANGE)
121+
if not (is_mfg or tag.value in self.mode_tags):
122+
asserts.fail("Mode tag value is not a common, derived or vendor tag.")
123+
124+
# Confirm if tag is common or derived.
125+
if not is_mfg:
126+
at_least_one_common_or_derived = True
127+
128+
if not at_least_one_common_or_derived:
129+
asserts.fail("There should be at least one common or derived tag on each ModeOptionsStruct")
130+
131+
if required_tags:
132+
has_required_tags = False
133+
for mode_options_struct in supported_modes:
134+
has_required_tags = any(tag.value in required_tags for tag in mode_options_struct.modeTags)
135+
if has_required_tags:
136+
break
137+
asserts.assert_true(has_required_tags, "No ModeOptionsStruct has the required tags.")
138+
139+
async def read_and_check_mode(self, endpoint, mode, supported_modes, is_nullable=False):
140+
"""Evaluates the current mode
141+
142+
This functions checks if the requested mode attribute has a valid value from the SupportedModes,
143+
supports optional nullable values.
144+
145+
Args:
146+
endpoint: The endpoint used for the requests to the cluster.
147+
mode: Mode that will be verified.
148+
supported_modes: A list of ModeOptionStruct.
149+
is_nullable: Optional argument to indicate if the tested mode allows NullValue
150+
"""
151+
mode_value = await self.read_single_attribute_check_success(endpoint=endpoint,
152+
cluster=self.cluster,
153+
attribute=mode)
154+
supported_modes_dut = {mode_option_struct.mode for mode_option_struct in supported_modes}
155+
is_valid = mode_value in supported_modes_dut
156+
if is_nullable and mode_value == NullValue:
157+
is_valid = True
158+
asserts.assert_true(is_valid, f"{mode} not supported")

src/python_testing/test_metadata.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ not_automated:
7777
reason: Shared code for TC_*, not a standalone test
7878
- name: test_plan_table_generator.py
7979
reason: Code/Test not being used or not shared code for any other tests
80+
- name: modebase_cluster_check.py
81+
reason: Shared code for Modebase derived clusters, not a standalone test.
8082

8183
# This is a list of slow tests (just arbitrarily picked around 20 seconds)
8284
# used in some script reporting for "be patient" messages as well as potentially

0 commit comments

Comments
 (0)