Skip to content

Commit 82b1389

Browse files
committed
TC-LVL-2.3: Add
1 parent d9bcdd3 commit 82b1389

File tree

2 files changed

+203
-2
lines changed

2 files changed

+203
-2
lines changed

src/python_testing/TC_LVL_2_3.py

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