From a43ce0ebfc47c135918f37d9f9a59d747e6c2371 Mon Sep 17 00:00:00 2001 From: C Freeman Date: Fri, 13 Dec 2024 14:21:20 -0500 Subject: [PATCH] TC-VALCC-3.1: Allow immediate open of valve (#35851) * TC-VALCC-3.1: Allow immediate open of valve If the valve can be opened before the command is complete or before the next read is done, the test will fail because it expects the transitioning phase to happen. In the spec: When the movement is complete, the device SHALL set the CurrentState attribute to the Open value. ^ this can happen before the end of the command. * fixup for 3.1 test * another fixup for 3.1 * Add close at start of test * linter * Restyled by clang-format * Restyled by isort * Add back the feature check * TC-VALCC-3.3: Allow immediate open * Linter * Fix 3.2 * couple little fixes * Restyled by isort * linter --------- Co-authored-by: Restyled.io --- .../linux/ValveControlDelegate.cpp | 3 - ...valve-configuration-and-control-server.cpp | 22 ++- src/python_testing/TC_VALCC_3_1.py | 97 ++++++------ src/python_testing/TC_VALCC_3_2.py | 145 ++++++++--------- src/python_testing/TC_VALCC_3_3.py | 148 +++++++++--------- 5 files changed, 214 insertions(+), 201 deletions(-) diff --git a/examples/all-clusters-app/linux/ValveControlDelegate.cpp b/examples/all-clusters-app/linux/ValveControlDelegate.cpp index f161ee65eae504..ed006eaa14baec 100644 --- a/examples/all-clusters-app/linux/ValveControlDelegate.cpp +++ b/examples/all-clusters-app/linux/ValveControlDelegate.cpp @@ -38,7 +38,6 @@ DataModel::Nullable ValveControlDelegate::HandleOpenValve(DataMod // In this demo application, the transition is considered instant, // so current level is set to the requested level and current state is set to kOpen. currentLevel = sLevel; - Attributes::CurrentState::Set(kValveEndpoint, ValveConfigurationAndControl::ValveStateEnum::kOpen); return DataModel::Nullable(currentLevel); } @@ -48,8 +47,6 @@ CHIP_ERROR ValveControlDelegate::HandleCloseValve() sLastOpenDuration = 0; sLevel = 0; ReturnErrorOnFailure(ValveConfigurationAndControl::UpdateCurrentLevel(kValveEndpoint, sLevel)); - ReturnErrorOnFailure( - ValveConfigurationAndControl::UpdateCurrentState(kValveEndpoint, ValveConfigurationAndControl::ValveStateEnum::kClosed)); ChipLogProgress(NotSpecified, "Valve closed"); return CHIP_NO_ERROR; } diff --git a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp index f6fa4e8fee6332..fd89ab135d384c 100644 --- a/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp +++ b/src/app/clusters/valve-configuration-and-control-server/valve-configuration-and-control-server.cpp @@ -360,9 +360,9 @@ CHIP_ERROR SetValveLevel(EndpointId ep, DataModel::Nullable level, Data if (!isDelegateNull(delegate)) { DataModel::Nullable cLevel = delegate->HandleOpenValve(level); - if (HasFeature(ep, ValveConfigurationAndControl::Feature::kLevel)) + if (HasFeature(ep, ValveConfigurationAndControl::Feature::kLevel) && !cLevel.IsNull()) { - VerifyOrReturnError(Status::Success == CurrentLevel::Set(ep, cLevel), attribute_error); + UpdateCurrentLevel(ep, cLevel.Value()); } } // start countdown @@ -376,14 +376,28 @@ CHIP_ERROR UpdateCurrentLevel(EndpointId ep, Percent currentLevel) if (HasFeature(ep, ValveConfigurationAndControl::Feature::kLevel)) { VerifyOrReturnError(Status::Success == CurrentLevel::Set(ep, currentLevel), CHIP_IM_GLOBAL_STATUS(ConstraintError)); - return CHIP_NO_ERROR; } - return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); + DataModel::Nullable targetLevel = DataModel::NullNullable; + TargetLevel::Get(ep, targetLevel); + if (!targetLevel.IsNull() && currentLevel == targetLevel.Value()) + { + targetLevel = DataModel::NullNullable; + TargetLevel::Set(ep, targetLevel); + UpdateCurrentState(ep, currentLevel == 0 ? ValveStateEnum::kClosed : ValveStateEnum::kOpen); + } + return CHIP_NO_ERROR; } CHIP_ERROR UpdateCurrentState(EndpointId ep, ValveConfigurationAndControl::ValveStateEnum currentState) { VerifyOrReturnError(Status::Success == CurrentState::Set(ep, currentState), CHIP_IM_GLOBAL_STATUS(ConstraintError)); + DataModel::Nullable targetState = DataModel::NullNullable; + TargetState::Get(ep, targetState); + if (currentState == targetState.ValueOr(ValveStateEnum::kUnknownEnumValue)) + { + targetState = DataModel::NullNullable; + TargetState::Set(ep, targetState); + } emitValveStateChangedEvent(ep, currentState); return CHIP_NO_ERROR; } diff --git a/src/python_testing/TC_VALCC_3_1.py b/src/python_testing/TC_VALCC_3_1.py index 5f3c58d0b96cf9..295c312c37f0cb 100644 --- a/src/python_testing/TC_VALCC_3_1.py +++ b/src/python_testing/TC_VALCC_3_1.py @@ -32,12 +32,10 @@ # quiet: true # === END CI TEST ARGUMENTS === -import time - import chip.clusters as Clusters from chip.clusters.Types import NullValue -from chip.interaction_model import InteractionModelError, Status -from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from chip.testing.matter_testing import (AttributeValue, ClusterAttributeChangeAccumulator, MatterBaseTest, TestStep, + async_test_body, default_matter_test_main) from mobly import asserts @@ -52,12 +50,16 @@ def desc_TC_VALCC_3_1(self) -> str: def steps_TC_VALCC_3_1(self) -> list[TestStep]: steps = [ TestStep(1, "Commissioning, already done", is_commissioning=True), - TestStep(2, "Send Open command"), - TestStep(3, "Read TargetState attribute"), - TestStep(4, "Read CurrentState attribute"), - TestStep(5, "Send Close command"), - TestStep(6, "Read TargetState attribute"), - TestStep(7, "Read CurrentState attribute"), + TestStep(2, "Set up a subscription to all attributes on the DUT"), + TestStep(3, "Send a close command to the DUT and wait until the CurrentState is closed", "DUT returns SUCCESS"), + TestStep(4, "Send Open command", "DUT returns SUCCESS"), + TestStep(5, "Wait until TH receives and data report for TargetState set to NULL and an attribute report for CurrentState set to Open (ordering does not matter)", + "Expected attribute reports are received"), + TestStep(6, "Read CurrentState and TargetState attribute", "CurrentState is Open, TargetState is NULL"), + TestStep(7, "Send Close command", "DUT returns SUCCESS"), + TestStep(8, "Wait until TH receives and data report for TargetState set to NULL and an attribute report for CurrentState set to Closed (ordering does not matter)", + "Expected attribute reports are received"), + TestStep(9, "Read CurrentState and TargetState attribute", "CurrentState is Closed, TargetState is NULL"), ] return steps @@ -72,62 +74,55 @@ async def test_TC_VALCC_3_1(self): endpoint = self.get_endpoint(default=1) - self.step(1) - attributes = Clusters.ValveConfigurationAndControl.Attributes + self.step(1) # commissioning - already done self.step(2) - try: - await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Open(), endpoint=endpoint) - except InteractionModelError as e: - asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") - pass + cluster = Clusters.ValveConfigurationAndControl + attributes = cluster.Attributes + attribute_subscription = ClusterAttributeChangeAccumulator(cluster) + await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) self.step(3) - target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) - - asserts.assert_true(target_state_dut is not NullValue, "TargetState is null") - asserts.assert_equal(target_state_dut, Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kOpen, - "TargetState is not the expected value") - - self.step(4) + # Wait for the entire duration of the test because this valve may be slow. The test will time out before this does. That's fine. + timeout = self.matter_test_config.timeout if self.matter_test_config.timeout is not None else self.default_timeout + await self.send_single_cmd(cmd=cluster.Commands.Close(), endpoint=endpoint) current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) - asserts.assert_true(current_state_dut is not NullValue, "CurrentState is null") - - while current_state_dut is Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kTransitioning: - time.sleep(1) - - current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) - asserts.assert_true(current_state_dut is not NullValue, "CurrentState is null") + if current_state_dut != cluster.Enums.ValveStateEnum.kClosed: + current_state_closed = AttributeValue( + endpoint_id=endpoint, attribute=attributes.CurrentState, value=cluster.Enums.ValveStateEnum.kClosed) + attribute_subscription.await_all_final_values_reported( + expected_final_values=[current_state_closed], timeout_sec=timeout) - asserts.assert_equal(current_state_dut, Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kOpen, - "CurrentState is not the expected value") + self.step(4) + attribute_subscription.reset() + await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Open(), endpoint=endpoint) self.step(5) - try: - await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Close(), endpoint=endpoint) - except InteractionModelError as e: - asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") - pass + # Wait until the current state is open and the target state is Null. + expected_final_state = [AttributeValue(endpoint_id=endpoint, attribute=attributes.TargetState, value=NullValue), AttributeValue( + endpoint_id=endpoint, attribute=attributes.CurrentState, value=cluster.Enums.ValveStateEnum.kOpen)] + attribute_subscription.await_all_final_values_reported(expected_final_values=expected_final_state, timeout_sec=timeout) self.step(6) target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) - - asserts.assert_true(target_state_dut is not NullValue, "TargetState is null") - asserts.assert_equal(target_state_dut, Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kClosed, - "TargetState is not the expected value") - - self.step(7) current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) - asserts.assert_true(current_state_dut is not NullValue, "CurrentState is null") + asserts.assert_equal(current_state_dut, cluster.Enums.ValveStateEnum.kOpen, "CurrentState is not open") + asserts.assert_equal(target_state_dut, NullValue, "TargetState is not null") - while current_state_dut is Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kTransitioning: - time.sleep(1) + self.step(7) + attribute_subscription.reset() + await self.send_single_cmd(cmd=cluster.Commands.Close(), endpoint=endpoint) - current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) - asserts.assert_true(current_state_dut is not NullValue, "CurrentState is null") + self.step(8) + expected_final_state = [AttributeValue(endpoint_id=endpoint, attribute=attributes.TargetState, value=NullValue), AttributeValue( + endpoint_id=endpoint, attribute=attributes.CurrentState, value=cluster.Enums.ValveStateEnum.kClosed)] + attribute_subscription.await_all_final_values_reported(expected_final_values=expected_final_state, timeout_sec=timeout) - asserts.assert_equal(current_state_dut, Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kClosed, - "CurrentState is not the expected value") + self.step(9) + target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) + current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) + asserts.assert_equal(current_state_dut, cluster.Enums.ValveStateEnum.kClosed, "CurrentState is not closed") + asserts.assert_equal(target_state_dut, NullValue, "TargetState is not null") if __name__ == "__main__": diff --git a/src/python_testing/TC_VALCC_3_2.py b/src/python_testing/TC_VALCC_3_2.py index 44dc320c0a8f33..5e22cda0ef39f0 100644 --- a/src/python_testing/TC_VALCC_3_2.py +++ b/src/python_testing/TC_VALCC_3_2.py @@ -25,6 +25,7 @@ # --commissioning-method on-network # --discriminator 1234 # --passcode 20202021 +# --endpoint 1 # --trace-to json:${TRACE_TEST_JSON}.json # --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto # --endpoint 1 @@ -32,13 +33,11 @@ # quiet: true # === END CI TEST ARGUMENTS === -import logging -import time - import chip.clusters as Clusters from chip.clusters.Types import NullValue from chip.interaction_model import InteractionModelError, Status -from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from chip.testing.matter_testing import (AttributeValue, ClusterAttributeChangeAccumulator, MatterBaseTest, TestStep, + async_test_body, default_matter_test_main) from mobly import asserts @@ -53,13 +52,18 @@ def desc_TC_VALCC_3_2(self) -> str: def steps_TC_VALCC_3_2(self) -> list[TestStep]: steps = [ TestStep(1, "Commissioning, already done", is_commissioning=True), - TestStep(2, "Read FeatureMap attribute"), - TestStep(3, "Send Open command with TargetLevel set to 100"), - TestStep(4, "Read TargetLevel attribute"), - TestStep(5, "Read CurrentLevel attribute"), - TestStep(6, "Send Close command"), - TestStep(7, "Read TargetLevel attribute"), - TestStep(8, "Read CurrentLevel attribute"), + TestStep(2, "Set up a subscription to all attributes on the DUT"), + TestStep(3, "Send a close command to the DUT and wait until the CurrentState is closed", "DUT returns SUCCESS"), + TestStep(4, "TH sends command Open command with TargetLevel field set to 100 and the remaining fields not populated.", + "Verify DUT responds w/ status SUCCESS(0x00)."), + TestStep(5, "Wait until TH receives data reports for TargetState set to NULL, TargetLevel set to NULL, CurrentState set to Open and CurrentLevel set to 100 (ordering does not matter)", + "Expected attribute reports are received"), + TestStep(6, "Read CurrentState, CurrentLevel, TargetState and TargetLevel attributes", + "CurrentState is Open, CurrentLevel is 100, TargetState is NULL and TargetLevel is NULL"), + TestStep(7, "Send Close command", "DUT returns SUCCESS"), + TestStep(8, "Wait until TH receives and data report for TargetState set to NULL and an attribute report for CurrentState set to Closed (ordering does not matter)", + "Expected attribute reports are received"), + TestStep(9, "Read CurrentState and TargetState attribute", "CurrentState is Closed, TargetState is NULL"), ] return steps @@ -73,82 +77,83 @@ def pics_TC_VALCC_3_2(self) -> list[str]: async def test_TC_VALCC_3_2(self): endpoint = self.get_endpoint(default=1) + asserts.assert_is_not_none( + endpoint, "Endpoint is required for this tests. The test endpoint is set using the --endpoint flag") self.step(1) attributes = Clusters.ValveConfigurationAndControl.Attributes - - self.step(2) + # TODO: replace with top-level check using run_if_endpoint_matches feature_map = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.FeatureMap) - is_lvl_feature_supported = feature_map & Clusters.ValveConfigurationAndControl.Bitmaps.Feature.kLevel + if not is_lvl_feature_supported: + asserts.skip('Endpoint does not match test requirements') + + self.step(2) + cluster = Clusters.ValveConfigurationAndControl + attributes = cluster.Attributes + attribute_subscription = ClusterAttributeChangeAccumulator(cluster) + await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) self.step(3) - if is_lvl_feature_supported: - try: - await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Open(targetLevel=100), endpoint=endpoint) - except InteractionModelError as e: - asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") - pass - else: - logging.info("Test step skipped") + # Wait for the entire duration of the test because this valve may be slow. The test will time out before this does. That's fine. + timeout = self.matter_test_config.timeout if self.matter_test_config.timeout is not None else self.default_timeout + await self.send_single_cmd(cmd=cluster.Commands.Close(), endpoint=endpoint) + current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) + if current_state_dut != cluster.Enums.ValveStateEnum.kClosed: + current_state_closed = AttributeValue( + endpoint_id=endpoint, attribute=attributes.CurrentState, value=cluster.Enums.ValveStateEnum.kClosed) + attribute_subscription.await_all_final_values_reported( + expected_final_values=[current_state_closed], timeout_sec=timeout) self.step(4) - if is_lvl_feature_supported: - target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) - - asserts.assert_true(target_level_dut is not NullValue, "TargetLevel is null") - asserts.assert_equal(target_level_dut, 100, "TargetLevel is not the expected value") - else: - logging.info("Test step skipped") + attribute_subscription.reset() + try: + await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Open(targetLevel=100), endpoint=endpoint) + except InteractionModelError as e: + asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") + pass self.step(5) - if is_lvl_feature_supported: - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - - while current_level_dut != 100: - time.sleep(1) - - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - - asserts.assert_equal(current_level_dut, 100, "CurrentLevel is not the expected value") - else: - logging.info("Test step skipped") + # Wait until the current state is open and the target state is Null. + expected_final_state = [AttributeValue(endpoint_id=endpoint, attribute=attributes.TargetState, value=NullValue), + AttributeValue(endpoint_id=endpoint, attribute=attributes.CurrentState, + value=cluster.Enums.ValveStateEnum.kOpen), + AttributeValue(endpoint_id=endpoint, attribute=attributes.TargetLevel, value=NullValue), + AttributeValue(endpoint_id=endpoint, attribute=attributes.CurrentLevel, value=100)] + attribute_subscription.await_all_final_values_reported(expected_final_values=expected_final_state, timeout_sec=timeout) self.step(6) - if is_lvl_feature_supported: - try: - await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Close(), endpoint=endpoint) - except InteractionModelError as e: - asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") - pass - else: - logging.info("Test step skipped") + target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) + current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) + target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) + current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) + asserts.assert_equal(current_state_dut, cluster.Enums.ValveStateEnum.kOpen, "CurrentState is not open") + asserts.assert_equal(target_state_dut, NullValue, "TargetState is not null") + asserts.assert_equal(current_level_dut, 100, "CurrentLevel is not 100") + asserts.assert_equal(target_level_dut, NullValue, "TargetLevel is not null") self.step(7) - if is_lvl_feature_supported: - target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) - - asserts.assert_true(target_level_dut is not NullValue, "TargetLevel is null") - asserts.assert_equal(target_level_dut, 0, "TargetLevel is not the expected value") - else: - logging.info("Test step skipped") + attribute_subscription.reset() + await self.send_single_cmd(cmd=cluster.Commands.Close(), endpoint=endpoint) self.step(8) - if is_lvl_feature_supported: - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - - while current_level_dut is Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kTransitioning: - time.sleep(1) - - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - - asserts.assert_equal(current_level_dut, 0, "CurrentLevel is not the expected value") - else: - logging.info("Test step skipped") + expected_final_state = [AttributeValue(endpoint_id=endpoint, attribute=attributes.TargetState, value=NullValue), + AttributeValue(endpoint_id=endpoint, attribute=attributes.CurrentState, + value=cluster.Enums.ValveStateEnum.kClosed), + AttributeValue(endpoint_id=endpoint, attribute=attributes.TargetLevel, value=NullValue), + AttributeValue(endpoint_id=endpoint, attribute=attributes.CurrentLevel, value=0)] + attribute_subscription.await_all_final_values_reported(expected_final_values=expected_final_state, timeout_sec=timeout) + attribute_subscription.await_all_final_values_reported(expected_final_values=expected_final_state, timeout_sec=timeout) + + self.step(9) + target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) + current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) + target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) + current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) + asserts.assert_equal(current_state_dut, cluster.Enums.ValveStateEnum.kClosed, "CurrentState is not closed") + asserts.assert_equal(target_state_dut, NullValue, "TargetState is not null") + asserts.assert_equal(current_level_dut, 0, "CurrentLevel is not 0") + asserts.assert_equal(target_level_dut, NullValue, "TargetLevel is not null") if __name__ == "__main__": diff --git a/src/python_testing/TC_VALCC_3_3.py b/src/python_testing/TC_VALCC_3_3.py index 97e29f9b1a82d1..2d962c22cb651a 100644 --- a/src/python_testing/TC_VALCC_3_3.py +++ b/src/python_testing/TC_VALCC_3_3.py @@ -32,13 +32,10 @@ # quiet: true # === END CI TEST ARGUMENTS === -import logging -import time - import chip.clusters as Clusters from chip.clusters.Types import NullValue -from chip.interaction_model import InteractionModelError, Status -from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from chip.testing.matter_testing import (AttributeValue, ClusterAttributeChangeAccumulator, MatterBaseTest, TestStep, + async_test_body, default_matter_test_main) from mobly import asserts @@ -53,14 +50,22 @@ def desc_TC_VALCC_3_3(self) -> str: def steps_TC_VALCC_3_3(self) -> list[TestStep]: steps = [ TestStep(1, "Commissioning, already done", is_commissioning=True), - TestStep(2, "Read AttributeList attribute"), - TestStep(3, "Read DefaultOpenLevel attribute, if supported"), - TestStep(4, "Send Open command"), - TestStep(5, "Read TargetLevel attribute"), - TestStep(6, "Read CurrentLevel attribute"), - TestStep(7, "Send Close command"), - TestStep(8, "Read TargetLevel attribute"), - TestStep(9, "Read CurrentLevel attribute"), + TestStep(2, "Read AttributeList attribute", "Verify that the DUT response contains the AttributeList attribute."), + TestStep(3, "If the DefaultOpenLevel is not supported, skip all remaining steps in this test"), + TestStep(4, "TH reads from the DUT the DefaultOpenLevel attribute. Store the value as defaultOpenLevel."), + TestStep(5, "Set up a subscription to all attributes on the DUT", "Subscription is successful"), + TestStep(6, "Send a close command to the DUT and wait until the CurrentState is reported as closed", "DUT returns SUCCESS"), + # TODO: this test should probably SET the default open attribute as well and un-set it at the end, so we're not testing against the default. + TestStep(7, "Send Open command with no fields populated", "DUT returns SUCCESS"), + TestStep(8, "Wait until TH receives the following data reports (ordering not checked): TargetState set to NULL, TargetLevel set to NULL, CurrentState set to Open, CurrentLevel set to defaultOpenLevel", + "Expected attribute reports are received"), + TestStep(9, "Read CurrentState and TargetState attribute", "CurrentState is Open, TargetState is NULL"), + TestStep(10, "Read CurrentLevel and TargetLevel attribute", "CurrentLevel is defaultOpenLevel, TargetLevel is NULL"), + TestStep(11, "Send Close command", "DUT returns SUCCESS"), + TestStep(12, "Wait until TH receives the following data reports (ordering not checked): TargetState set to NULL, TargetLevel set to NULL, CurrentState set to Closed, CurrentLevel set to 0", + "Expected attribute reports are received"), + TestStep(13, "Read CurrentState and TargetState attribute", "CurrentState is Closed, TargetState is NULL"), + TestStep(14, "Read CurrentLevel and TargetLevel attribute", "CurrentLevel is 0, TargetLevel is NULL"), ] return steps @@ -82,78 +87,75 @@ async def test_TC_VALCC_3_3(self): attribute_list = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) self.step(3) - if attributes.DefaultOpenLevel.attribute_id in attribute_list: - defaultOpenLevel = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.DefaultOpenLevel) - else: - logging.info("Test step skipped") + if attributes.DefaultOpenLevel.attribute_id not in attribute_list: + asserts.skip('Endpoint does not match test requirements') self.step(4) - if attributes.DefaultOpenLevel.attribute_id in attribute_list: - try: - await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Open(), endpoint=endpoint) - except InteractionModelError as e: - asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") - pass - else: - logging.info("Test step skipped") + default_open_level = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.DefaultOpenLevel) self.step(5) - if attributes.DefaultOpenLevel.attribute_id in attribute_list: - target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) - - asserts.assert_true(target_level_dut is not NullValue, "TargetLevel is null") - asserts.assert_equal(target_level_dut, defaultOpenLevel, "TargetLevel is not the expected value") - else: - logging.info("Test step skipped") + cluster = Clusters.ValveConfigurationAndControl + attributes = cluster.Attributes + attribute_subscription = ClusterAttributeChangeAccumulator(cluster) + await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) self.step(6) - if attributes.DefaultOpenLevel.attribute_id in attribute_list: - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - - while current_level_dut != defaultOpenLevel: - time.sleep(1) - - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - - asserts.assert_equal(current_level_dut, defaultOpenLevel, "CurrentLevel is not the expected value") - else: - logging.info("Test step skipped") + timeout = self.matter_test_config.timeout if self.matter_test_config.timeout is not None else self.default_timeout + await self.send_single_cmd(cmd=cluster.Commands.Close(), endpoint=endpoint) + current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) + if current_state_dut != cluster.Enums.ValveStateEnum.kClosed: + current_state_closed = AttributeValue( + endpoint_id=endpoint, attribute=attributes.CurrentState, value=cluster.Enums.ValveStateEnum.kClosed) + attribute_subscription.await_all_final_values_reported( + expected_final_values=[current_state_closed], timeout_sec=timeout) self.step(7) - if attributes.DefaultOpenLevel.attribute_id in attribute_list: - try: - await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Close(), endpoint=endpoint) - except InteractionModelError as e: - asserts.assert_equal(e.status, Status.Success, "Unexpected error returned") - pass - else: - logging.info("Test step skipped") + attribute_subscription.reset() + await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Open(), endpoint=endpoint) self.step(8) - if attributes.DefaultOpenLevel.attribute_id in attribute_list: - target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) - - asserts.assert_true(target_level_dut is not NullValue, "TargetLevel is null") - asserts.assert_equal(target_level_dut, 0, "TargetLevel is not the expected value") - else: - logging.info("Test step skipped") + expected_final_state = [AttributeValue(endpoint_id=endpoint, attribute=attributes.TargetState, value=NullValue), + AttributeValue(endpoint_id=endpoint, attribute=attributes.CurrentState, + value=cluster.Enums.ValveStateEnum.kOpen), + AttributeValue(endpoint_id=endpoint, attribute=attributes.TargetLevel, value=NullValue), + AttributeValue(endpoint_id=endpoint, attribute=attributes.CurrentLevel, value=default_open_level)] + attribute_subscription.await_all_final_values_reported(expected_final_values=expected_final_state, timeout_sec=timeout) self.step(9) - if attributes.DefaultOpenLevel.attribute_id in attribute_list: - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - - while current_level_dut is Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kTransitioning: - time.sleep(1) - - current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) - asserts.assert_true(current_level_dut is not NullValue, "CurrentLevel is null") - - asserts.assert_equal(current_level_dut, 0, "CurrentLevel is not the expected value") - else: - logging.info("Test step skipped") + target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) + current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) + asserts.assert_equal(current_state_dut, cluster.Enums.ValveStateEnum.kOpen, "CurrentState is not open") + asserts.assert_equal(target_state_dut, NullValue, "TargetState is not null") + + self.step(10) + target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) + current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) + asserts.assert_equal(current_level_dut, default_open_level, "CurrentLevel is not defaultOpenLevel") + asserts.assert_equal(target_level_dut, NullValue, "TargetLevel is not null") + + self.step(11) + attribute_subscription.reset() + await self.send_single_cmd(cmd=Clusters.Objects.ValveConfigurationAndControl.Commands.Close(), endpoint=endpoint) + + self.step(12) + expected_final_state = [AttributeValue(endpoint_id=endpoint, attribute=attributes.TargetState, value=NullValue), + AttributeValue(endpoint_id=endpoint, attribute=attributes.CurrentState, + value=cluster.Enums.ValveStateEnum.kClosed), + AttributeValue(endpoint_id=endpoint, attribute=attributes.TargetLevel, value=NullValue), + AttributeValue(endpoint_id=endpoint, attribute=attributes.CurrentLevel, value=0)] + attribute_subscription.await_all_final_values_reported(expected_final_values=expected_final_state, timeout_sec=timeout) + + self.step(13) + target_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) + current_state_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) + asserts.assert_equal(current_state_dut, cluster.Enums.ValveStateEnum.kClosed, "CurrentState is not open") + asserts.assert_equal(target_state_dut, NullValue, "TargetState is not null") + + self.step(14) + target_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) + current_level_dut = await self.read_valcc_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) + asserts.assert_equal(current_level_dut, 0, "CurrentLevel is not 0") + asserts.assert_equal(target_level_dut, NullValue, "TargetLevel is not null") if __name__ == "__main__":