Skip to content

Commit 29b055a

Browse files
authored
TC-LVL-2.3: Add (project-chip#34610)
* TC-LVL-2.3: Add * add to tests.yaml * linter
1 parent 1baf55d commit 29b055a

File tree

3 files changed

+199
-2
lines changed

3 files changed

+199
-2
lines changed

.github/workflows/tests.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,7 @@ jobs:
598598
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_RVCOPSTATE_2_4.py'
599599
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_SC_7_1.py'
600600
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_SWTCH.py'
601+
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TC_LVL_2_3.py'
601602
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --load-from-env /tmp/test_env.yaml --script src/python_testing/TCP_Tests.py'
602603
scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --script "src/python_testing/TestConformanceSupport.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"'
603604
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"'

src/python_testing/TC_LVL_2_3.py

+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
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+
# 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: run1
23+
# test-runner-run/run1/app: ${CHIP_MICROWAVE_OVEN_APP}
24+
# test-runner-run/run1/factoryreset: True
25+
# test-runner-run/run1/quiet: True
26+
# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json
27+
# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
28+
# === END CI TEST ARGUMENTS ===
29+
30+
import logging
31+
import time
32+
33+
import chip.clusters as Clusters
34+
import test_plan_support
35+
from matter_testing_support import (ClusterAttributeChangeAccumulator, MatterBaseTest, TestStep, default_matter_test_main,
36+
has_cluster, per_endpoint_test)
37+
from mobly import asserts
38+
39+
40+
class TC_LVL_2_3(MatterBaseTest):
41+
42+
def steps_TC_LVL_2_3(self) -> list[TestStep]:
43+
THRead = "TH reads"
44+
THcommand = "TH sends the command"
45+
return [TestStep(1, test_plan_support.commission_if_required(), is_commissioning=True),
46+
TestStep(2, f"{THRead} FeatureMap attribute."),
47+
TestStep(3, f"{THRead} MaxLevel attribute and store value as maxLevel", test_plan_support.verify_success()),
48+
TestStep(4, f"{THcommand} MoveWithOnOff with MoveMode field set to Down and remaining fields set to 0",
49+
test_plan_support.verify_success()),
50+
TestStep(5, f"{THRead} CurrentLevel attribute and store value as startCurrentLevel",
51+
test_plan_support.verify_success()),
52+
TestStep(6, "Set up a subscription wildcard subscription for the Level Control Cluster, with MinIntervalFloor set to 0, MaxIntervalCeiling set to 30 and KeepSubscriptions set to false",
53+
"Subscription successfully established"),
54+
TestStep(7, f"{THcommand} MoveToLevel with Level field set to maxLevel, TransitionTime field set to 10 and remaining fields set to 0",
55+
test_plan_support.verify_success()),
56+
TestStep(8, "TH stores the reported values of CurrentLevel in all incoming reports for CurrentLevel attribute, that contains data in reportedCurrentLevelValuesList, over a period of 30 seconds."),
57+
TestStep(9, "TH verifies that reportedCurrentLevelValuesList does not contain more than 10 entries for CurrentLevel",
58+
"reportedCurrentLevelValuesList has 10 or less entries in the list"),
59+
TestStep(10, "If reportedCurrentLevelValuesList only contain a single entry, TH verifies the value of the entry is equal to maxLevel",
60+
"The entry in reportedCurrentLevelValuesList is equal to maxLevel"),
61+
TestStep(11, "If reportedCurrentLevelValuesList contains two or more entries, TH verifies the value of the first entry is larger than startCurrentLevel",
62+
"The first entry in reportedCurrentLevelValuesList is equal to or larger than to startCurrentLevel"),
63+
TestStep(12, "If reportedCurrentLevelValuesList contains two or more entries, TH verifies the value of the last entry is equal to maxLevel",
64+
"The last entry in reportedCurrentLevelValuesList is equal to maxLevel"),
65+
TestStep(13, "If the LT feature is not supported, skip remaining steps and end test case"),
66+
# 14 is missing in the test plan
67+
TestStep(15, "TH stores the reported values of RemainingTime in all incoming reports for RemainingTime attribute, that contains data in reportedRemainingTimeValuesList."),
68+
TestStep(16, f" {THcommand} MoveToLevel with Level field set to startCurrentLevel, TransitionTime field set to 10 and remaining fields set to 0",
69+
test_plan_support.verify_success()),
70+
TestStep(17, "Wait for 5 seconds"),
71+
TestStep(18, f"{THcommand} MoveToLevel with Level field set to startCurrentLevel, TransitionTime field set to 15 and remaining fields set to 0",
72+
test_plan_support.verify_success()),
73+
TestStep(19, "Wait for 20 seconds"),
74+
TestStep(20, "TH verifies reportedRemainingTimeValuesList contains three entries",
75+
"reportedRemainingTimeValuesList has 3 entries in the list"),
76+
TestStep(21, "TH verifies the first entry in reportedRemainingTimeValuesList is 10",
77+
"The first entry in reportedRemainingTimeValuesList is equal to 10"),
78+
TestStep(22, "TH verifies the second entry in reportedRemainingTimeValuesList is 15",
79+
"The second entry in reportedRemainingTimeValuesList is equal to 15"),
80+
TestStep(23, "TH verifies the third entry in reportedRemainingTimeValuesList is 0",
81+
"The third entry in reportedRemainingTimeValuesList is equal to 0")
82+
]
83+
84+
@per_endpoint_test(has_cluster(Clusters.LevelControl))
85+
async def test_TC_LVL_2_3(self):
86+
# Commissioning - already done
87+
self.step(1)
88+
89+
lvl = Clusters.LevelControl
90+
91+
self.step(2)
92+
feature_map = await self.read_single_attribute_check_success(cluster=lvl, attribute=lvl.Attributes.FeatureMap)
93+
94+
self.step(3)
95+
max_level = await self.read_single_attribute_check_success(cluster=lvl, attribute=lvl.Attributes.MaxLevel)
96+
97+
self.step(4)
98+
cmd = Clusters.LevelControl.Commands.MoveWithOnOff(moveMode=lvl.Enums.MoveModeEnum.kDown)
99+
await self.send_single_cmd(cmd)
100+
# NOTE: added this sleep to let the DUT have some time to move
101+
logging.info("Test waits for 5 seconds")
102+
time.sleep(5)
103+
104+
self.step(5)
105+
start_current_level = await self.read_single_attribute_check_success(cluster=lvl, attribute=lvl.Attributes.CurrentLevel)
106+
107+
self.step(6)
108+
sub_handler = ClusterAttributeChangeAccumulator(lvl)
109+
await sub_handler.start(self.default_controller, self.dut_node_id, self.matter_test_config.endpoint)
110+
111+
self.step(7)
112+
# NOTE: had to use the WithOnOff version of this command because the dut is off at this point thanks to the above command
113+
# TODO: Need to check above and here that the on/off cluster is actually implemented.
114+
cmd = lvl.Commands.MoveToLevelWithOnOff(level=max_level, transitionTime=100, optionsMask=0, optionsOverride=0)
115+
await self.send_single_cmd(cmd)
116+
117+
self.step(8)
118+
logging.info('Test will now collect data for 30 seconds')
119+
time.sleep(30)
120+
121+
self.step(9)
122+
count = sub_handler.attribute_report_counts[lvl.Attributes.CurrentLevel]
123+
asserts.assert_less_equal(count, 10, "Received more than 10 reports for CurrentLevel")
124+
asserts.assert_greater(count, 0, "Did not receive any reports for CurrentLevel")
125+
126+
self.step(10)
127+
if count == 1:
128+
entry = sub_handler.attribute_reports[lvl.Attributes.CurrentLevel][-1]
129+
asserts.assert_equal(entry.value, max_level, "Entry is not equal to max level")
130+
131+
if count > 1:
132+
self.step(11)
133+
last_value = start_current_level
134+
for e in sub_handler.attribute_reports[lvl.Attributes.CurrentLevel]:
135+
asserts.assert_greater_equal(e.value, last_value, "Values are not increasing")
136+
137+
self.step(12)
138+
asserts.assert_equal(e.value, max_level, "Last entry is not max value")
139+
else:
140+
self.skip_step(11)
141+
self.skip_step(12)
142+
143+
self.step(13)
144+
if (lvl.Bitmaps.Feature.kLighting & feature_map) == 0:
145+
self.skip_all_remaining_steps(15)
146+
147+
self.step(15)
148+
# reports are stored by the handler, so just reset so we get a clean look
149+
sub_handler.reset()
150+
151+
self.step(16)
152+
cmd = Clusters.LevelControl.Commands.MoveToLevel(
153+
level=start_current_level, transitionTime=100, optionsMask=0, optionsOverride=0)
154+
await self.send_single_cmd(cmd)
155+
156+
self.step(17)
157+
logging.info("Test waits for 5 seconds")
158+
time.sleep(5)
159+
160+
self.step(18)
161+
cmd = Clusters.LevelControl.Commands.MoveToLevel(
162+
level=start_current_level, transitionTime=150, optionsMask=0, optionsOverride=0)
163+
await self.send_single_cmd(cmd)
164+
165+
self.step(19)
166+
logging.info("Test waits for 20 seconds")
167+
time.sleep(20)
168+
169+
self.step(20)
170+
count = sub_handler.attribute_report_counts[lvl.Attributes.RemainingTime]
171+
asserts.assert_equal(count, 3, "Unexpected number of remaining time reports")
172+
173+
self.step(21)
174+
remaining_time = sub_handler.attribute_reports[lvl.Attributes.RemainingTime]
175+
logging.info(f'Reamining time reports: {remaining_time}')
176+
asserts.assert_equal(remaining_time[0].value, 100, "Unexpected first RemainingTime report")
177+
178+
self.step(22)
179+
asserts.assert_almost_equal(remaining_time[1].value, 150, delta=10, msg="Unexpected second RemainingTime report")
180+
181+
self.step(23)
182+
asserts.assert_equal(remaining_time[2].value, 0, "Unexpected last RemainingTime report")
183+
184+
185+
if __name__ == "__main__":
186+
default_matter_test_main()

src/python_testing/matter_testing_support.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -327,14 +327,19 @@ class AttributeValue:
327327

328328
class ClusterAttributeChangeAccumulator:
329329
def __init__(self, expected_cluster: ClusterObjects.Cluster):
330-
self._q = queue.Queue()
331330
self._expected_cluster = expected_cluster
332331
self._subscription = None
332+
self.reset()
333+
334+
def reset(self):
333335
self._attribute_report_counts = {}
334-
attrs = [cls for name, cls in inspect.getmembers(expected_cluster.Attributes) if inspect.isclass(
336+
attrs = [cls for name, cls in inspect.getmembers(self._expected_cluster.Attributes) if inspect.isclass(
335337
cls) and issubclass(cls, ClusterObjects.ClusterAttributeDescriptor)]
338+
self._attribute_reports = {}
336339
for a in attrs:
337340
self._attribute_report_counts[a] = 0
341+
self._attribute_reports[a] = []
342+
self._q = queue.Queue()
338343

339344
async def start(self, dev_ctrl, node_id: int, endpoint: int, fabric_filtered: bool = False, min_interval_sec: int = 0, max_interval_sec: int = 5) -> Any:
340345
"""This starts a subscription for attributes on the specified node_id and endpoint. The cluster is specified when the class instance is created."""
@@ -358,6 +363,7 @@ def __call__(self, path: TypedAttributePath, transaction: SubscriptionTransactio
358363
logging.info(f"Got subscription report for {path.AttributeType}: {data}")
359364
self._q.put(value)
360365
self._attribute_report_counts[path.AttributeType] += 1
366+
self._attribute_reports[path.AttributeType].append(value)
361367

362368
@property
363369
def attribute_queue(self) -> queue.Queue:
@@ -367,6 +373,10 @@ def attribute_queue(self) -> queue.Queue:
367373
def attribute_report_counts(self) -> dict[ClusterObjects.ClusterAttributeDescriptor, int]:
368374
return self._attribute_report_counts
369375

376+
@property
377+
def attribute_reports(self) -> dict[ClusterObjects.ClusterAttributeDescriptor, AttributeValue]:
378+
return self._attribute_reports
379+
370380

371381
class InternalTestRunnerHooks(TestRunnerHooks):
372382

0 commit comments

Comments
 (0)