Skip to content

Commit

Permalink
[OPSTATE] Add Q test script for CountdownTime (project-chip#34632)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
2 people authored and hasty committed Aug 1, 2024
1 parent b2e22c7 commit 12d0123
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 4 deletions.
1 change: 1 addition & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,7 @@ jobs:
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'
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'
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'
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'
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'
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'
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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ class OperationalStateDelegate : public GenericOperationalStateDelegateImpl
};

Instance * GetOperationalStateInstance();
OperationalStateDelegate * GetOperationalStateDelegate();

void Shutdown();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ void GenericOperationalStateDelegateImpl::HandlePauseStateCallback(GenericOperat
auto error = GetInstance()->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kPaused));
if (error == CHIP_NO_ERROR)
{
GetInstance()->UpdateCountdownTimeFromDelegate();
err.Set(to_underlying(ErrorStateEnum::kNoError));
}
else
Expand All @@ -73,6 +74,7 @@ void GenericOperationalStateDelegateImpl::HandleResumeStateCallback(GenericOpera
auto error = GetInstance()->SetOperationalState(to_underlying(OperationalStateEnum::kRunning));
if (error == CHIP_NO_ERROR)
{
GetInstance()->UpdateCountdownTimeFromDelegate();
err.Set(to_underlying(ErrorStateEnum::kNoError));
}
else
Expand All @@ -96,6 +98,7 @@ void GenericOperationalStateDelegateImpl::HandleStartStateCallback(GenericOperat
auto error = GetInstance()->SetOperationalState(to_underlying(OperationalStateEnum::kRunning));
if (error == CHIP_NO_ERROR)
{
GetInstance()->UpdateCountdownTimeFromDelegate();
(void) DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds16(1), onOperationalStateTimerTick, this);
err.Set(to_underlying(ErrorStateEnum::kNoError));
}
Expand All @@ -113,6 +116,8 @@ void GenericOperationalStateDelegateImpl::HandleStopStateCallback(GenericOperati
{
(void) DeviceLayer::SystemLayer().CancelTimer(onOperationalStateTimerTick, this);

GetInstance()->UpdateCountdownTimeFromDelegate();

OperationalState::GenericOperationalError current_err(to_underlying(OperationalState::ErrorStateEnum::kNoError));
GetInstance()->GetCurrentOperationalError(current_err);

Expand Down Expand Up @@ -152,6 +157,11 @@ static void onOperationalStateTimerTick(System::Layer * systemLayer, void * data
delegate->mPausedTime++;
}
}
else if (!countdown_time.IsNull() && countdown_time.Value() <= 0)
{
OperationalState::GenericOperationalError noError(to_underlying(OperationalState::ErrorStateEnum::kNoError));
delegate->HandleStopStateCallback(noError);
}

if (state == OperationalState::OperationalStateEnum::kRunning || state == OperationalState::OperationalStateEnum::kPaused)
{
Expand All @@ -173,6 +183,11 @@ OperationalState::Instance * OperationalState::GetOperationalStateInstance()
return gOperationalStateInstance;
}

OperationalStateDelegate * OperationalState::GetOperationalStateDelegate()
{
return gOperationalStateDelegate;
}

void OperationalState::Shutdown()
{
if (gOperationalStateInstance != nullptr)
Expand Down
50 changes: 47 additions & 3 deletions examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -667,21 +667,65 @@ void AllClustersAppCommandHandler::OnModeChangeHandler(std::string device, std::

void AllClustersAppCommandHandler::OnOperationalStateChange(std::string device, std::string operation, Json::Value param)
{
OperationalState::Instance * operationalStateInstance = nullptr;
if (device == "Generic")
{
operationalStateInstance = OperationalState::GetOperationalStateInstance();
OnGenericOperationalStateChange(device, operation, param);
}
else if (device == "Oven")
{
operationalStateInstance = OvenCavityOperationalState::GetOperationalStateInstance();
OnOvenOperationalStateChange(device, operation, param);
}
else
{
ChipLogDetail(NotSpecified, "Invalid device type : %s", device.c_str());
return;
}
}

void AllClustersAppCommandHandler::OnGenericOperationalStateChange(std::string device, std::string operation, Json::Value param)
{
OperationalState::Instance * operationalStateInstance = OperationalState::GetOperationalStateInstance();
OperationalState::OperationalStateDelegate * operationalStateDelegate = OperationalState::GetOperationalStateDelegate();
OperationalState::GenericOperationalError noError(to_underlying(OperationalState::ErrorStateEnum::kNoError));
OperationalState::OperationalStateEnum state =
static_cast<OperationalState::OperationalStateEnum>(operationalStateInstance->GetCurrentOperationalState());
if (operation == "Start")
{
operationalStateDelegate->HandleStartStateCallback(noError);
}
else if (operation == "Resume")
{
operationalStateDelegate->HandleResumeStateCallback(noError);
}
else if (operation == "Pause")
{
operationalStateDelegate->HandlePauseStateCallback(noError);
}
else if (operation == "Stop" && state == OperationalState::OperationalStateEnum::kRunning)
{
operationalStateDelegate->HandleStopStateCallback(noError);
}
else if (operation == "OnFault")
{
uint8_t event_id = to_underlying(OperationalState::ErrorStateEnum::kUnableToCompleteOperation);
if (!param.isNull())
{
event_id = to_underlying(static_cast<OperationalState::ErrorStateEnum>(param.asUInt()));
}

OperationalState::GenericOperationalError err(event_id);
operationalStateInstance->OnOperationalErrorDetected(err);
}
else
{
ChipLogDetail(NotSpecified, "Invalid operation : %s", operation.c_str());
return;
}
}

void AllClustersAppCommandHandler::OnOvenOperationalStateChange(std::string device, std::string operation, Json::Value param)
{
OperationalState::Instance * operationalStateInstance = OvenCavityOperationalState::GetOperationalStateInstance();
if (operation == "Start" || operation == "Resume")
{
operationalStateInstance->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kRunning));
Expand Down
10 changes: 10 additions & 0 deletions examples/all-clusters-app/linux/AllClustersCommandDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ class AllClustersAppCommandHandler
* Should be called when it is necessary to change the operational state as a manual operation.
*/
void OnOperationalStateChange(std::string device, std::string operation, Json::Value param);

/**
* Should be called when it is necessary to change the operational state as a manual operation.
*/
void OnGenericOperationalStateChange(std::string device, std::string operation, Json::Value param);

/**
* Should be called when it is necessary to change the operational state as a manual operation.
*/
void OnOvenOperationalStateChange(std::string device, std::string operation, Json::Value param);
};

class AllClustersCommandDelegate : public NamedPipeCommandDelegate
Expand Down
62 changes: 62 additions & 0 deletions src/python_testing/TC_OPSTATE_2_6.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#
# Copyright (c) 2024 Project CHIP Authors
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments
# for details about the block below.
#
# === BEGIN CI TEST ARGUMENTS ===
# test-runner-runs: run1
# test-runner-run/run1/app: ${ALL_CLUSTERS_APP}
# test-runner-run/run1/factoryreset: True
# test-runner-run/run1/quiet: True
# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json
# 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
# === END CI TEST ARGUMENTS ===


import chip.clusters as Clusters
from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main
from TC_OpstateCommon import TC_OPSTATE_BASE, TestInfo


class TC_OPSTATE_2_6(MatterBaseTest, TC_OPSTATE_BASE):
def __init__(self, *args):
super().__init__(*args)

test_info = TestInfo(
pics_code="OPSTATE",
cluster=Clusters.OperationalState
)

super().setup_base(test_info=test_info)

def steps_TC_OPSTATE_2_6(self) -> list[TestStep]:
return self.steps_TC_OPSTATE_BASE_2_6()

def pics_TC_OPSTATE_2_6(self) -> list[str]:
return ["OPSTATE.S", "OPSTATE.S.A0002"]

@async_test_body
async def test_TC_OPSTATE_2_6(self):
# endpoint = self.matter_test_config.endpoint

# await self.TEST_TC_OPSTATE_BASE_2_6(endpoint=endpoint)
await self.TEST_TC_OPSTATE_BASE_2_6(endpoint=1)


if __name__ == "__main__":
default_matter_test_main()
112 changes: 111 additions & 1 deletion src/python_testing/TC_OpstateCommon.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from chip.clusters.Attribute import EventReadResult, SubscriptionTransaction
from chip.clusters.Types import NullValue
from chip.interaction_model import InteractionModelError, Status
from matter_testing_support import EventChangeCallback, TestStep
from matter_testing_support import ClusterAttributeChangeAccumulator, EventChangeCallback, TestStep
from mobly import asserts


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

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

# STEP 8: TH sends Stop command to the DUT
Expand Down Expand Up @@ -1221,3 +1222,112 @@ async def TEST_TC_OPSTATE_BASE_2_5(self, endpoint=1):
self.skip_step(20)
self.skip_step(21)
self.skip_step(22)

############################
# TEST CASE 2.6 - Optional Reports with DUT as Server
############################
def steps_TC_OPSTATE_BASE_2_6(self) -> list[TestStep]:
steps = [TestStep(1, "Commissioning, already done", is_commissioning=True),
TestStep(2, "Subscribe to CountdownTime attribute"),
TestStep(3, "Manually put the DUT into a state where it will use the CountdownTime attribute, "
"the initial value of the CountdownTime is greater than 30, "
"and it will begin counting down the CountdownTime attribute."),
TestStep(4, "Over a period of 30 seconds, TH counts all report transactions with an attribute "
"report for the CountdownTime attribute in numberOfReportsReceived"),
TestStep(5, "Until the current operation finishes, TH counts all report transactions with "
"an attribute report for the CountdownTime attribute in numberOfReportsReceived and saves up to 5 such reports."),
TestStep(6, "Manually put the DUT into a state where it will use the CountdownTime attribute, "
"the initial value of the CountdownTime is greater than 30, and it will begin counting down the CountdownTime attribute."),
TestStep(7, "TH reads from the DUT the OperationalState attribute"),
TestStep(8, "Manually put the device in the Paused(0x02) operational state")
]
return steps

async def TEST_TC_OPSTATE_BASE_2_6(self, endpoint=1):
cluster = self.test_info.cluster
attributes = cluster.Attributes

self.init_test()

# commission
self.step(1)

# Note that this does a subscribe-all instead of subscribing only to the CountdownTime attribute.
# To-Do: Update the TP to subscribe-all.
self.step(2)
sub_handler = ClusterAttributeChangeAccumulator(cluster)
await sub_handler.start(self.default_controller, self.dut_node_id, endpoint)

self.step(3)
if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ST_RUNNING")):
self.send_manual_or_pipe_command(name="OperationalStateChange",
device=self.device,
operation="Start")
time.sleep(1)
await self.read_and_expect_value(endpoint=endpoint,
attribute=attributes.OperationalState,
expected_value=cluster.Enums.OperationalStateEnum.kRunning)
count = sub_handler.attribute_report_counts[attributes.CountdownTime]
asserts.assert_greater(count, 0, "Did not receive any reports for CountdownTime")
else:
self.skip_step(3)

sub_handler.reset()
self.step(4)
logging.info('Test will now collect data for 30 seconds')
time.sleep(30)

count = sub_handler.attribute_report_counts[attributes.CountdownTime]
sub_handler.reset()
asserts.assert_less_equal(count, 5, "Received more than 5 reports for CountdownTime")
asserts.assert_greater_equal(count, 0, "Did not receive any reports for CountdownTime")

attr_value = await self.read_expect_success(
endpoint=endpoint,
attribute=attributes.OperationalState)
if attr_value == cluster.Enums.OperationalStateEnum.kRunning:
self.step(5)
wait_count = 0
while (attr_value != cluster.Enums.OperationalStateEnum.kStopped) and (wait_count < 20):
time.sleep(1)
wait_count = wait_count + 1
attr_value = await self.read_expect_success(
endpoint=endpoint,
attribute=attributes.OperationalState)
count = sub_handler.attribute_report_counts[attributes.CountdownTime]
asserts.assert_less_equal(count, 5, "Received more than 5 reports for CountdownTime")
asserts.assert_greater(count, 0, "Did not receive any reports for CountdownTime")
else:
self.skip_step(5)

sub_handler.reset()
self.step(6)
if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ST_RUNNING")):
self.send_manual_or_pipe_command(name="OperationalStateChange",
device=self.device,
operation="Start")
time.sleep(1)
await self.read_and_expect_value(endpoint=endpoint,
attribute=attributes.OperationalState,
expected_value=cluster.Enums.OperationalStateEnum.kRunning)
count = sub_handler.attribute_report_counts[attributes.CountdownTime]
asserts.assert_greater(count, 0, "Did not receive any reports for CountdownTime")
else:
self.skip_step(6)

self.step(7)
await self.read_and_expect_value(endpoint=endpoint,
attribute=attributes.OperationalState,
expected_value=cluster.Enums.OperationalStateEnum.kRunning)

sub_handler.reset()
self.step(8)
if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ST_PAUSED")):
self.send_manual_or_pipe_command(name="OperationalStateChange",
device=self.device,
operation="Pause")
time.sleep(1)
count = sub_handler.attribute_report_counts[attributes.CountdownTime]
asserts.assert_greater(count, 0, "Did not receive any reports for CountdownTime")
else:
self.skip_step(8)

0 comments on commit 12d0123

Please sign in to comment.