From f33ae787c3f683b39a3c648790e1de83053e78ad Mon Sep 17 00:00:00 2001 From: Cecille Freeman Date: Fri, 26 Jul 2024 18:28:03 -0400 Subject: [PATCH] TC-SWTCH-2.5: Add Also adds a non-AS implementation in the button simulator. --- .../linux/AllClustersCommandDelegate.cpp | 50 +- .../linux/ButtonEventsSimulator.cpp | 90 +++- .../linux/ButtonEventsSimulator.h | 20 +- src/python_testing/TC_SWTCH.py | 434 +++++++++++++++++- 4 files changed, 545 insertions(+), 49 deletions(-) diff --git a/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp b/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp index 3c42eb1dd6a32e..45ab1dac61bebc 100644 --- a/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp +++ b/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp @@ -56,22 +56,23 @@ bool HasNumericField(Json::Value & jsonValue, const std::string & field) } /** - * Named pipe handler for simulated long press on an action switch. + * Named pipe handler for simulated long press * * Usage example: - * echo '{"Name": "SimulateActionSwitchLongPress", "EndpointId": 3, "ButtonId": 1, "LongPressDelayMillis": 800, + * echo '{"Name": "SimulateLongPress", "EndpointId": 3, "ButtonId": 1, "LongPressDelayMillis": 800, * "LongPressDurationMillis": 1000}' > /tmp/chip_all_clusters_fifo_1146610 * * JSON Arguments: - * - "Name": Must be "SimulateActionSwitchLongPress" + * - "Name": Must be "SimulateLongPress" * - "EndpointId": number of endpoint having a switch cluster * - "ButtonId": switch position in the switch cluster for "down" button (not idle) * - "LongPressDelayMillis": Time in milliseconds before the LongPress * - "LongPressDurationMillis": Total duration in milliseconds from start of the press to LongRelease + * - "FeatureMap": The feature map to simulate * * @param jsonValue - JSON payload from named pipe */ -void HandleSimulateActionSwitchLongPress(Json::Value & jsonValue) +void HandleSimulateLongPress(Json::Value & jsonValue) { if (sButtonSimulatorInstance != nullptr) { @@ -83,13 +84,14 @@ void HandleSimulateActionSwitchLongPress(Json::Value & jsonValue) bool hasButtonId = HasNumericField(jsonValue, "ButtonId"); bool hasLongPressDelayMillis = HasNumericField(jsonValue, "LongPressDelayMillis"); bool hasLongPressDurationMillis = HasNumericField(jsonValue, "LongPressDurationMillis"); - if (!hasEndpointId || !hasButtonId || !hasLongPressDelayMillis || !hasLongPressDurationMillis) + bool hasFeatureMap = HasNumericField(jsonValue, "FeatureMap"); + if (!hasEndpointId || !hasButtonId || !hasLongPressDelayMillis || !hasLongPressDurationMillis || !hasFeatureMap) { std::string inputJson = jsonValue.toStyledString(); - ChipLogError( - NotSpecified, - "Missing or invalid value for one of EndpointId, ButtonId, LongPressDelayMillis or LongPressDurationMillis in %s", - inputJson.c_str()); + ChipLogError(NotSpecified, + "Missing or invalid value for one of EndpointId, ButtonId, LongPressDelayMillis, LongPressDurationMillis or " + "FeatureMap in %s", + inputJson.c_str()); return; } @@ -97,6 +99,7 @@ void HandleSimulateActionSwitchLongPress(Json::Value & jsonValue) uint8_t buttonId = static_cast(jsonValue["ButtonId"].asUInt()); System::Clock::Milliseconds32 longPressDelayMillis{ static_cast(jsonValue["LongPressDelayMillis"].asUInt()) }; System::Clock::Milliseconds32 longPressDurationMillis{ static_cast(jsonValue["LongPressDurationMillis"].asUInt()) }; + uint32_t featureMap = static_cast(jsonValue["FeatureMap"].asUInt()); auto buttonSimulator = std::make_unique(); bool success = buttonSimulator->SetMode(ButtonEventsSimulator::Mode::kModeLongPress) @@ -105,6 +108,7 @@ void HandleSimulateActionSwitchLongPress(Json::Value & jsonValue) .SetIdleButtonId(0) .SetPressedButtonId(buttonId) .SetEndpointId(endpointId) + .SetFeatureMap(featureMap) .Execute([]() { sButtonSimulatorInstance.reset(); }); if (!success) @@ -117,11 +121,11 @@ void HandleSimulateActionSwitchLongPress(Json::Value & jsonValue) } /** - * Named pipe handler for simulated multi-press on an action switch. + * Named pipe handler for simulated multi-press. * * Usage example: - * echo '{"Name": "SimulateActionSwitchMultiPress", "EndpointId": 3, "ButtonId": 1, "MultiPressPressedTimeMillis": 100, - * "MultiPressReleasedTimeMillis": 350, "MultiPressNumPresses": 2}' > /tmp/chip_all_clusters_fifo_1146610 + * echo '{"Name": "SimulateMultiPress", "EndpointId": 3, "ButtonId": 1, "MultiPressPressedTimeMillis": 100, + * "MultiPressReleasedTimeMillis": 350, "MultiPressNumPresses": 2, "FeatureMap": 58}' > /tmp/chip_all_clusters_fifo_1146610 * * JSON Arguments: * - "Name": Must be "SimulateActionSwitchMultiPress" @@ -130,10 +134,12 @@ void HandleSimulateActionSwitchLongPress(Json::Value & jsonValue) * - "MultiPressPressedTimeMillis": Pressed time in milliseconds for each press * - "MultiPressReleasedTimeMillis": Released time in milliseconds after each press * - "MultiPressNumPresses": Number of presses to simulate + * - "FeatureMap": The feature map to simulate + * - "MultiPressMax": max number of presses (from attribute). * * @param jsonValue - JSON payload from named pipe */ -void HandleSimulateActionSwitchMultiPress(Json::Value & jsonValue) +void HandleSimulateMultiPress(Json::Value & jsonValue) { if (sButtonSimulatorInstance != nullptr) { @@ -146,13 +152,15 @@ void HandleSimulateActionSwitchMultiPress(Json::Value & jsonValue) bool hasMultiPressPressedTimeMillis = HasNumericField(jsonValue, "MultiPressPressedTimeMillis"); bool hasMultiPressReleasedTimeMillis = HasNumericField(jsonValue, "MultiPressReleasedTimeMillis"); bool hasMultiPressNumPresses = HasNumericField(jsonValue, "MultiPressNumPresses"); + bool hasFeatureMap = HasNumericField(jsonValue, "FeatureMap"); + bool hasMultiPressMax = HasNumericField(jsonValue, "MultiPressMax"); if (!hasEndpointId || !hasButtonId || !hasMultiPressPressedTimeMillis || !hasMultiPressReleasedTimeMillis || - !hasMultiPressNumPresses) + !hasMultiPressNumPresses || !hasFeatureMap || !hasMultiPressMax) { std::string inputJson = jsonValue.toStyledString(); ChipLogError(NotSpecified, "Missing or invalid value for one of EndpointId, ButtonId, MultiPressPressedTimeMillis, " - "MultiPressReleasedTimeMillis or MultiPressNumPresses in %s", + "MultiPressReleasedTimeMillis, MultiPressNumPresses, FeatureMap or MultiPressMax in %s", inputJson.c_str()); return; } @@ -164,6 +172,8 @@ void HandleSimulateActionSwitchMultiPress(Json::Value & jsonValue) System::Clock::Milliseconds32 multiPressReleasedTimeMillis{ static_cast( jsonValue["MultiPressReleasedTimeMillis"].asUInt()) }; uint8_t multiPressNumPresses = static_cast(jsonValue["MultiPressNumPresses"].asUInt()); + uint32_t featureMap = static_cast(jsonValue["FeatureMap"].asUInt()); + uint8_t multiPressMax = static_cast(jsonValue["MultiPressMax"].asUInt()); auto buttonSimulator = std::make_unique(); bool success = buttonSimulator->SetMode(ButtonEventsSimulator::Mode::kModeMultiPress) @@ -173,6 +183,8 @@ void HandleSimulateActionSwitchMultiPress(Json::Value & jsonValue) .SetIdleButtonId(0) .SetPressedButtonId(buttonId) .SetEndpointId(endpointId) + .SetFeatureMap(featureMap) + .SetMultiPressMax(multiPressMax) .Execute([]() { sButtonSimulatorInstance.reset(); }); if (!success) @@ -333,13 +345,13 @@ void AllClustersAppCommandHandler::HandleCommand(intptr_t context) std::string operation = self->mJsonValue["Operation"].asString(); self->OnOperationalStateChange(device, operation, self->mJsonValue["Param"]); } - else if (name == "SimulateActionSwitchLongPress") + else if (name == "SimulateLongPress") { - HandleSimulateActionSwitchLongPress(self->mJsonValue); + HandleSimulateLongPress(self->mJsonValue); } - else if (name == "SimulateActionSwitchMultiPress") + else if (name == "SimulateMultiPress") { - HandleSimulateActionSwitchMultiPress(self->mJsonValue); + HandleSimulateMultiPress(self->mJsonValue); } else { diff --git a/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp b/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp index 44bf5657f5c2f6..53a08672fdbc07 100644 --- a/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp +++ b/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp @@ -114,6 +114,42 @@ void EmitMultiPressComplete(EndpointId endpointId, uint8_t previousPosition, uin } } +void EmitShortRelease(EndpointId endpointId, uint8_t previousPosition) +{ + Clusters::Switch::Events::ShortRelease::Type event{}; + event.previousPosition = previousPosition; + EventNumber eventNumber = 0; + + CHIP_ERROR err = LogEvent(event, endpointId, eventNumber); + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to log ShortRelease event: %" CHIP_ERROR_FORMAT, err.Format()); + } + else + { + ChipLogProgress(NotSpecified, "Logged ShortRelease on Endpoint %u", static_cast(endpointId)); + } +} + +void EmitMultiPressOngoing(EndpointId endpointId, uint8_t newPosition, uint8_t count) +{ + Clusters::Switch::Events::MultiPressOngoing::Type event{}; + event.newPosition = newPosition; + event.currentNumberOfPressesCounted = count; + EventNumber eventNumber = 0; + + CHIP_ERROR err = LogEvent(event, endpointId, eventNumber); + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to log MultiPressOngoing event: %" CHIP_ERROR_FORMAT, err.Format()); + } + else + { + ChipLogProgress(NotSpecified, "Logged MultiPressOngoing on Endpoint %u position %u, count %u", + static_cast(endpointId), newPosition, count); + } +} + } // namespace void ButtonEventsSimulator::OnTimerDone(System::Layer * layer, void * appState) @@ -186,19 +222,65 @@ void ButtonEventsSimulator::Next() } case ButtonEventsSimulator::State::kEmitLongRelease: { SetButtonPosition(mEndpointId, mIdleButtonId); - EmitLongRelease(mEndpointId, mPressedButtonId); + if (mFeatureMap & static_cast(Clusters::Switch::Feature::kMomentarySwitchLongPress)) + { + EmitLongRelease(mEndpointId, mPressedButtonId); + } + else if (mFeatureMap & static_cast(Clusters::Switch::Feature::kMomentarySwitchRelease)) + { + EmitShortRelease(mEndpointId, mPressedButtonId); + } SetState(ButtonEventsSimulator::State::kIdle); mDoneCallback(); break; } case ButtonEventsSimulator::State::kEmitStartOfMultiPress: { EmitInitialPress(mEndpointId, mPressedButtonId); - StartTimer(mMultiPressNumPresses * (mMultiPressPressedTimeMillis + mMultiPressReleasedTimeMillis)); - SetState(ButtonEventsSimulator::State::kEmitEndOfMultiPress); + if (mFeatureMap & static_cast(Clusters::Switch::Feature::kActionSwitch)) + { + StartTimer(mMultiPressNumPresses * (mMultiPressPressedTimeMillis + mMultiPressReleasedTimeMillis)); + SetState(ButtonEventsSimulator::State::kEmitEndOfMultiPress); + } + else + { + SetState(ButtonEventsSimulator::State::kMultiPressButtonRelease); + StartTimer(mMultiPressPressedTimeMillis); + } break; } + case ButtonEventsSimulator::State::kMultiPressButtonRelease: { + ++mMultiPressPressesDone; + if (mMultiPressPressesDone > 1) + { + EmitMultiPressOngoing(mEndpointId, mPressedButtonId, mMultiPressPressesDone); + } + + if (mMultiPressPressesDone == mMultiPressNumPresses) + { + SetState(ButtonEventsSimulator::State::kEmitEndOfMultiPress); + } + else + { + SetState(ButtonEventsSimulator::State::kEmitStartOfMultiPress); + } + + if (mFeatureMap & static_cast(Clusters::Switch::Feature::kMomentarySwitchRelease)) + { + EmitShortRelease(mEndpointId, mPressedButtonId); + } + StartTimer(mMultiPressReleasedTimeMillis); + break; + } + case ButtonEventsSimulator::State::kEmitEndOfMultiPress: { - EmitMultiPressComplete(mEndpointId, mPressedButtonId, mMultiPressNumPresses); + if (mFeatureMap & static_cast(Clusters::Switch::Feature::kActionSwitch) && mMultiPressNumPresses > mMultiPressMax) + { + EmitMultiPressComplete(mEndpointId, mPressedButtonId, 0); + } + else + { + EmitMultiPressComplete(mEndpointId, mPressedButtonId, mMultiPressNumPresses); + } SetState(ButtonEventsSimulator::State::kIdle); mDoneCallback(); break; diff --git a/examples/all-clusters-app/linux/ButtonEventsSimulator.h b/examples/all-clusters-app/linux/ButtonEventsSimulator.h index 658da98f14fefd..539b2010099bd6 100644 --- a/examples/all-clusters-app/linux/ButtonEventsSimulator.h +++ b/examples/all-clusters-app/linux/ButtonEventsSimulator.h @@ -42,7 +42,8 @@ class ButtonEventsSimulator enum class Mode { kModeLongPress, - kModeMultiPress + kModeMultiPress, + kModeMultiPressNonAs }; using DoneCallback = std::function; @@ -107,6 +108,18 @@ class ButtonEventsSimulator return *this; } + ButtonEventsSimulator & SetFeatureMap(uint32_t featureMap) + { + mFeatureMap = featureMap; + return *this; + } + + ButtonEventsSimulator & SetMultiPressMax(uint8_t multiPressMax) + { + mMultiPressMax = multiPressMax; + return *this; + } + private: enum class State { @@ -118,6 +131,8 @@ class ButtonEventsSimulator kEmitStartOfMultiPress = 4, kEmitEndOfMultiPress = 5, + + kMultiPressButtonRelease = 6, }; static void OnTimerDone(System::Layer * layer, void * appState); @@ -131,9 +146,12 @@ class ButtonEventsSimulator System::Clock::Milliseconds32 mMultiPressPressedTimeMillis{}; System::Clock::Milliseconds32 mMultiPressReleasedTimeMillis{}; uint8_t mMultiPressNumPresses{ 1 }; + uint8_t mMultiPressPressesDone{ 0 }; uint8_t mIdleButtonId{ 0 }; uint8_t mPressedButtonId{ 1 }; EndpointId mEndpointId{ 1 }; + uint32_t mFeatureMap{ 0 }; + uint8_t mMultiPressMax{ 0 }; Mode mMode{ Mode::kModeLongPress }; State mState{ State::kIdle }; diff --git a/src/python_testing/TC_SWTCH.py b/src/python_testing/TC_SWTCH.py index 5fb968b724e098..1d05ee3a91d940 100644 --- a/src/python_testing/TC_SWTCH.py +++ b/src/python_testing/TC_SWTCH.py @@ -23,7 +23,7 @@ # 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: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --endpoint 3 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto --PICS src/app/tests/suites/certification/ci-pics-values +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto --PICS src/app/tests/suites/certification/ci-pics-values # === END CI TEST ARGUMENTS === import json @@ -37,9 +37,11 @@ import test_plan_support from chip.clusters import ClusterObjects as ClusterObjects from chip.clusters.Attribute import EventReadResult, TypedAttributePath +from chip.tlv import uint from matter_testing_support import (AttributeValue, ClusterAttributeChangeAccumulator, EventChangeCallback, MatterBaseTest, - TestStep, async_test_body, default_matter_test_main) + TestStep, default_matter_test_main, has_feature, per_endpoint_test) from mobly import asserts +import test_plan_support logger = logging.getLogger(__name__) @@ -52,10 +54,6 @@ def desc_TC_SWTCH_2_4(self) -> str: """Returns a description of this test""" return "[TC-SWTCH-2.4] Momentary Switch Long Press Verification" - def pics_TC_SWTCH_2_4(self): - """ This function returns a list of PICS for this test case that must be True for the test to be run""" - return ["SWTCH.S", "SWTCH.S.F01"] - # def steps_TC_SWTCH_2_4(self) -> list[TestStep]: # steps = [ # TestStep("0", "Commissioning, already done", is_commissioning=True), @@ -86,24 +84,62 @@ def _ask_for_switch_idle(self): if not self._use_button_simulator(): self.wait_for_user_input(prompt_msg="Ensure switch is idle") - def _ask_for_long_press(self, endpoint_id: int, pressed_position: int): + def _send_multi_press_named_pipe_command(self, endpoint_id: int, number_of_presses: int, pressed_position: int, feature_map: uint, multi_press_max: uint): + command_dict = {"Name": 'SimulateMultiPress', "EndpointId": endpoint_id, + "ButtonId": pressed_position, "MultiPressPressedTimeMillis": 500, "MultiPressReleasedTimeMillis": 500, + "MultiPressNumPresses": number_of_presses, "FeatureMap": feature_map, "MultiPressMax": multi_press_max} + self._send_named_pipe_command(command_dict) + + def _send_long_press_named_pipe_command(self, endpoint_id: int, pressed_position: int, feature_map: int): + command_dict = {"Name": "SimulateLongPress", "EndpointId": endpoint_id, + "ButtonId": pressed_position, "LongPressDelayMillis": 5000, "LongPressDurationMillis": 5500, "FeatureMap": feature_map} + self._send_named_pipe_command(command_dict) + + def _ask_for_multi_press_short_long(self, endpoint_id: int, pressed_position: int, feature_map: uint, multi_press_max: uint): + if not self._use_button_simulator(): + msg = f""" + Actuate the switch in the following sequence: + 1. Operate switch (press briefly) associated with position {pressed_position} on the DUT then release switch from DUT + 2. Operate switch (keep pressed for long time, e.g. 5 seconds) on the DUT immediately after the previous step + 3. Release switch from the DUT + """ + else: + # This is just a simulator, ignore the long press instruction for now, it doesn't matter for the CI. It does for cert. + self._send_multi_press_named_pipe_command(endpoint_id, 2, pressed_position, feature_map, multi_press_max) + + def _ask_for_multi_press_long_short(self, endpoint_id, pressed_position, feature_map: int): + if not self._use_button_simulator(): + msg = f""" + Actuate the switch in the following sequence: + 1. Operate switch (keep pressed for long time, e.g. 5 seconds) on the DUT + 2. Releases switch from the DUT + 3. Immediately after the previous step completes, operate switch (press briefly) associated with position {pressed_position} on the DUT then release switch from DUT + """ + self.wait_for_user_input(msg) + else: + # This is just the start of the sequence + # we'll need to send the short press after getting the LongRelease event because the simulator doesn't queue requests. + self._send_long_press_named_pipe_command(endpoint_id, pressed_position, feature_map) + + def _ask_for_multi_press(self, endpoint_id: int, number_of_presses: int, pressed_position: int, feature_map: uint, multi_press_max: uint): + if not self._use_button_simulator(): + self.wait_for_user_input(f'Operate the switch (press briefly) associated with position {pressed_position} then release {number_of_presses} times') + else: + self._send_multi_press_named_pipe_command(endpoint_id, number_of_presses, pressed_position, feature_map, multi_press_max) + + def _ask_for_long_press(self, endpoint_id: int, pressed_position: int, feature_map): if not self._use_button_simulator(): self.wait_for_user_input( prompt_msg=f"Press switch position {pressed_position} for a long time (around 5 seconds) on the DUT, then release it.") else: - command_dict = {"Name": "SimulateActionSwitchLongPress", "EndpointId": endpoint_id, - "ButtonId": pressed_position, "LongPressDelayMillis": 5000, "LongPressDurationMillis": 5500} - self._send_named_pipe_command(command_dict) + self._send_long_press_named_pipe_command(endpoint_id, pressed_position, feature_map) - def _ask_for_keep_pressed(self, endpoint_id: int, pressed_position: int): + def _ask_for_keep_pressed(self, endpoint_id: int, pressed_position: int, feature_map: int): if not self._use_button_simulator(): self.wait_for_user_input( prompt_msg=f"Press switch position {pressed_position} for a long time (around 5 seconds) on the DUT, then release it.") else: - # Using the long press here with a long duration so we can check the intermediate value. - command_dict = {"Name": "SimulateActionSwitchLongPress", "EndpointId": endpoint_id, - "ButtonId": pressed_position, "LongPressDelayMillis": 0, "LongPressDurationMillis": self.keep_pressed_delay} - self._send_named_pipe_command(command_dict) + self._send_long_press_named_pipe_command(endpoint_id, pressed_position, feature_map) def _ask_for_release(self): # Since we used a long press for this, "ask for release" on the button simulator just means waiting out the delay @@ -219,7 +255,7 @@ def _expect_no_events_for_cluster(self, event_queue: queue.Queue, endpoint_id: i logging.info(f"Successfully waited for no further events on {expected_cluster} for {elapsed:.1f} seconds") - @async_test_body + @per_endpoint_test(has_feature(Clusters.Switch, Clusters.Switch.Bitmaps.Feature.kMomentarySwitch)) async def test_TC_SWTCH_2_4(self): # TODO: Make this come from PIXIT switch_pressed_position = 1 @@ -263,7 +299,7 @@ async def test_TC_SWTCH_2_4(self): # Step 4a: Operator operates switch (keep pressed for long time, e.g. 5 seconds) on the DUT, the release it self._placeholder_for_step("4a") - self._ask_for_long_press(endpoint_id, switch_pressed_position) + self._ask_for_long_press(endpoint_id, switch_pressed_position, feature_map) # Step 4b: TH expects report of CurrentPosition 1, followed by a report of Current Position 0. self._placeholder_for_step("4b") @@ -323,9 +359,6 @@ def _received_event(self, event_listener: EventChangeCallback, target_event: Clu remaining = end_time - datetime.now() return False - def pics_TC_SWTCH_2_3(self): - return ['SWTCH.S.F01'] - def steps_TC_SWTCH_2_3(self): return [TestStep(1, test_plan_support.commission_if_required(), "", is_commissioning=True), TestStep(2, "Set up subscription to all events of Switch cluster on the endpoint"), @@ -335,13 +368,14 @@ def steps_TC_SWTCH_2_3(self): "Verify that the TH receives InitialPress event with NewPosition set to 1 on the DUT"), TestStep(6, "TH reads the CurrentPosition attribute from the DUT", "Verify that the value is 1"), TestStep(7, "Operator releases switch on the DUT"), - TestStep("8a", "If the DUT implements the MSR feature, verify that the TH receives ShortRelease event with NewPosition set to 0 on the DUT", "Event received"), + TestStep("8a", "If the DUT implements the MSR feature and does not implement the MSL feature, verify that the TH receives ShortRelease event with NewPosition set to 0 on the DUT", "Event received"), + TestStep("8b", "If the DUT implements the MSR feature and the MSL feature, verify that the TH receives LongRelease event with NewPosition set to 0 on the DUT", "Event received"), TestStep( - "8b", "If the DUT implements the AS feature, verify that the TH does not receive ShortRelease event on the DUT", "No event received"), + "8c", "If the DUT implements the AS feature, verify that the TH does not receive ShortRelease event on the DUT", "No event received"), TestStep(9, "TH reads the CurrentPosition attribute from the DUT", "Verify that the value is 0"), ] - @async_test_body + @per_endpoint_test(has_feature(Clusters.Switch, Clusters.Switch.Bitmaps.Feature.kMomentarySwitch)) async def test_TC_SWTCH_2_3(self): # Commissioning - already done self.step(1) @@ -349,6 +383,7 @@ async def test_TC_SWTCH_2_3(self): feature_map = await self.read_single_attribute_check_success(cluster, attribute=cluster.Attributes.FeatureMap) has_msr_feature = (feature_map & cluster.Bitmaps.Feature.kMomentarySwitchRelease) != 0 + has_msl_feature = (feature_map & cluster.Bitmaps.Feature.kMomentarySwitchLongPress) != 0 has_as_feature = (feature_map & cluster.Bitmaps.Feature.kActionSwitch) != 0 endpoint_id = self.matter_test_config.endpoint @@ -369,7 +404,7 @@ async def test_TC_SWTCH_2_3(self): # This is 1s larger than the subscription ceiling self.keep_pressed_delay = 6000 self.pressed_position = 1 - self._ask_for_keep_pressed(endpoint_id, self.pressed_position) + self._ask_for_keep_pressed(endpoint_id, self.pressed_position, feature_map) event_listener.wait_for_event_report(cluster.Events.InitialPress) self.step(6) @@ -380,13 +415,18 @@ async def test_TC_SWTCH_2_3(self): self._ask_for_release() self.step("8a") - if has_msr_feature: + if has_msr_feature and not has_msl_feature: asserts.assert_true(self._received_event(event_listener, cluster.Events.ShortRelease, 10), "Did not receive short release") else: self.mark_current_step_skipped() self.step("8b") + if has_msr_feature and has_msl_feature: + asserts.assert_true(self._received_event(event_listener, cluster.Events.LongRelease, 10), + "Did not receive long release") + + self.step("8c") if has_as_feature: asserts.assert_false(self._received_event(event_listener, cluster.Events.ShortRelease, 10), "Received short release") else: @@ -397,5 +437,349 @@ async def test_TC_SWTCH_2_3(self): asserts.assert_equal(button_val, 0, "Button value is not 0") + def steps_TC_SWTCH_2_5(self): + return [TestStep(1, test_plan_support.commission_if_required(), "", is_commissioning=True), + TestStep(2, "Set up a subscription to all Switch cluster events"), + TestStep(3, "Operate does not operate the switch on the DUT"), + TestStep("4a", "Operator operates switch (press briefly) associated with position 1 on the DUT then release switch from DUT", + """ + + * Verify that the TH receives InitialPress event with NewPosition set to 1 from the DUT + * Verify that the TH receives ShortRelease event with PreviousPosition set to 1 from the DUT + """), + TestStep("4b", "Operator does not operate switch on the DUT", + "TH receives MultiPressComplete event with PreviousPosition set to 1 and TotalNumberOfPressesCounted set to 1 from the DUT"), + TestStep("5a", "Operator repeat step 4a 2 times quickly", + """ + + * Verify that the TH receives InitialPress event with NewPosition set to 1 from the DUT + * Verify that the TH receives ShortRelease event with PreviousPosition set to 1 from the DUT + * Verify that the TH receives InitialPress event with NewPosition set to 1 from the DUT + * Verify that the TH receives MultiPressOngoing event with NewPosition set to 1 and CurrentNumberOfPressesCounted set to 2 from the DUT + * Verify that the TH receives ShortRelease event with PreviousPosition set to 1 from the DUT + + + The events sequence SHALL follow the same sequence as above + """), + TestStep("5b", "Operator does not operate switch on the DUT", + "Verify that the TH receives MultiPressComplete event with PreviousPosition set to 1 and TotalNumberOfPressesCounted set to 2 from the DUT"), + TestStep("6a", "If MultiPressMax == 2 (see 2c of TC-SWTCH-2.1), skip steps 6b .. 6c"), + TestStep("6b", "Operator repeat step 4a 3 times quickly", + """ + * Verify that the TH receives InitialPress event with NewPosition set to 1 from the DUT + * Verify that the TH receives ShortRelease event with PreviousPosition set to 1 from the DUT + * Verify that the TH receives InitialPress event with NewPosition set to 1 from the DUT + * Verify that the TH receives MultiPressOngoing event with NewPosition set to 1 and CurrentNumberOfPressesCounted set to 2 from the DUT + * Verify that the TH receives ShortRelease event with PreviousPosition set to 1 from the DUT + * Verify that the TH receives InitialPress event with NewPosition set to 1 from the DUT + * Verify that the TH receives MultiPressOngoing event with NewPosition set to 1 and CurrentNumberOfPressesCounted set to 3 from the DUT + * Verify that the TH receives ShortRelease event with PreviousPosition set to 1 from the DUT + + + The events sequence from the subscription SHALL follow the same sequence as expressed above, in the exact order of events specified. + """), + TestStep("6c", "Operator does not operate switch on the DUT for 5 seconds", + "Verify that the TH receives MultiPressComplete event with PreviousPosition set to 1 and TotalNumberOfPressesCounted set to 3 from the DUT"), + TestStep(7, "Set up subscription to all Switch cluster events"), + TestStep("8a", + """ + Operator operates switch in below sequence: + 1. Operator operates switch (press briefly) associated with position 1 on the DUT then release switch from DUT + 2. Operator operates switch (keep pressed for long time, e.g. 5 seconds) on the DUT immediately after the previous step + 3. Operator releases switch from the DUT + """, + """ + + * Verify that the TH receives InitialPress event with NewPosition set to 1 from the DUT + * Verify that the TH receives ShortRelease event with PreviousPosition set to 1 from the DUT + * Verify that the TH receives InitialPress event with NewPosition set to 1 from the DUT + + * Verify that the TH receives MultiPressOngoing event with NewPosition set to 1 and CurrentNumberOfPressesCounted set to 2 from the DUT + * Verify that the TH receives ShortRelease event with PreviousPosition set to 1 from the DUT + * Verify that the TH does not receive LongPress event from the DUT + * Verify that the TH does not receive LongRelease event from the DUT + + The events sequence from the subscription SHALL follow the same sequence as expressed above, in the exact order of events specified. + """), + TestStep("8b", "Operator does not operate switch on the DUT", "TH receives MultiPressComplete event with PreviousPosition set to 1 and TotalNumberOfPressesCounted set to 2 from the DUT"), + TestStep("9a", + """ + Operator operates switch in below sequence: + 1. Operator operates switch (keep pressed for long time, e.g. 5 seconds) on the DUT + 2. Operator releases switch from the DUT + 3. Immediately after the previous step completes, Operator operates switch (press briefly) associated with position 1 on the DUT then release switch from DUT + """, + """ + + * Verify that the TH receives InitialPress event with NewPosition set to 1 from the DUT + * Verify that the TH receives (one, not more than one) LongPress event with NewPosition set to 1 from the DUT + * Verify that the TH receives LongRelease event with PreviousPosition set to 1 from the DUT + * Verify that the TH receives InitialPress event with NewPosition set to 1 from the DUT + * Verify that the TH receives ShortRelease event with PreviousPosition set to 1 from the DUT + * Verify that the TH does not receive MultiPressOngoing event from the DUT + + The events sequence from the subscription SHALL follow the same sequence as expressed above, in the exact order of events specified. + """), + TestStep("9b", "Operator does not operate switch on the DUT", "TH receives MultiPressComplete event with PreviousPosition set to 1 and TotalNumberOfPressesCounted set to 2 from the DUT") + + ] + + + @staticmethod + def should_run_SWTCH_2_5(wildcard, endpoint): + msm = has_feature(Clusters.Switch, Clusters.Switch.Bitmaps.Feature.kMomentarySwitchMultiPress) + asf = has_feature(Clusters.Switch, 0x20) + return msm(wildcard, endpoint) and not asf(wildcard, endpoint) + + @per_endpoint_test(should_run_SWTCH_2_5) + async def test_TC_SWTCH_2_5(self): + # Commissioning - already done + self.step(1) + + cluster = Clusters.Switch + feature_map = await self.read_single_attribute_check_success(cluster, attribute=cluster.Attributes.FeatureMap) + has_msl_feature = (feature_map & cluster.Bitmaps.Feature.kMomentarySwitchLongPress) + multi_press_max = await self.read_single_attribute_check_success(cluster, attribute=cluster.Attributes.MultiPressMax) + + endpoint_id = self.matter_test_config.endpoint + pressed_position = 1 + + self.step(2) + event_listener = EventChangeCallback(cluster) + await event_listener.start(self.default_controller, self.dut_node_id, endpoint=endpoint_id) + + self.step(3) + self._ask_for_switch_idle() + + def test_multi_press_sequence(starting_step:str, count:int, short_long:bool = False): + step = starting_step + self.step(step) + + if short_long: + self._ask_for_multi_press_short_long(endpoint_id, pressed_position, feature_map=feature_map, multi_press_max=multi_press_max) + else: + self._ask_for_multi_press(endpoint_id, number_of_presses=count, pressed_position=pressed_position, feature_map=feature_map, multi_press_max=multi_press_max) + for i in range(count): + event = event_listener.wait_for_event_report(cluster.Events.InitialPress) + asserts.assert_equal(event.newPosition, pressed_position, "Unexpected NewPosition on InitialEvent") + if i > 0: + event = event_listener.wait_for_event_report(cluster.Events.MultiPressOngoing) + asserts.assert_equal(event.newPosition, pressed_position, "Unexpected NewPosition on MultiPressOngoing") + asserts.assert_equal(event.currentNumberOfPressesCounted, i+1, "Unexpected CurrentNumberOfPressesCounted on MultiPressOngoing") + event = event_listener.wait_for_event_report(cluster.Events.ShortRelease) + asserts.assert_equal(event.previousPosition, pressed_position, "Unexpected PreviousPosition on ShortRelease") + + step = step[:-1] + chr(ord(step[-1])+1) + self.step(step) + self._ask_for_switch_idle() + event = event_listener.wait_for_event_report(cluster.Events.MultiPressComplete) + asserts.assert_equal(event.previousPosition, pressed_position, "Unexpected PreviousPosition on MultiPressComplete") + asserts.assert_equal(event.totalNumberOfPressesCounted, count, "Unexpected count on MultiPressComplete") + + + test_multi_press_sequence("4a", 1) + + test_multi_press_sequence("5a", 2) + + self.step("6a") + multi_press_max = await self.read_single_attribute_check_success(cluster=cluster, attribute=cluster.Attributes.MultiPressMax) + if multi_press_max == 2: + self.skip_step("6b") + self.skip_step("6c") + else: + test_multi_press_sequence("6b", 3) + + if not has_msl_feature: + self.skip_all_remaining_steps(7) + return + + self.step(7) + # subscription is already set up + + test_multi_press_sequence("8a", 2, short_long=True) + + self.step("9a") + self._ask_for_multi_press_long_short(endpoint_id, pressed_position, feature_map) + + event = event_listener.wait_for_event_report(cluster.Events.InitialPress) + asserts.assert_equal(event.newPosition, pressed_position, "Unexpected NewPosition on InitialEvent") + event = event_listener.wait_for_event_report(cluster.Events.LongPress) + asserts.assert_equal(event.newPosition, pressed_position, "Unexpected NewPosition on LongPress") + event = event_listener.wait_for_event_report(cluster.Events.LongRelease) + asserts.assert_equal(event.previousPosition, pressed_position, "Unexpected PreviousPosition on LongRelease") + if self._use_button_simulator: + # simulator can't sequence so we need to help it along here + self._send_multi_press_named_pipe_command(endpoint_id, number_of_presses=1, pressed_position=1, feature_map=feature_map, multi_press_max=multi_press_max) + + event = event_listener.wait_for_event_report(cluster.Events.InitialPress) + asserts.assert_equal(event.newPosition, pressed_position, "Unexpected NewPosition on InitialEvent") + event = event_listener.wait_for_event_report(cluster.Events.ShortRelease) + asserts.assert_equal(event.previousPosition, pressed_position, "Unexpected PreviousPosition on ShortRelease") + + # Because this is a queue, we verify that no multipress ongoing is received by verifying that the next event is the multipress complete + + self.step("9b") + self._ask_for_switch_idle() + event = event_listener.wait_for_event_report(cluster.Events.MultiPressComplete) + asserts.assert_equal(event.previousPosition, pressed_position, "Unexpected PreviousPosition on MultiPressComplete") + asserts.assert_equal(event.totalNumberOfPressesCounted, 1, "Unexpected count on MultiPressComplete") + + + def steps_TC_SWTCH_2_6(self): + return [TestStep(1, test_plan_support.commission_if_required(), is_commissioning=True), + TestStep(2, "Set up subscription to all Switch cluster events"), + TestStep(3, "Operator does not operate switch on the DUT"), + TestStep("4a", "Operator operates switch (press briefly) associated with position 1 on the DUT then release switch from DUT", + """ + + * Verify that the TH receives InitialPress event with NewPosition set to 1 from the DUT + * Verify that the TH does not receive ShortRelease event from the DUT + """), + TestStep("4b", "Operator does not operate switch on the DUT", + "TH receives MultiPressComplete event with PreviousPosition set to 1 and TotalNumberOfPressesCounted set to 1 from the DUT"), + TestStep("5a", "Operator repeat step 4a 2 times quickly", + """ + + * Verify that the TH receives InitialPress(one, not more than one) event with NewPosition set to 1 from the DUT + * Verify that the TH does not receive ShortRelease event from the DUT + * Verify that the TH does not receive MultiPressOngoing event from the DUT + """), + TestStep("5b", "Operator does not operate switch on the DUT", + "Verify that the TH receives MultiPressComplete event with PreviousPosition set to 1 and TotalNumberOfPressesCounted set to 2 from the DUT"), + TestStep("6a", "Operator repeat step 4a MultiPressMax + 1(see 2c of TC-SWTCH-2.1) times quickly", + """ + + * Verify that the TH receives InitialPress(one, not more than one) event with NewPosition set to 1 from the DUT + * Verify that the TH does not receive ShortRelease event from the DUT + * Verify that the TH does not receive MultiPressOngoing event from the DUT + """ + ), + TestStep("6b", "Operator does not operate switch on the DUT", + "Verify that the TH receives MultiPressComplete event with PreviousPosition set to 1 and TotalNumberOfPressesCounted set to 0 from the DUT"), + TestStep("7a", "If the switch cluster does not implement the MomentarySwitchLongPress (MSL) feature, skip the remaining steps"), + TestStep("7b", "Set up subscription to all Switch cluster events"), + TestStep("8a", + """ + Operator operates switch in below sequence: + 1. Operator operates switch (press briefly) associated with position 1 on the DUT then release switch from DUT + 2. Operator operates switch (keep pressed for long time, e.g. 5 seconds) on the DUT immediately after the previous step + 3. Operator releases switch from the DUT + """, + """ + + * Verify that the TH receives InitialPress(one, not more than one) event with NewPosition set to 1 from the DUT + * Verify that the TH does not receive ShortRelease event from the DUT + * Verify that the TH does not receive MultiPressOngoing event from the DUT + * Verify that the TH does not receive LongPress event from the DUT + * Verify that the TH does not receive LongRelease event from the DUT + """), + TestStep("8b", "Operator does not operate switch on the DUT", + "TH receives MultiPressComplete event with PreviousPosition set to 1 and TotalNumberOfPressesCounted set to 2 from the DUT"), + TestStep("9a", + """ + Operator operates switch in below sequence: + + 1. Operator operates switch (keep pressed for long time, e.g. 5 seconds) on the DUT + 2. Operator releases switch from the DUT + 3. Immediately after the previous step complete, Operator operates switch (press briefly) associated with position 1 on the DUT then release switch from DUT + """, + """ + + * Verify that the TH receives InitialPress event with NewPosition set to 1 from the DUT + * Verify that the TH receives (one, not more than one) LongPress event with NewPosition set to 1 from the DUT + * Verify that the TH receives LongRelease event with PreviousPosition set to 1 from the DUT + * Verify that the TH receives InitialPress event with NewPosition set to 1 from the DUT + * Verify that the TH does not receive MultiPressOngoing event from the DUT + * Verify that the TH does not receive ShortRelease event from the DUT + + The events sequence from the subscription SHALL follow the same sequence as expressed above, in the exact order of events specified. + """), + TestStep("9b", "Operator does not operate switch on the DUT" + "Verify that the TH receives MultiPressComplete event with PreviousPosition set to 1 and TotalNumberOfPressesCounted set to 1 from the DUT"), + ] + @staticmethod + def should_run_SWTCH_2_6(wildcard, endpoint): + msm = has_feature(Clusters.Switch, Clusters.Switch.Bitmaps.Feature.kMomentarySwitchMultiPress) + asf = has_feature(Clusters.Switch, 0x20) + return msm(wildcard, endpoint) and asf(wildcard, endpoint) + + @per_endpoint_test(should_run_SWTCH_2_6) + async def test_TC_SWTCH_2_6(self): + # Commissioning - already done + self.step(1) + + cluster = Clusters.Switch + feature_map = await self.read_single_attribute_check_success(cluster, attribute=cluster.Attributes.FeatureMap) + has_msl_feature = (feature_map & cluster.Bitmaps.Feature.kMomentarySwitchLongPress) + multi_press_max = await self.read_single_attribute_check_success(cluster, attribute=cluster.Attributes.MultiPressMax) + + endpoint_id = self.matter_test_config.endpoint + pressed_position = 1 + + self.step(2) + event_listener = EventChangeCallback(cluster) + await event_listener.start(self.default_controller, self.dut_node_id, endpoint=endpoint_id) + + self.step(3) + self._ask_for_switch_idle() + + def test_multi_press_sequence(starting_step:str, count:int, short_long:bool = False): + step = starting_step + self.step(step) + + if short_long: + self._ask_for_multi_press_short_long(endpoint_id, pressed_position, feature_map=feature_map, multi_press_max=multi_press_max) + else: + self._ask_for_multi_press(endpoint_id, number_of_presses=count, pressed_position=pressed_position, feature_map=feature_map, multi_press_max=multi_press_max) + + event = event_listener.wait_for_event_report(cluster.Events.InitialPress) + asserts.assert_equal(event.newPosition, pressed_position, "Unexpected NewPosition on InitialEvent") + + step = step[:-1] + chr(ord(step[-1])+1) + self.step(step) + self._ask_for_switch_idle() + event = event_listener.wait_for_event_report(cluster.Events.MultiPressComplete) + asserts.assert_equal(event.previousPosition, pressed_position, "Unexpected PreviousPosition on MultiPressComplete") + expected_count = 0 if count > multi_press_max else count + asserts.assert_equal(event.totalNumberOfPressesCounted, expected_count, "Unexpected count on MultiPressComplete") + + test_multi_press_sequence("4a", 1) + + test_multi_press_sequence("5a", 2) + + test_multi_press_sequence("6a", multi_press_max + 1) + + self.step("7a") + if not has_msl_feature: + self.skip_all_remaining_steps("7b") + + # subscription is already established + self.step("7b") + + test_multi_press_sequence("8a", 2, short_long=True) + + self.step("9a") + self._ask_for_multi_press_long_short(endpoint_id, pressed_position, feature_map) + + event = event_listener.wait_for_event_report(cluster.Events.InitialPress) + asserts.assert_equal(event.newPosition, pressed_position, "Unexpected NewPosition on InitialEvent") + event = event_listener.wait_for_event_report(cluster.Events.LongPress) + asserts.assert_equal(event.newPosition, pressed_position, "Unexpected NewPosition on LongPress") + event = event_listener.wait_for_event_report(cluster.Events.LongRelease) + asserts.assert_equal(event.previousPosition, pressed_position, "Unexpected PreviousPosition on LongRelease") + if self._use_button_simulator: + # simulator can't sequence so we need to help it along here + self._send_multi_press_named_pipe_command(endpoint_id, number_of_presses=1, pressed_position=1, feature_map=feature_map, multi_press_max=multi_press_max) + + event = event_listener.wait_for_event_report(cluster.Events.InitialPress) + asserts.assert_equal(event.newPosition, pressed_position, "Unexpected NewPosition on InitialEvent") + + # Verify that we don't receive the multi-press ongoing or short release by verifying that the next event in the sequence is the multi-press complete + self.step("9b") + self._ask_for_switch_idle() + event = event_listener.wait_for_event_report(cluster.Events.MultiPressComplete) + asserts.assert_equal(event.previousPosition, pressed_position, "Unexpected PreviousPosition on MultiPressComplete") + asserts.assert_equal(event.totalNumberOfPressesCounted, 1, "Unexpected count on MultiPressComplete") + + + if __name__ == "__main__": default_matter_test_main()