Skip to content

Commit 43661ab

Browse files
[OPSTATE] Add Q test script for CountdownTime (project-chip#34632)
* Add Q test * Added test to test set * Remove unused var * Restyled by autopep8 * Restyled by isort * Fix name * Use pics over other method * Removed unused stuff * Added pipe commands * Fix reset * Get example to report appropriate changes. * WiP * Added some comments * Changes to make things work * Removed dev msgs * Missed some * Removed dev msgs * Straggler * Restyled by clang-format * Restyled by autopep8 * Restyled by isort * Commented unused var * Update examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp * Fix bug --------- Co-authored-by: Restyled.io <commits@restyled.io>
1 parent d938a28 commit 43661ab

File tree

7 files changed

+247
-4
lines changed

7 files changed

+247
-4
lines changed

.github/workflows/tests.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,7 @@ jobs:
578578
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_OPSTATE_2_3.py'
579579
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_OPSTATE_2_4.py'
580580
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_OPSTATE_2_5.py'
581+
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_OPSTATE_2_6.py'
581582
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_OVENOPSTATE_2_1.py'
582583
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_OVENOPSTATE_2_2.py'
583584
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_OVENOPSTATE_2_3.py'

examples/all-clusters-app/all-clusters-common/include/operational-state-delegate-impl.h

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ class OperationalStateDelegate : public GenericOperationalStateDelegateImpl
138138
};
139139

140140
Instance * GetOperationalStateInstance();
141+
OperationalStateDelegate * GetOperationalStateDelegate();
141142

142143
void Shutdown();
143144

examples/all-clusters-app/all-clusters-common/src/operational-state-delegate-impl.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ void GenericOperationalStateDelegateImpl::HandlePauseStateCallback(GenericOperat
5959
auto error = GetInstance()->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kPaused));
6060
if (error == CHIP_NO_ERROR)
6161
{
62+
GetInstance()->UpdateCountdownTimeFromDelegate();
6263
err.Set(to_underlying(ErrorStateEnum::kNoError));
6364
}
6465
else
@@ -73,6 +74,7 @@ void GenericOperationalStateDelegateImpl::HandleResumeStateCallback(GenericOpera
7374
auto error = GetInstance()->SetOperationalState(to_underlying(OperationalStateEnum::kRunning));
7475
if (error == CHIP_NO_ERROR)
7576
{
77+
GetInstance()->UpdateCountdownTimeFromDelegate();
7678
err.Set(to_underlying(ErrorStateEnum::kNoError));
7779
}
7880
else
@@ -96,6 +98,7 @@ void GenericOperationalStateDelegateImpl::HandleStartStateCallback(GenericOperat
9698
auto error = GetInstance()->SetOperationalState(to_underlying(OperationalStateEnum::kRunning));
9799
if (error == CHIP_NO_ERROR)
98100
{
101+
GetInstance()->UpdateCountdownTimeFromDelegate();
99102
(void) DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds16(1), onOperationalStateTimerTick, this);
100103
err.Set(to_underlying(ErrorStateEnum::kNoError));
101104
}
@@ -113,6 +116,8 @@ void GenericOperationalStateDelegateImpl::HandleStopStateCallback(GenericOperati
113116
{
114117
(void) DeviceLayer::SystemLayer().CancelTimer(onOperationalStateTimerTick, this);
115118

119+
GetInstance()->UpdateCountdownTimeFromDelegate();
120+
116121
OperationalState::GenericOperationalError current_err(to_underlying(OperationalState::ErrorStateEnum::kNoError));
117122
GetInstance()->GetCurrentOperationalError(current_err);
118123

@@ -152,6 +157,11 @@ static void onOperationalStateTimerTick(System::Layer * systemLayer, void * data
152157
delegate->mPausedTime++;
153158
}
154159
}
160+
else if (!countdown_time.IsNull() && countdown_time.Value() <= 0)
161+
{
162+
OperationalState::GenericOperationalError noError(to_underlying(OperationalState::ErrorStateEnum::kNoError));
163+
delegate->HandleStopStateCallback(noError);
164+
}
155165

156166
if (state == OperationalState::OperationalStateEnum::kRunning || state == OperationalState::OperationalStateEnum::kPaused)
157167
{
@@ -173,6 +183,11 @@ OperationalState::Instance * OperationalState::GetOperationalStateInstance()
173183
return gOperationalStateInstance;
174184
}
175185

186+
OperationalStateDelegate * OperationalState::GetOperationalStateDelegate()
187+
{
188+
return gOperationalStateDelegate;
189+
}
190+
176191
void OperationalState::Shutdown()
177192
{
178193
if (gOperationalStateInstance != nullptr)

examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp

+47-3
Original file line numberDiff line numberDiff line change
@@ -667,21 +667,65 @@ void AllClustersAppCommandHandler::OnModeChangeHandler(std::string device, std::
667667

668668
void AllClustersAppCommandHandler::OnOperationalStateChange(std::string device, std::string operation, Json::Value param)
669669
{
670-
OperationalState::Instance * operationalStateInstance = nullptr;
671670
if (device == "Generic")
672671
{
673-
operationalStateInstance = OperationalState::GetOperationalStateInstance();
672+
OnGenericOperationalStateChange(device, operation, param);
674673
}
675674
else if (device == "Oven")
676675
{
677-
operationalStateInstance = OvenCavityOperationalState::GetOperationalStateInstance();
676+
OnOvenOperationalStateChange(device, operation, param);
678677
}
679678
else
680679
{
681680
ChipLogDetail(NotSpecified, "Invalid device type : %s", device.c_str());
682681
return;
683682
}
683+
}
684+
685+
void AllClustersAppCommandHandler::OnGenericOperationalStateChange(std::string device, std::string operation, Json::Value param)
686+
{
687+
OperationalState::Instance * operationalStateInstance = OperationalState::GetOperationalStateInstance();
688+
OperationalState::OperationalStateDelegate * operationalStateDelegate = OperationalState::GetOperationalStateDelegate();
689+
OperationalState::GenericOperationalError noError(to_underlying(OperationalState::ErrorStateEnum::kNoError));
690+
OperationalState::OperationalStateEnum state =
691+
static_cast<OperationalState::OperationalStateEnum>(operationalStateInstance->GetCurrentOperationalState());
692+
if (operation == "Start")
693+
{
694+
operationalStateDelegate->HandleStartStateCallback(noError);
695+
}
696+
else if (operation == "Resume")
697+
{
698+
operationalStateDelegate->HandleResumeStateCallback(noError);
699+
}
700+
else if (operation == "Pause")
701+
{
702+
operationalStateDelegate->HandlePauseStateCallback(noError);
703+
}
704+
else if (operation == "Stop" && state == OperationalState::OperationalStateEnum::kRunning)
705+
{
706+
operationalStateDelegate->HandleStopStateCallback(noError);
707+
}
708+
else if (operation == "OnFault")
709+
{
710+
uint8_t event_id = to_underlying(OperationalState::ErrorStateEnum::kUnableToCompleteOperation);
711+
if (!param.isNull())
712+
{
713+
event_id = to_underlying(static_cast<OperationalState::ErrorStateEnum>(param.asUInt()));
714+
}
684715

716+
OperationalState::GenericOperationalError err(event_id);
717+
operationalStateInstance->OnOperationalErrorDetected(err);
718+
}
719+
else
720+
{
721+
ChipLogDetail(NotSpecified, "Invalid operation : %s", operation.c_str());
722+
return;
723+
}
724+
}
725+
726+
void AllClustersAppCommandHandler::OnOvenOperationalStateChange(std::string device, std::string operation, Json::Value param)
727+
{
728+
OperationalState::Instance * operationalStateInstance = OvenCavityOperationalState::GetOperationalStateInstance();
685729
if (operation == "Start" || operation == "Resume")
686730
{
687731
operationalStateInstance->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kRunning));

examples/all-clusters-app/linux/AllClustersCommandDelegate.h

+10
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,16 @@ class AllClustersAppCommandHandler
105105
* Should be called when it is necessary to change the operational state as a manual operation.
106106
*/
107107
void OnOperationalStateChange(std::string device, std::string operation, Json::Value param);
108+
109+
/**
110+
* Should be called when it is necessary to change the operational state as a manual operation.
111+
*/
112+
void OnGenericOperationalStateChange(std::string device, std::string operation, Json::Value param);
113+
114+
/**
115+
* Should be called when it is necessary to change the operational state as a manual operation.
116+
*/
117+
void OnOvenOperationalStateChange(std::string device, std::string operation, Json::Value param);
108118
};
109119

110120
class AllClustersCommandDelegate : public NamedPipeCommandDelegate

src/python_testing/TC_OPSTATE_2_6.py

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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: ${ALL_CLUSTERS_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: --endpoint 1 --int-arg PIXIT.WAITTIME.REBOOT:5 --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+
31+
import chip.clusters as Clusters
32+
from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main
33+
from TC_OpstateCommon import TC_OPSTATE_BASE, TestInfo
34+
35+
36+
class TC_OPSTATE_2_6(MatterBaseTest, TC_OPSTATE_BASE):
37+
def __init__(self, *args):
38+
super().__init__(*args)
39+
40+
test_info = TestInfo(
41+
pics_code="OPSTATE",
42+
cluster=Clusters.OperationalState
43+
)
44+
45+
super().setup_base(test_info=test_info)
46+
47+
def steps_TC_OPSTATE_2_6(self) -> list[TestStep]:
48+
return self.steps_TC_OPSTATE_BASE_2_6()
49+
50+
def pics_TC_OPSTATE_2_6(self) -> list[str]:
51+
return ["OPSTATE.S", "OPSTATE.S.A0002"]
52+
53+
@async_test_body
54+
async def test_TC_OPSTATE_2_6(self):
55+
# endpoint = self.matter_test_config.endpoint
56+
57+
# await self.TEST_TC_OPSTATE_BASE_2_6(endpoint=endpoint)
58+
await self.TEST_TC_OPSTATE_BASE_2_6(endpoint=1)
59+
60+
61+
if __name__ == "__main__":
62+
default_matter_test_main()

src/python_testing/TC_OpstateCommon.py

+111-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from chip.clusters.Attribute import EventReadResult, SubscriptionTransaction
2929
from chip.clusters.Types import NullValue
3030
from chip.interaction_model import InteractionModelError, Status
31-
from matter_testing_support import EventChangeCallback, TestStep
31+
from matter_testing_support import ClusterAttributeChangeAccumulator, EventChangeCallback, TestStep
3232
from mobly import asserts
3333

3434

@@ -1082,6 +1082,7 @@ async def TEST_TC_OPSTATE_BASE_2_5(self, endpoint=1):
10821082

10831083
# STEP 7: TH waits for initial-countdown-time
10841084
self.step(7)
1085+
logging.info(f'Sleeping for {initial_countdown_time:.1f} seconds.')
10851086
time.sleep(initial_countdown_time)
10861087

10871088
# STEP 8: TH sends Stop command to the DUT
@@ -1221,3 +1222,112 @@ async def TEST_TC_OPSTATE_BASE_2_5(self, endpoint=1):
12211222
self.skip_step(20)
12221223
self.skip_step(21)
12231224
self.skip_step(22)
1225+
1226+
############################
1227+
# TEST CASE 2.6 - Optional Reports with DUT as Server
1228+
############################
1229+
def steps_TC_OPSTATE_BASE_2_6(self) -> list[TestStep]:
1230+
steps = [TestStep(1, "Commissioning, already done", is_commissioning=True),
1231+
TestStep(2, "Subscribe to CountdownTime attribute"),
1232+
TestStep(3, "Manually put the DUT into a state where it will use the CountdownTime attribute, "
1233+
"the initial value of the CountdownTime is greater than 30, "
1234+
"and it will begin counting down the CountdownTime attribute."),
1235+
TestStep(4, "Over a period of 30 seconds, TH counts all report transactions with an attribute "
1236+
"report for the CountdownTime attribute in numberOfReportsReceived"),
1237+
TestStep(5, "Until the current operation finishes, TH counts all report transactions with "
1238+
"an attribute report for the CountdownTime attribute in numberOfReportsReceived and saves up to 5 such reports."),
1239+
TestStep(6, "Manually put the DUT into a state where it will use the CountdownTime attribute, "
1240+
"the initial value of the CountdownTime is greater than 30, and it will begin counting down the CountdownTime attribute."),
1241+
TestStep(7, "TH reads from the DUT the OperationalState attribute"),
1242+
TestStep(8, "Manually put the device in the Paused(0x02) operational state")
1243+
]
1244+
return steps
1245+
1246+
async def TEST_TC_OPSTATE_BASE_2_6(self, endpoint=1):
1247+
cluster = self.test_info.cluster
1248+
attributes = cluster.Attributes
1249+
1250+
self.init_test()
1251+
1252+
# commission
1253+
self.step(1)
1254+
1255+
# Note that this does a subscribe-all instead of subscribing only to the CountdownTime attribute.
1256+
# To-Do: Update the TP to subscribe-all.
1257+
self.step(2)
1258+
sub_handler = ClusterAttributeChangeAccumulator(cluster)
1259+
await sub_handler.start(self.default_controller, self.dut_node_id, endpoint)
1260+
1261+
self.step(3)
1262+
if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ST_RUNNING")):
1263+
self.send_manual_or_pipe_command(name="OperationalStateChange",
1264+
device=self.device,
1265+
operation="Start")
1266+
time.sleep(1)
1267+
await self.read_and_expect_value(endpoint=endpoint,
1268+
attribute=attributes.OperationalState,
1269+
expected_value=cluster.Enums.OperationalStateEnum.kRunning)
1270+
count = sub_handler.attribute_report_counts[attributes.CountdownTime]
1271+
asserts.assert_greater(count, 0, "Did not receive any reports for CountdownTime")
1272+
else:
1273+
self.skip_step(3)
1274+
1275+
sub_handler.reset()
1276+
self.step(4)
1277+
logging.info('Test will now collect data for 30 seconds')
1278+
time.sleep(30)
1279+
1280+
count = sub_handler.attribute_report_counts[attributes.CountdownTime]
1281+
sub_handler.reset()
1282+
asserts.assert_less_equal(count, 5, "Received more than 5 reports for CountdownTime")
1283+
asserts.assert_greater_equal(count, 0, "Did not receive any reports for CountdownTime")
1284+
1285+
attr_value = await self.read_expect_success(
1286+
endpoint=endpoint,
1287+
attribute=attributes.OperationalState)
1288+
if attr_value == cluster.Enums.OperationalStateEnum.kRunning:
1289+
self.step(5)
1290+
wait_count = 0
1291+
while (attr_value != cluster.Enums.OperationalStateEnum.kStopped) and (wait_count < 20):
1292+
time.sleep(1)
1293+
wait_count = wait_count + 1
1294+
attr_value = await self.read_expect_success(
1295+
endpoint=endpoint,
1296+
attribute=attributes.OperationalState)
1297+
count = sub_handler.attribute_report_counts[attributes.CountdownTime]
1298+
asserts.assert_less_equal(count, 5, "Received more than 5 reports for CountdownTime")
1299+
asserts.assert_greater(count, 0, "Did not receive any reports for CountdownTime")
1300+
else:
1301+
self.skip_step(5)
1302+
1303+
sub_handler.reset()
1304+
self.step(6)
1305+
if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ST_RUNNING")):
1306+
self.send_manual_or_pipe_command(name="OperationalStateChange",
1307+
device=self.device,
1308+
operation="Start")
1309+
time.sleep(1)
1310+
await self.read_and_expect_value(endpoint=endpoint,
1311+
attribute=attributes.OperationalState,
1312+
expected_value=cluster.Enums.OperationalStateEnum.kRunning)
1313+
count = sub_handler.attribute_report_counts[attributes.CountdownTime]
1314+
asserts.assert_greater(count, 0, "Did not receive any reports for CountdownTime")
1315+
else:
1316+
self.skip_step(6)
1317+
1318+
self.step(7)
1319+
await self.read_and_expect_value(endpoint=endpoint,
1320+
attribute=attributes.OperationalState,
1321+
expected_value=cluster.Enums.OperationalStateEnum.kRunning)
1322+
1323+
sub_handler.reset()
1324+
self.step(8)
1325+
if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ST_PAUSED")):
1326+
self.send_manual_or_pipe_command(name="OperationalStateChange",
1327+
device=self.device,
1328+
operation="Pause")
1329+
time.sleep(1)
1330+
count = sub_handler.attribute_report_counts[attributes.CountdownTime]
1331+
asserts.assert_greater(count, 0, "Did not receive any reports for CountdownTime")
1332+
else:
1333+
self.skip_step(8)

0 commit comments

Comments
 (0)