From 2a70eea0aeec474e19b9aef8e5784f7456fa7c70 Mon Sep 17 00:00:00 2001 From: "tennessee.carmelveilleux@gmail.com" Date: Tue, 16 Jul 2024 16:55:34 -0400 Subject: [PATCH 01/10] Implement TC-SWTCH-2.4 and all-clusters button simulator - Implement TC-SWTCH-2.4 - Implement action switch button simulator to drive the test Testing done: - New test (TC-SWTCH-2.4) works using the button simulator --- .../linux/AllClustersCommandDelegate.cpp | 132 ++++++++ examples/all-clusters-app/linux/BUILD.gn | 3 + .../linux/ButtonEventsSimulator.cpp | 209 +++++++++++++ .../linux/ButtonEventsSimulator.h | 143 +++++++++ src/python_testing/TC_SWTCH.py | 282 ++++++++++++++++++ src/python_testing/matter_testing_support.py | 66 +++- 6 files changed, 825 insertions(+), 10 deletions(-) create mode 100644 examples/all-clusters-app/linux/ButtonEventsSimulator.cpp create mode 100644 examples/all-clusters-app/linux/ButtonEventsSimulator.h create mode 100644 src/python_testing/TC_SWTCH.py diff --git a/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp b/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp index 8effe6426cc49d..4687a39ce7e230 100644 --- a/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp +++ b/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp @@ -29,6 +29,7 @@ #include #include +#include "ButtonEventsSimulator.h" #include #include #include @@ -36,13 +37,136 @@ #include #include +#include #include +#include using namespace chip; using namespace chip::app; using namespace chip::app::Clusters; using namespace chip::DeviceLayer; +namespace +{ + +std::unique_ptr sButtonSimulatorInstance{nullptr}; + +bool HasNumericField(Json::Value & jsonValue, const std::string& field) +{ + return jsonValue.isMember(field) && jsonValue[field].isNumeric(); +} + +/** + * Named pipe handler for simulated long press on an action switch. + * + * Usage example: + * echo '{"Name": "SimulateActionSwitchLongPress", "EndpointId": 3, "ButtonId": 1, "LongPressDelayMillis": 800, "LongPressDurationMillis": 1000}' > /tmp/chip_all_clusters_fifo_1146610 + * + * JSON Arguments: + * - "Name": Must be "SimulateActionSwitchLongPress" + * - "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 + * + * @param jsonValue - JSON payload from named pipe + */ +void HandleSimulateActionSwitchLongPress(Json::Value & jsonValue) +{ + if (sButtonSimulatorInstance != nullptr) + { + ChipLogError(NotSpecified, "Button simulation already in progress! Ignoring request."); + return; + } + + bool hasEndpointId = HasNumericField(jsonValue, "EndpointId"); + bool hasButtonId = HasNumericField(jsonValue, "ButtonId"); + bool hasLongPressDelayMillis = HasNumericField(jsonValue, "LongPressDelayMillis"); + bool hasLongPressDurationMillis = HasNumericField(jsonValue, "LongPressDurationMillis"); + if (!hasEndpointId || !hasButtonId || !hasLongPressDelayMillis || !hasLongPressDurationMillis) + { + std::string inputJson = jsonValue.toStyledString(); + ChipLogError(NotSpecified, "Missing or invalid value for one of EndpointId, ButtonId, LongPressDelayMillis or LongPressDurationMillis in %s", inputJson.c_str()); + return; + } + + EndpointId endpointId = static_cast(jsonValue["EndpointId"].asUInt()); + 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())}; + auto buttonSimulator = std::make_unique(); + + bool success = buttonSimulator->SetMode(ButtonEventsSimulator::Mode::kModeLongPress).SetLongPressDelayMillis(longPressDelayMillis) + .SetLongPressDurationMillis(longPressDurationMillis).SetIdleButtonId(0).SetPressedButtonId(buttonId).SetEndpointId(endpointId) + .Execute([]() { sButtonSimulatorInstance.reset(); }); + + if (!success) + { + ChipLogError(NotSpecified, "Failed to start execution of button simulator!"); + return; + } + + sButtonSimulatorInstance = std::move(buttonSimulator); +} + +/** + * Named pipe handler for simulated multi-press on an action switch. + * + * Usage example: + * echo '{"Name": "SimulateActionSwitchMultiPress", "EndpointId": 3, "ButtonId": 1, "MultiPressPressedTimeMillis": 100, "MultiPressReleasedTimeMillis": 350, "MultiPressNumPresses": 2}' > /tmp/chip_all_clusters_fifo_1146610 + * + * JSON Arguments: + * - "Name": Must be "SimulateActionSwitchMultiPress" + * - "EndpointId": number of endpoint having a switch cluster + * - "ButtonId": switch position in the switch cluster for "down" button (not idle) + * - "MultiPressPressedTimeMillis": Pressed time in milliseconds for each press + * - "MultiPressReleasedTimeMillis": Released time in milliseconds after each press + * - "MultiPressNumPresses": Number of presses to simulate + * + * @param jsonValue - JSON payload from named pipe + */ +void HandleSimulateActionSwitchMultiPress(Json::Value & jsonValue) +{ + if (sButtonSimulatorInstance != nullptr) + { + ChipLogError(NotSpecified, "Button simulation already in progress! Ignoring request."); + return; + } + + bool hasEndpointId = HasNumericField(jsonValue, "EndpointId"); + bool hasButtonId = HasNumericField(jsonValue, "ButtonId"); + bool hasMultiPressPressedTimeMillis = HasNumericField(jsonValue, "MultiPressPressedTimeMillis"); + bool hasMultiPressReleasedTimeMillis = HasNumericField(jsonValue, "MultiPressReleasedTimeMillis"); + bool hasMultiPressNumPresses = HasNumericField(jsonValue, "MultiPressNumPresses"); + if (!hasEndpointId || !hasButtonId || !hasMultiPressPressedTimeMillis || !hasMultiPressReleasedTimeMillis || !hasMultiPressNumPresses) + { + std::string inputJson = jsonValue.toStyledString(); + ChipLogError(NotSpecified, "Missing or invalid value for one of EndpointId, ButtonId, MultiPressPressedTimeMillis, MultiPressReleasedTimeMillis or MultiPressNumPresses in %s", inputJson.c_str()); + return; + } + + EndpointId endpointId = static_cast(jsonValue["EndpointId"].asUInt()); + uint8_t buttonId = static_cast(jsonValue["ButtonId"].asUInt()); + System::Clock::Milliseconds32 multiPressPressedTimeMillis{static_cast(jsonValue["MultiPressPressedTimeMillis"].asUInt())}; + System::Clock::Milliseconds32 multiPressReleasedTimeMillis{static_cast(jsonValue["MultiPressReleasedTimeMillis"].asUInt())}; + uint8_t multiPressNumPresses = static_cast(jsonValue["MultiPressNumPresses"].asUInt()); + auto buttonSimulator = std::make_unique(); + + bool success = buttonSimulator->SetMode(ButtonEventsSimulator::Mode::kModeMultiPress).SetMultiPressPressedTimeMillis(multiPressPressedTimeMillis) + .SetMultiPressReleasedTimeMillis(multiPressReleasedTimeMillis).SetMultiPressNumPresses(multiPressNumPresses).SetIdleButtonId(0).SetPressedButtonId(buttonId).SetEndpointId(endpointId) + .Execute([]() { sButtonSimulatorInstance.reset(); }); + + if (!success) + { + ChipLogError(NotSpecified, "Failed to start execution of button simulator!"); + return; + } + + sButtonSimulatorInstance = std::move(buttonSimulator); +} + +} // namespace + AllClustersAppCommandHandler * AllClustersAppCommandHandler::FromJSON(const char * json) { Json::Reader reader; @@ -190,6 +314,14 @@ void AllClustersAppCommandHandler::HandleCommand(intptr_t context) std::string operation = self->mJsonValue["Operation"].asString(); self->OnOperationalStateChange(device, operation, self->mJsonValue["Param"]); } + else if (name == "SimulateActionSwitchLongPress") + { + HandleSimulateActionSwitchLongPress(self->mJsonValue); + } + else if (name == "SimulateActionSwitchMultiPress") + { + HandleSimulateActionSwitchMultiPress(self->mJsonValue); + } else { ChipLogError(NotSpecified, "Unhandled command: Should never happens"); diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index 3d52ef748de90d..a2f4ff0ab1f781 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -66,7 +66,10 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/energy-management-app/energy-management-common/src/device-energy-management-mode.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/src/energy-evse-mode.cpp", "AllClustersCommandDelegate.cpp", + "AllClustersCommandDelegate.h", "AppOptions.cpp", + "ButtonEventsSimulator.cpp", + "ButtonEventsSimulator.h", "ValveControlDelegate.cpp", "WindowCoveringManager.cpp", "include/tv-callbacks.cpp", diff --git a/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp b/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp new file mode 100644 index 00000000000000..a8423db121b911 --- /dev/null +++ b/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp @@ -0,0 +1,209 @@ +/* + * + * 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. + */ + +#include "ButtonEventsSimulator.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { + +namespace { + +void SetButtonPosition(EndpointId endpointId, uint8_t position) +{ + Clusters::Switch::Attributes::CurrentPosition::Set(endpointId, position); +} + +void EmitInitialPress(EndpointId endpointId, uint8_t newPosition) +{ + Clusters::Switch::Events::InitialPress::Type event{newPosition}; + EventNumber eventNumber = 0; + + CHIP_ERROR err = LogEvent(event, endpointId, eventNumber); + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to log InitialPress event: %" CHIP_ERROR_FORMAT, err.Format()); + } + else + { + ChipLogProgress(NotSpecified, "Logged InitialPress(%" PRIu8 ") with ID %" PRIu64 " on Endpoint %" PRIu16, + newPosition, eventNumber, endpointId); + } +} + +void EmitLongPress(EndpointId endpointId, uint8_t newPosition) +{ + Clusters::Switch::Events::LongPress::Type event{newPosition}; + EventNumber eventNumber = 0; + + CHIP_ERROR err = LogEvent(event, endpointId, eventNumber); + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to log LongPress event: %" CHIP_ERROR_FORMAT, err.Format()); + } + else + { + ChipLogProgress(NotSpecified, "Logged LongPress(%" PRIu8 ") with ID %" PRIu64 " on Endpoint %" PRIu16, + newPosition, eventNumber, endpointId); + } +} + +void EmitLongRelease(EndpointId endpointId, uint8_t previousPosition) +{ + Clusters::Switch::Events::LongRelease::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 LongRelease event: %" CHIP_ERROR_FORMAT, err.Format()); + } + else + { + ChipLogProgress(NotSpecified, "Logged LongRelease with ID %" PRIu64 " on Endpoint %" PRIu16, + eventNumber, endpointId); + } +} + +void EmitMultiPressComplete(EndpointId endpointId, uint8_t previousPosition, uint8_t count) +{ + Clusters::Switch::Events::MultiPressComplete::Type event{}; + event.previousPosition = previousPosition; + event.totalNumberOfPressesCounted = count; + EventNumber eventNumber = 0; + + CHIP_ERROR err = LogEvent(event, endpointId, eventNumber); + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to log MultiPressComplete event: %" CHIP_ERROR_FORMAT, err.Format()); + } + else + { + ChipLogProgress(NotSpecified, "Logged MultiPressComplete(count=%" PRIu8 ") with ID %" PRIu64 " on Endpoint %" PRIu16, + count, eventNumber, endpointId); + } +} + +} // namespace + +void ButtonEventsSimulator::OnTimerDone(System::Layer * layer, void * appState) +{ + ButtonEventsSimulator *that = reinterpret_cast(appState); + that->Next(); +} + +bool ButtonEventsSimulator::Execute(DoneCallback && doneCallback) +{ + VerifyOrReturnValue(mIdleButtonId != mPressedButtonId, false); + + switch(mMode) + { + case Mode::kModeLongPress: + VerifyOrReturnValue(mLongPressDurationMillis > mLongPressDelayMillis, false); + SetState(State::kEmitStartOfLongPress); + break; + case Mode::kModeMultiPress: + VerifyOrReturnValue(mMultiPressPressedTimeMillis.count() > 0, false); + VerifyOrReturnValue(mMultiPressReleasedTimeMillis.count() > 0, false); + VerifyOrReturnValue(mMultiPressNumPresses > 0, false); + SetState(State::kEmitStartOfMultiPress); + break; + default: + return false; + } + mDoneCallback = std::move(doneCallback); + Next(); + return true; +} + +void ButtonEventsSimulator::SetState(ButtonEventsSimulator::State newState) +{ + ButtonEventsSimulator::State oldState = mState; + if (oldState != newState) + { + ChipLogProgress(NotSpecified, "ButtonEventsSimulator state change %u -> %u", static_cast(oldState), static_cast(newState)); + } + + mState = newState; +} + +void ButtonEventsSimulator::StartTimer(System::Clock::Timeout duration) +{ + chip::DeviceLayer::SystemLayer().StartTimer(duration, &ButtonEventsSimulator::OnTimerDone, this); +} + +void ButtonEventsSimulator::Next() +{ + switch (mState) { + case ButtonEventsSimulator::State::kIdle: { + ChipLogError(NotSpecified, "Found idle state where not expected!"); + break; + } + case ButtonEventsSimulator::State::kEmitStartOfLongPress: { + SetButtonPosition(mEndpointId, mPressedButtonId); + EmitInitialPress(mEndpointId, mPressedButtonId); + SetState(ButtonEventsSimulator::State::kEmitLongPress); + StartTimer(mLongPressDelayMillis); + break; + } + case ButtonEventsSimulator::State::kEmitLongPress: { + EmitLongPress(mEndpointId, mPressedButtonId); + SetState(ButtonEventsSimulator::State::kEmitLongRelease); + StartTimer(mLongPressDurationMillis - mLongPressDelayMillis); + break; + } + case ButtonEventsSimulator::State::kEmitLongRelease: { + SetButtonPosition(mEndpointId, mIdleButtonId); + EmitLongRelease(mEndpointId, mPressedButtonId); + SetState(ButtonEventsSimulator::State::kIdle); + mDoneCallback(); + break; + } + case ButtonEventsSimulator::State::kEmitStartOfMultiPress: { + EmitInitialPress(mEndpointId, mPressedButtonId); + StartTimer(mMultiPressNumPresses * (mMultiPressPressedTimeMillis + mMultiPressReleasedTimeMillis)); + SetState(ButtonEventsSimulator::State::kEmitEndOfMultiPress); + break; + } + case ButtonEventsSimulator::State::kEmitEndOfMultiPress: { + EmitMultiPressComplete(mEndpointId, mPressedButtonId, mMultiPressNumPresses); + SetState(ButtonEventsSimulator::State::kIdle); + mDoneCallback(); + break; + } + } +} + +} // namespace app +} // namespace chip diff --git a/examples/all-clusters-app/linux/ButtonEventsSimulator.h b/examples/all-clusters-app/linux/ButtonEventsSimulator.h new file mode 100644 index 00000000000000..cf3dade65c351f --- /dev/null +++ b/examples/all-clusters-app/linux/ButtonEventsSimulator.h @@ -0,0 +1,143 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace chip { +namespace app { + +/** + * State machine to emit button sequences. Configure with `SetXxx()` methods + * and then call `Execute()` with a functor to be called when done. + * + * The implementation has dependencies on SystemLayer (to start timers) and on + * EventLogging. + * + */ +class ButtonEventsSimulator { + public: + + enum class Mode + { + kModeLongPress, + kModeMultiPress + }; + + using DoneCallback = std::function; + + ButtonEventsSimulator() = default; + + // Returns true on success to start execution, false on something going awry. + // `doneCallback` is called only if execution got started. + bool Execute(DoneCallback && doneCallback); + + ButtonEventsSimulator& SetLongPressDelayMillis(System::Clock::Milliseconds32 longPressDelayMillis) + { + mLongPressDelayMillis = longPressDelayMillis; + return *this; + } + + ButtonEventsSimulator& SetLongPressDurationMillis(System::Clock::Milliseconds32 longPressDurationMillis) + { + mLongPressDurationMillis = longPressDurationMillis; + return *this; + } + + ButtonEventsSimulator& SetMultiPressPressedTimeMillis(System::Clock::Milliseconds32 multiPressPressedTimeMillis) + { + mMultiPressPressedTimeMillis = multiPressPressedTimeMillis; + return *this; + } + + ButtonEventsSimulator& SetMultiPressReleasedTimeMillis(System::Clock::Milliseconds32 multiPressReleasedTimeMillis) + { + mMultiPressReleasedTimeMillis = multiPressReleasedTimeMillis; + return *this; + } + + ButtonEventsSimulator& SetMultiPressNumPresses(uint8_t multiPressNumPresses) + { + mMultiPressNumPresses = multiPressNumPresses; + return *this; + } + + ButtonEventsSimulator& SetIdleButtonId(uint8_t idleButtonId) + { + mIdleButtonId = idleButtonId; + return *this; + } + + ButtonEventsSimulator& SetPressedButtonId(uint8_t pressedButtonId) + { + mPressedButtonId = pressedButtonId; + return *this; + } + + ButtonEventsSimulator& SetMode(Mode mode) + { + mMode = mode; + return *this; + } + + ButtonEventsSimulator& SetEndpointId(EndpointId endpointId) + { + mEndpointId = endpointId; + return *this; + } + + private: + enum class State + { + kIdle = 0, + + kEmitStartOfLongPress = 1, + kEmitLongPress = 2, + kEmitLongRelease = 3, + + kEmitStartOfMultiPress = 4, + kEmitEndOfMultiPress = 5, + }; + + static void OnTimerDone(System::Layer * layer, void * appState); + void SetState(State newState); + void Next(); + void StartTimer(System::Clock::Timeout duration); + + DoneCallback mDoneCallback; + System::Clock::Milliseconds32 mLongPressDelayMillis{}; + System::Clock::Milliseconds32 mLongPressDurationMillis{}; + System::Clock::Milliseconds32 mMultiPressPressedTimeMillis{}; + System::Clock::Milliseconds32 mMultiPressReleasedTimeMillis{}; + uint8_t mMultiPressNumPresses{1}; + uint8_t mIdleButtonId{0}; + uint8_t mPressedButtonId{1}; + EndpointId mEndpointId{1}; + + Mode mMode{Mode::kModeLongPress}; + State mState{State::kIdle}; +}; + +} // namespace app +} // namespace chip diff --git a/src/python_testing/TC_SWTCH.py b/src/python_testing/TC_SWTCH.py new file mode 100644 index 00000000000000..fbcdc5ba6250a9 --- /dev/null +++ b/src/python_testing/TC_SWTCH.py @@ -0,0 +1,282 @@ +# +# Copyright (c) 2023 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: --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 +# === END CI TEST ARGUMENTS === + +import logging +import json +import queue +import time + +from typing import Any +from mobly import asserts +import chip.clusters as Clusters +from chip.clusters import ClusterObjects as ClusterObjects +from chip.clusters.Attribute import TypedAttributePath, EventReadResult +from chip.clusters.Types import NullValue +from matter_testing_support import EventChangeCallback, ClusterAttributeChangeAccumulator, AttributeValue, MatterBaseTest, TestStep, async_test_body, default_matter_test_main + +logger = logging.getLogger(__name__) + +class TC_SwitchTests(MatterBaseTest): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + 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), + # # TODO: fill when test is done + # ] + + # return steps + + def _send_named_pipe_command(self, command_dict: dict[str, Any]): + app_pid = self.matter_test_config.app_pid + if app_pid == 0: + asserts.fail("The --app-pid flag must be set when usage of button simulation named pipe is required (e.g. CI)") + + app_pipe = f"/tmp/chip_all_clusters_fifo_{app_pid}" + command = json.dumps(command_dict) + + # Sends an out-of-band command to the sample app + with open(app_pipe, "w") as outfile: + logging.info(f"Sending named pipe command to {app_pipe}: '{command}'") + outfile.write(command + "\n") + # Delay for pipe command to be processed (otherwise tests may be flaky). + time.sleep(0.1) + + def _use_button_simulator(self) -> bool: + return self.check_pics("PICS_SDK_CI_ONLY") or self.user_params.get("use_button_simulator", False) + + 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): + 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) + + def _placeholder_for_step(self, step_id: str): + # TODO: Global search an replace of `self._placeholder_for_step` with `self.step` when done. + logging.info(f"Step {step_id}") + pass + + def _placeholder_for_skip(self, step_id: str): + logging.info(f"Skipped step {step_id}") + + def _await_sequence_of_reports(self, report_queue: queue.Queue, endpoint_id: int, attribute: TypedAttributePath, sequence: list[Any], timeout_sec: float): + start_time = time.time() + elapsed = 0.0 + time_remaining = timeout_sec + + sequence_idx = 0 + actual_values = [] + + while time_remaining > 0: + expected_value = sequence[sequence_idx] + logging.info(f"Expecting value {expected_value} for attribute {attribute} on endpoint {endpoint_id}") + try: + item: AttributeValue = report_queue.get(block=True, timeout=time_remaining) + + # Track arrival of all values for the given attribute. + if item.endpoint_id == endpoint_id and item.attribute == attribute: + actual_values.append(item.value) + + if item.value == expected_value: + logging.info(f"Got expected attribute change {sequence_idx+1}/{len(sequence)} for attribute {attribute}") + sequence_idx += 1 + else: + asserts.assert_equal(item.value, expected_value, msg="Did not get expected attribute value in correct sequence.") + + # We are done waiting when we have accumulated all results. + if sequence_idx == len(sequence): + logging.info("Got all attribute changes, done waiting.") + return + except queue.Empty: + # No error, we update timeouts and keep going + pass + + elapsed = time.time() - start_time + time_remaining = timeout_sec - elapsed + + asserts.fail(f"Did not get full sequence {sequence} in {timeout_sec:.1f} seconds. Got {actual_values} before time-out.") + + def _await_sequence_of_events(self, event_queue: queue.Queue, endpoint_id: int, sequence: list[ClusterObjects.ClusterEvent], timeout_sec: float): + start_time = time.time() + elapsed = 0.0 + time_remaining = timeout_sec + + sequence_idx = 0 + actual_events = [] + + while time_remaining > 0: + logging.info(f"Expecting event {sequence[sequence_idx]} on endpoint {endpoint_id}") + try: + item: EventReadResult = event_queue.get(block=True, timeout=time_remaining) + expected_event = sequence[sequence_idx] + event_data = item.Data + + if item.Header.EndpointId == endpoint_id and item.Header.ClusterId == event_data.cluster_id: + actual_events.append(event_data) + + if event_data == expected_event: + logging.info(f"Got expected Event {sequence_idx+1}/{len(sequence)}: {event_data}") + sequence_idx += 1 + else: + asserts.assert_equal(event_data, expected_event, msg="Did not get expected event in correct sequence.") + + # We are done waiting when we have accumulated all results. + if sequence_idx == len(sequence): + logging.info("Got all expected events, done waiting.") + return + except queue.Empty: + # No error, we update timeouts and keep going + pass + + elapsed = time.time() - start_time + time_remaining = timeout_sec - elapsed + + asserts.fail(f"Did not get full sequence {sequence} in {timeout_sec:.1f} seconds. Got {actual_events} before time-out.") + + def _expect_no_events_for_cluster(self, event_queue: queue.Queue, endpoint_id: int, expected_cluster: ClusterObjects.Cluster, timeout_sec: float): + start_time = time.time() + elapsed = 0.0 + time_remaining = timeout_sec + + sequence_idx = 0 + + logging.info(f"Waiting {timeout_sec:.1f} seconds for no more events for cluster {expected_cluster} on endpoint {endpoint_id}") + while time_remaining > 0: + try: + item: EventReadResult = event_queue.get(block=True, timeout=time_remaining) + event_data = item.Data + + if item.Header.EndpointId == endpoint_id and item.Header.ClusterId == event_data.cluster_id and item.Header.ClusterId == expected_cluster.id: + asserts.fail(f"Got Event {event_data} when we expected no further events for {expected_cluster}") + except queue.Empty: + # No error, we update timeouts and keep going + pass + + elapsed = time.time() - start_time + time_remaining = timeout_sec - elapsed + + logging.info(f"Successfully waited for no further events on {expected_cluster} for {elapsed:.1f} seconds") + + @async_test_body + async def test_TC_SWTCH_2_4(self): + # TODO: Make this come from PIXIT + switch_pressed_position = 1 + post_prompt_settle_delay_seconds = 10.0 + + # Commission DUT - already done + + # Read feature map to set bool markers + cluster = Clusters.Objects.Switch + feature_map = await self.read_single_attribute_check_success(cluster, attribute=cluster.Attributes.FeatureMap) + + has_ms_feature = (feature_map & cluster.Bitmaps.Feature.kMomentarySwitch) != 0 + 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 + has_msm_feature = (feature_map & cluster.Bitmaps.Feature.kMomentarySwitchMultiPress) != 0 + + if not has_ms_feature: + logging.info("Skipping rest of test: SWTCH.S.F01(MS) feature not present") + self.skip_all_remaining_steps("2") + + endpoint_id = self.matter_test_config.endpoint + + # Step 1: Set up subscription to all Switch cluster events + self._placeholder_for_step("1") + event_listener = EventChangeCallback(cluster) + attrib_listener = ClusterAttributeChangeAccumulator(cluster) + event_sub = await event_listener.start(self.default_controller, self.dut_node_id, endpoint=endpoint_id) + cluster_sub = await attrib_listener.start(self.default_controller, self.dut_node_id, endpoint=endpoint_id) + + # Step 2: Operator does not operate switch on the DUT + self._placeholder_for_step("2") + self._ask_for_switch_idle() + + # Step 3: TH reads the CurrentPosition attribute from the DUT + self._placeholder_for_step("3") + + # Verify that the value is 0 + current_position = await self.read_single_attribute_check_success(cluster, attribute=cluster.Attributes.CurrentPosition) + asserts.assert_equal(current_position, 0) + + # 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) + + # Step 4b: TH expects report of CurrentPosition 1, followed by a report of Current Position 0. + self._placeholder_for_step("4b") + logging.info(f"Starting to wait for {post_prompt_settle_delay_seconds:.1f} seconds for CurrentPosition to go {switch_pressed_position}, then 0.") + self._await_sequence_of_reports(report_queue=attrib_listener.attribute_queue, endpoint_id=endpoint_id, attribute=cluster.Attributes.CurrentPosition, sequence = [switch_pressed_position, 0], timeout_sec=post_prompt_settle_delay_seconds) + + # Step 4c: TH expects at least InitialPress with NewPosition = 1 + self._placeholder_for_step("4c") + logging.info(f"Starting to wait for {post_prompt_settle_delay_seconds:.1f} seconds for InitialPress event.") + expected_events = [cluster.Events.InitialPress(newPosition=switch_pressed_position)] + self._await_sequence_of_events(event_queue=event_listener.event_queue, endpoint_id=endpoint_id, sequence=expected_events, timeout_sec=post_prompt_settle_delay_seconds) + + # Step 4d: For MSL/AS, expect to see LongPress/LongRelease in that order + if not has_msl_feature and not has_as_feature: + logging.info("Skipping Step 4d due to missing MSL and AS features") + self._placeholder_for_skip("4d") + else: + # Steb 4d: TH expects report of LongPress, LongRelease in that order. + self._placeholder_for_step("4d") + logging.info(f"Starting to wait for {post_prompt_settle_delay_seconds:.1f} seconds for LongPress then LongRelease.") + expected_events = [] + expected_events.append(cluster.Events.LongPress(newPosition=switch_pressed_position)) + expected_events.append(cluster.Events.LongRelease(previousPosition=switch_pressed_position)) + self._await_sequence_of_events(event_queue=event_listener.event_queue, endpoint_id=endpoint_id, sequence=expected_events, timeout_sec=post_prompt_settle_delay_seconds) + + # Step 4e: For MS & (!MSL & !AS & !MSR), expect no further events for 10 seconds. + if not has_msl_feature and not has_as_feature and not has_msr_feature: + self._placeholder_for_step("4e") + self._expect_no_events_for_cluster(event_queue=event_listener.event_queue, endpoint_id=endpoint_id, expected_cluster=cluster, timeout_sec=10.0) + + # Step 4f: For MSR & not MSL, expect to see ShortRelease. + if not has_msl_feature and has_msr_feature: + self._placeholder_for_step("4f") + expected_events = [cluster.Events.ShortRelease(previousPosition=switch_pressed_position)] + self._await_sequence_of_events(event_queue=event_listener.event_queue, endpoint_id=endpoint_id, sequence=expected_events, timeout_sec=post_prompt_settle_delay_seconds) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index b8854e23a7d2ee..d91853304504a7 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -34,7 +34,7 @@ from dataclasses import dataclass, field from datetime import datetime, timedelta, timezone from enum import Enum -from typing import List, Optional, Tuple +from typing import List, Optional, Tuple, Any from chip.tlv import float32, uint @@ -231,19 +231,21 @@ def name(self) -> str: class EventChangeCallback: - def __init__(self, expected_cluster: ClusterObjects): + def __init__(self, expected_cluster: ClusterObjects.Cluster): """This class creates a queue to store received event callbacks, that can be checked by the test script expected_cluster: is the cluster from which the events are expected """ self._q = queue.Queue() self._expected_cluster = expected_cluster - async def start(self, dev_ctrl, node_id: int, endpoint: int): + async def start(self, dev_ctrl, node_id: int, endpoint: int, fabric_filtered: bool=False, min_interval_sec: int=0, max_interval_sec: int=30) -> Any: """This starts a subscription for events on the specified node_id and endpoint. The cluster is specified when the class instance is created.""" + urgent = True self._subscription = await dev_ctrl.ReadEvent(node_id, - events=[(endpoint, self._expected_cluster, True)], reportInterval=(1, 5), - fabricFiltered=False, keepSubscriptions=True, autoResubscribe=False) + events=[(endpoint, self._expected_cluster, urgent)], reportInterval=(min_interval_sec, max_interval_sec), + fabricFiltered=fabric_filtered, keepSubscriptions=True, autoResubscribe=False) self._subscription.SetEventUpdateCallback(self.__call__) + return self._subscription def __call__(self, res: EventReadResult, transaction: SubscriptionTransaction): """This is the subscription callback when an event is received. @@ -265,6 +267,10 @@ def wait_for_event_report(self, expected_event: ClusterObjects.ClusterEvent, tim asserts.assert_equal(res.Header.EventId, expected_event.event_id, "Expected event ID not found in event report") return res.Data + @property + def event_queue(self) -> queue.Queue: + return self._q + class AttributeChangeCallback: def __init__(self, expected_attribute: ClusterObjects.ClusterAttributeDescriptor): @@ -298,6 +304,44 @@ def wait_for_report(self): except KeyError: asserts.fail("[AttributeChangeCallback] Attribute {expected_attribute} not found in returned report") +@dataclass +class AttributeValue: + endpoint_id: int + attribute: ClusterObjects.ClusterAttributeDescriptor + value: Any + + +class ClusterAttributeChangeAccumulator: + def __init__(self, expected_cluster: ClusterObjects.Cluster): + self._q = queue.Queue() + self._expected_cluster = expected_cluster + self._subscription = None + + async def start(self, dev_ctrl, node_id: int, endpoint: int, fabric_filtered: bool=False, min_interval_sec: int=0, max_interval_sec: int=30) -> Any: + """This starts a subscription for attributes on the specified node_id and endpoint. The cluster is specified when the class instance is created.""" + self._subscription = await dev_ctrl.ReadAttribute( + nodeid=node_id, + attributes=[(endpoint, self._expected_cluster)], + reportInterval=(min_interval_sec, max_interval_sec), + fabricFiltered=fabric_filtered, + keepSubscriptions=True + ) + self._subscription.SetAttributeUpdateCallback(self.__call__) + return self._subscription + + def __call__(self, path: TypedAttributePath, transaction: SubscriptionTransaction): + """This is the subscription callback when an attribute report is received. + It checks the report is from the expected_cluster and then posts it into the queue for later processing.""" + if path.ClusterType == self._expected_cluster: + data = transaction.GetAttribute(path) + value = AttributeValue(endpoint_id=path.Path.EndpointId, attribute=path.AttributeType, value=data) + logging.info(f"Got subscription report for {path.AttributeType}: {data}") + self._q.put(value) + + @property + def attribute_queue(self) -> queue.Queue: + return self._q + class InternalTestRunnerHooks(TestRunnerHooks): @@ -682,7 +726,7 @@ def get_test_steps(self, test: str) -> list[TestStep]: return [TestStep(1, "Run entire test")] if steps is None else steps def get_defined_test_steps(self, test: str) -> list[TestStep]: - steps_name = 'steps_' + test[5:] + steps_name = f'steps_{test.removeprefix("test_")}' try: fn = getattr(self, steps_name) return fn() @@ -702,7 +746,7 @@ def get_test_pics(self, test: str) -> list[str]: return [] if pics is None else pics def _get_defined_pics(self, test: str) -> list[TestStep]: - steps_name = 'pics_' + test[5:] + steps_name = f'pics_{test.removeprefix("test_")}' try: fn = getattr(self, steps_name) return fn() @@ -722,7 +766,7 @@ def get_test_desc(self, test: str) -> str: ex: 133.1.1. [TC-ACL-1.1] Global attributes ''' - desc_name = 'desc_' + test[5:] + desc_name = f'desc_{test.removeprefix("test_")}' try: fn = getattr(self, desc_name) return fn() @@ -1021,7 +1065,7 @@ def mark_current_step_skipped(self): logging.info(f'**** Skipping: {num}') self.step_skipped = True - def skip_step(self, step): + def skip_steps(self, step): self.step(step) self.mark_current_step_skipped() @@ -1116,7 +1160,9 @@ def wait_for_user_input(self, self.runner_hook.show_prompt(msg=prompt_msg, placeholder=prompt_msg_placeholder, default_value=default_value) - return input(f'{prompt_msg.removesuffix(chr(10))}\n') + logging.info("========= USER PROMPT =========") + logging.info(f">>> {prompt_msg.rstrip()} (press enter to confirm)") + return input() def generate_mobly_test_config(matter_test_config: MatterTestConfig): From ca59fd9fc260038cd2a80dd9cd98f6b74cf42511 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Thu, 18 Jul 2024 21:16:52 +0000 Subject: [PATCH 02/10] Restyled by clang-format --- .../linux/AllClustersCommandDelegate.cpp | 81 ++++++++++++------- .../linux/ButtonEventsSimulator.cpp | 69 ++++++++-------- .../linux/ButtonEventsSimulator.h | 54 ++++++------- 3 files changed, 112 insertions(+), 92 deletions(-) diff --git a/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp b/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp index 4687a39ce7e230..3c42eb1dd6a32e 100644 --- a/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp +++ b/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp @@ -28,8 +28,8 @@ #include #include -#include #include "ButtonEventsSimulator.h" +#include #include #include #include @@ -46,12 +46,11 @@ using namespace chip::app; using namespace chip::app::Clusters; using namespace chip::DeviceLayer; -namespace -{ +namespace { -std::unique_ptr sButtonSimulatorInstance{nullptr}; +std::unique_ptr sButtonSimulatorInstance{ nullptr }; -bool HasNumericField(Json::Value & jsonValue, const std::string& field) +bool HasNumericField(Json::Value & jsonValue, const std::string & field) { return jsonValue.isMember(field) && jsonValue[field].isNumeric(); } @@ -60,7 +59,8 @@ bool HasNumericField(Json::Value & jsonValue, const std::string& field) * Named pipe handler for simulated long press on an action switch. * * Usage example: - * echo '{"Name": "SimulateActionSwitchLongPress", "EndpointId": 3, "ButtonId": 1, "LongPressDelayMillis": 800, "LongPressDurationMillis": 1000}' > /tmp/chip_all_clusters_fifo_1146610 + * echo '{"Name": "SimulateActionSwitchLongPress", "EndpointId": 3, "ButtonId": 1, "LongPressDelayMillis": 800, + * "LongPressDurationMillis": 1000}' > /tmp/chip_all_clusters_fifo_1146610 * * JSON Arguments: * - "Name": Must be "SimulateActionSwitchLongPress" @@ -79,26 +79,33 @@ void HandleSimulateActionSwitchLongPress(Json::Value & jsonValue) return; } - bool hasEndpointId = HasNumericField(jsonValue, "EndpointId"); - bool hasButtonId = HasNumericField(jsonValue, "ButtonId"); - bool hasLongPressDelayMillis = HasNumericField(jsonValue, "LongPressDelayMillis"); + bool hasEndpointId = HasNumericField(jsonValue, "EndpointId"); + bool hasButtonId = HasNumericField(jsonValue, "ButtonId"); + bool hasLongPressDelayMillis = HasNumericField(jsonValue, "LongPressDelayMillis"); bool hasLongPressDurationMillis = HasNumericField(jsonValue, "LongPressDurationMillis"); if (!hasEndpointId || !hasButtonId || !hasLongPressDelayMillis || !hasLongPressDurationMillis) { 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 or LongPressDurationMillis in %s", + inputJson.c_str()); return; } EndpointId endpointId = static_cast(jsonValue["EndpointId"].asUInt()); - 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())}; + 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()) }; auto buttonSimulator = std::make_unique(); - bool success = buttonSimulator->SetMode(ButtonEventsSimulator::Mode::kModeLongPress).SetLongPressDelayMillis(longPressDelayMillis) - .SetLongPressDurationMillis(longPressDurationMillis).SetIdleButtonId(0).SetPressedButtonId(buttonId).SetEndpointId(endpointId) - .Execute([]() { sButtonSimulatorInstance.reset(); }); + bool success = buttonSimulator->SetMode(ButtonEventsSimulator::Mode::kModeLongPress) + .SetLongPressDelayMillis(longPressDelayMillis) + .SetLongPressDurationMillis(longPressDurationMillis) + .SetIdleButtonId(0) + .SetPressedButtonId(buttonId) + .SetEndpointId(endpointId) + .Execute([]() { sButtonSimulatorInstance.reset(); }); if (!success) { @@ -113,7 +120,8 @@ void HandleSimulateActionSwitchLongPress(Json::Value & jsonValue) * Named pipe handler for simulated multi-press on an action switch. * * Usage example: - * echo '{"Name": "SimulateActionSwitchMultiPress", "EndpointId": 3, "ButtonId": 1, "MultiPressPressedTimeMillis": 100, "MultiPressReleasedTimeMillis": 350, "MultiPressNumPresses": 2}' > /tmp/chip_all_clusters_fifo_1146610 + * echo '{"Name": "SimulateActionSwitchMultiPress", "EndpointId": 3, "ButtonId": 1, "MultiPressPressedTimeMillis": 100, + * "MultiPressReleasedTimeMillis": 350, "MultiPressNumPresses": 2}' > /tmp/chip_all_clusters_fifo_1146610 * * JSON Arguments: * - "Name": Must be "SimulateActionSwitchMultiPress" @@ -133,28 +141,39 @@ void HandleSimulateActionSwitchMultiPress(Json::Value & jsonValue) return; } - bool hasEndpointId = HasNumericField(jsonValue, "EndpointId"); - bool hasButtonId = HasNumericField(jsonValue, "ButtonId"); - bool hasMultiPressPressedTimeMillis = HasNumericField(jsonValue, "MultiPressPressedTimeMillis"); + bool hasEndpointId = HasNumericField(jsonValue, "EndpointId"); + bool hasButtonId = HasNumericField(jsonValue, "ButtonId"); + bool hasMultiPressPressedTimeMillis = HasNumericField(jsonValue, "MultiPressPressedTimeMillis"); bool hasMultiPressReleasedTimeMillis = HasNumericField(jsonValue, "MultiPressReleasedTimeMillis"); - bool hasMultiPressNumPresses = HasNumericField(jsonValue, "MultiPressNumPresses"); - if (!hasEndpointId || !hasButtonId || !hasMultiPressPressedTimeMillis || !hasMultiPressReleasedTimeMillis || !hasMultiPressNumPresses) + bool hasMultiPressNumPresses = HasNumericField(jsonValue, "MultiPressNumPresses"); + if (!hasEndpointId || !hasButtonId || !hasMultiPressPressedTimeMillis || !hasMultiPressReleasedTimeMillis || + !hasMultiPressNumPresses) { std::string inputJson = jsonValue.toStyledString(); - ChipLogError(NotSpecified, "Missing or invalid value for one of EndpointId, ButtonId, MultiPressPressedTimeMillis, MultiPressReleasedTimeMillis or MultiPressNumPresses in %s", inputJson.c_str()); + ChipLogError(NotSpecified, + "Missing or invalid value for one of EndpointId, ButtonId, MultiPressPressedTimeMillis, " + "MultiPressReleasedTimeMillis or MultiPressNumPresses in %s", + inputJson.c_str()); return; } EndpointId endpointId = static_cast(jsonValue["EndpointId"].asUInt()); - uint8_t buttonId = static_cast(jsonValue["ButtonId"].asUInt()); - System::Clock::Milliseconds32 multiPressPressedTimeMillis{static_cast(jsonValue["MultiPressPressedTimeMillis"].asUInt())}; - System::Clock::Milliseconds32 multiPressReleasedTimeMillis{static_cast(jsonValue["MultiPressReleasedTimeMillis"].asUInt())}; + uint8_t buttonId = static_cast(jsonValue["ButtonId"].asUInt()); + System::Clock::Milliseconds32 multiPressPressedTimeMillis{ static_cast( + jsonValue["MultiPressPressedTimeMillis"].asUInt()) }; + System::Clock::Milliseconds32 multiPressReleasedTimeMillis{ static_cast( + jsonValue["MultiPressReleasedTimeMillis"].asUInt()) }; uint8_t multiPressNumPresses = static_cast(jsonValue["MultiPressNumPresses"].asUInt()); - auto buttonSimulator = std::make_unique(); - - bool success = buttonSimulator->SetMode(ButtonEventsSimulator::Mode::kModeMultiPress).SetMultiPressPressedTimeMillis(multiPressPressedTimeMillis) - .SetMultiPressReleasedTimeMillis(multiPressReleasedTimeMillis).SetMultiPressNumPresses(multiPressNumPresses).SetIdleButtonId(0).SetPressedButtonId(buttonId).SetEndpointId(endpointId) - .Execute([]() { sButtonSimulatorInstance.reset(); }); + auto buttonSimulator = std::make_unique(); + + bool success = buttonSimulator->SetMode(ButtonEventsSimulator::Mode::kModeMultiPress) + .SetMultiPressPressedTimeMillis(multiPressPressedTimeMillis) + .SetMultiPressReleasedTimeMillis(multiPressReleasedTimeMillis) + .SetMultiPressNumPresses(multiPressNumPresses) + .SetIdleButtonId(0) + .SetPressedButtonId(buttonId) + .SetEndpointId(endpointId) + .Execute([]() { sButtonSimulatorInstance.reset(); }); if (!success) { diff --git a/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp b/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp index a8423db121b911..af18e233c2d55d 100644 --- a/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp +++ b/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp @@ -18,10 +18,10 @@ #include "ButtonEventsSimulator.h" -#include #include #include #include +#include #include #include @@ -31,8 +31,8 @@ #include #include #include -#include #include +#include namespace chip { namespace app { @@ -46,7 +46,7 @@ void SetButtonPosition(EndpointId endpointId, uint8_t position) void EmitInitialPress(EndpointId endpointId, uint8_t newPosition) { - Clusters::Switch::Events::InitialPress::Type event{newPosition}; + Clusters::Switch::Events::InitialPress::Type event{ newPosition }; EventNumber eventNumber = 0; CHIP_ERROR err = LogEvent(event, endpointId, eventNumber); @@ -56,14 +56,14 @@ void EmitInitialPress(EndpointId endpointId, uint8_t newPosition) } else { - ChipLogProgress(NotSpecified, "Logged InitialPress(%" PRIu8 ") with ID %" PRIu64 " on Endpoint %" PRIu16, - newPosition, eventNumber, endpointId); + ChipLogProgress(NotSpecified, "Logged InitialPress(%" PRIu8 ") with ID %" PRIu64 " on Endpoint %" PRIu16, newPosition, + eventNumber, endpointId); } } void EmitLongPress(EndpointId endpointId, uint8_t newPosition) { - Clusters::Switch::Events::LongPress::Type event{newPosition}; + Clusters::Switch::Events::LongPress::Type event{ newPosition }; EventNumber eventNumber = 0; CHIP_ERROR err = LogEvent(event, endpointId, eventNumber); @@ -73,15 +73,15 @@ void EmitLongPress(EndpointId endpointId, uint8_t newPosition) } else { - ChipLogProgress(NotSpecified, "Logged LongPress(%" PRIu8 ") with ID %" PRIu64 " on Endpoint %" PRIu16, - newPosition, eventNumber, endpointId); + ChipLogProgress(NotSpecified, "Logged LongPress(%" PRIu8 ") with ID %" PRIu64 " on Endpoint %" PRIu16, newPosition, + eventNumber, endpointId); } } void EmitLongRelease(EndpointId endpointId, uint8_t previousPosition) { Clusters::Switch::Events::LongRelease::Type event{}; - event.previousPosition = previousPosition; + event.previousPosition = previousPosition; EventNumber eventNumber = 0; CHIP_ERROR err = LogEvent(event, endpointId, eventNumber); @@ -91,17 +91,16 @@ void EmitLongRelease(EndpointId endpointId, uint8_t previousPosition) } else { - ChipLogProgress(NotSpecified, "Logged LongRelease with ID %" PRIu64 " on Endpoint %" PRIu16, - eventNumber, endpointId); + ChipLogProgress(NotSpecified, "Logged LongRelease with ID %" PRIu64 " on Endpoint %" PRIu16, eventNumber, endpointId); } } void EmitMultiPressComplete(EndpointId endpointId, uint8_t previousPosition, uint8_t count) { Clusters::Switch::Events::MultiPressComplete::Type event{}; - event.previousPosition = previousPosition; + event.previousPosition = previousPosition; event.totalNumberOfPressesCounted = count; - EventNumber eventNumber = 0; + EventNumber eventNumber = 0; CHIP_ERROR err = LogEvent(event, endpointId, eventNumber); if (err != CHIP_NO_ERROR) @@ -110,8 +109,8 @@ void EmitMultiPressComplete(EndpointId endpointId, uint8_t previousPosition, uin } else { - ChipLogProgress(NotSpecified, "Logged MultiPressComplete(count=%" PRIu8 ") with ID %" PRIu64 " on Endpoint %" PRIu16, - count, eventNumber, endpointId); + ChipLogProgress(NotSpecified, "Logged MultiPressComplete(count=%" PRIu8 ") with ID %" PRIu64 " on Endpoint %" PRIu16, count, + eventNumber, endpointId); } } @@ -119,7 +118,7 @@ void EmitMultiPressComplete(EndpointId endpointId, uint8_t previousPosition, uin void ButtonEventsSimulator::OnTimerDone(System::Layer * layer, void * appState) { - ButtonEventsSimulator *that = reinterpret_cast(appState); + ButtonEventsSimulator * that = reinterpret_cast(appState); that->Next(); } @@ -127,19 +126,19 @@ bool ButtonEventsSimulator::Execute(DoneCallback && doneCallback) { VerifyOrReturnValue(mIdleButtonId != mPressedButtonId, false); - switch(mMode) + switch (mMode) { - case Mode::kModeLongPress: + case Mode::kModeLongPress: VerifyOrReturnValue(mLongPressDurationMillis > mLongPressDelayMillis, false); SetState(State::kEmitStartOfLongPress); break; - case Mode::kModeMultiPress: + case Mode::kModeMultiPress: VerifyOrReturnValue(mMultiPressPressedTimeMillis.count() > 0, false); VerifyOrReturnValue(mMultiPressReleasedTimeMillis.count() > 0, false); VerifyOrReturnValue(mMultiPressNumPresses > 0, false); SetState(State::kEmitStartOfMultiPress); break; - default: + default: return false; } mDoneCallback = std::move(doneCallback); @@ -152,7 +151,8 @@ void ButtonEventsSimulator::SetState(ButtonEventsSimulator::State newState) ButtonEventsSimulator::State oldState = mState; if (oldState != newState) { - ChipLogProgress(NotSpecified, "ButtonEventsSimulator state change %u -> %u", static_cast(oldState), static_cast(newState)); + ChipLogProgress(NotSpecified, "ButtonEventsSimulator state change %u -> %u", static_cast(oldState), + static_cast(newState)); } mState = newState; @@ -165,43 +165,44 @@ void ButtonEventsSimulator::StartTimer(System::Clock::Timeout duration) void ButtonEventsSimulator::Next() { - switch (mState) { - case ButtonEventsSimulator::State::kIdle: { + switch (mState) + { + case ButtonEventsSimulator::State::kIdle: { ChipLogError(NotSpecified, "Found idle state where not expected!"); break; - } - case ButtonEventsSimulator::State::kEmitStartOfLongPress: { + } + case ButtonEventsSimulator::State::kEmitStartOfLongPress: { SetButtonPosition(mEndpointId, mPressedButtonId); EmitInitialPress(mEndpointId, mPressedButtonId); SetState(ButtonEventsSimulator::State::kEmitLongPress); StartTimer(mLongPressDelayMillis); break; - } - case ButtonEventsSimulator::State::kEmitLongPress: { + } + case ButtonEventsSimulator::State::kEmitLongPress: { EmitLongPress(mEndpointId, mPressedButtonId); SetState(ButtonEventsSimulator::State::kEmitLongRelease); StartTimer(mLongPressDurationMillis - mLongPressDelayMillis); break; - } - case ButtonEventsSimulator::State::kEmitLongRelease: { + } + case ButtonEventsSimulator::State::kEmitLongRelease: { SetButtonPosition(mEndpointId, mIdleButtonId); EmitLongRelease(mEndpointId, mPressedButtonId); SetState(ButtonEventsSimulator::State::kIdle); mDoneCallback(); break; - } - case ButtonEventsSimulator::State::kEmitStartOfMultiPress: { + } + case ButtonEventsSimulator::State::kEmitStartOfMultiPress: { EmitInitialPress(mEndpointId, mPressedButtonId); StartTimer(mMultiPressNumPresses * (mMultiPressPressedTimeMillis + mMultiPressReleasedTimeMillis)); SetState(ButtonEventsSimulator::State::kEmitEndOfMultiPress); break; - } - case ButtonEventsSimulator::State::kEmitEndOfMultiPress: { + } + case ButtonEventsSimulator::State::kEmitEndOfMultiPress: { 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 cf3dade65c351f..658da98f14fefd 100644 --- a/examples/all-clusters-app/linux/ButtonEventsSimulator.h +++ b/examples/all-clusters-app/linux/ButtonEventsSimulator.h @@ -36,13 +36,13 @@ namespace app { * EventLogging. * */ -class ButtonEventsSimulator { - public: - +class ButtonEventsSimulator +{ +public: enum class Mode { - kModeLongPress, - kModeMultiPress + kModeLongPress, + kModeMultiPress }; using DoneCallback = std::function; @@ -53,71 +53,71 @@ class ButtonEventsSimulator { // `doneCallback` is called only if execution got started. bool Execute(DoneCallback && doneCallback); - ButtonEventsSimulator& SetLongPressDelayMillis(System::Clock::Milliseconds32 longPressDelayMillis) + ButtonEventsSimulator & SetLongPressDelayMillis(System::Clock::Milliseconds32 longPressDelayMillis) { mLongPressDelayMillis = longPressDelayMillis; return *this; } - ButtonEventsSimulator& SetLongPressDurationMillis(System::Clock::Milliseconds32 longPressDurationMillis) + ButtonEventsSimulator & SetLongPressDurationMillis(System::Clock::Milliseconds32 longPressDurationMillis) { mLongPressDurationMillis = longPressDurationMillis; return *this; } - ButtonEventsSimulator& SetMultiPressPressedTimeMillis(System::Clock::Milliseconds32 multiPressPressedTimeMillis) + ButtonEventsSimulator & SetMultiPressPressedTimeMillis(System::Clock::Milliseconds32 multiPressPressedTimeMillis) { mMultiPressPressedTimeMillis = multiPressPressedTimeMillis; return *this; } - ButtonEventsSimulator& SetMultiPressReleasedTimeMillis(System::Clock::Milliseconds32 multiPressReleasedTimeMillis) + ButtonEventsSimulator & SetMultiPressReleasedTimeMillis(System::Clock::Milliseconds32 multiPressReleasedTimeMillis) { mMultiPressReleasedTimeMillis = multiPressReleasedTimeMillis; return *this; } - ButtonEventsSimulator& SetMultiPressNumPresses(uint8_t multiPressNumPresses) + ButtonEventsSimulator & SetMultiPressNumPresses(uint8_t multiPressNumPresses) { mMultiPressNumPresses = multiPressNumPresses; return *this; } - ButtonEventsSimulator& SetIdleButtonId(uint8_t idleButtonId) + ButtonEventsSimulator & SetIdleButtonId(uint8_t idleButtonId) { mIdleButtonId = idleButtonId; return *this; } - ButtonEventsSimulator& SetPressedButtonId(uint8_t pressedButtonId) + ButtonEventsSimulator & SetPressedButtonId(uint8_t pressedButtonId) { mPressedButtonId = pressedButtonId; return *this; } - ButtonEventsSimulator& SetMode(Mode mode) + ButtonEventsSimulator & SetMode(Mode mode) { mMode = mode; return *this; } - ButtonEventsSimulator& SetEndpointId(EndpointId endpointId) + ButtonEventsSimulator & SetEndpointId(EndpointId endpointId) { mEndpointId = endpointId; return *this; } - private: +private: enum class State { - kIdle = 0, + kIdle = 0, - kEmitStartOfLongPress = 1, - kEmitLongPress = 2, - kEmitLongRelease = 3, + kEmitStartOfLongPress = 1, + kEmitLongPress = 2, + kEmitLongRelease = 3, - kEmitStartOfMultiPress = 4, - kEmitEndOfMultiPress = 5, + kEmitStartOfMultiPress = 4, + kEmitEndOfMultiPress = 5, }; static void OnTimerDone(System::Layer * layer, void * appState); @@ -130,13 +130,13 @@ class ButtonEventsSimulator { System::Clock::Milliseconds32 mLongPressDurationMillis{}; System::Clock::Milliseconds32 mMultiPressPressedTimeMillis{}; System::Clock::Milliseconds32 mMultiPressReleasedTimeMillis{}; - uint8_t mMultiPressNumPresses{1}; - uint8_t mIdleButtonId{0}; - uint8_t mPressedButtonId{1}; - EndpointId mEndpointId{1}; + uint8_t mMultiPressNumPresses{ 1 }; + uint8_t mIdleButtonId{ 0 }; + uint8_t mPressedButtonId{ 1 }; + EndpointId mEndpointId{ 1 }; - Mode mMode{Mode::kModeLongPress}; - State mState{State::kIdle}; + Mode mMode{ Mode::kModeLongPress }; + State mState{ State::kIdle }; }; } // namespace app From 1a304596398bb4317bcf9f49176cec12077b9b65 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Thu, 18 Jul 2024 21:16:55 +0000 Subject: [PATCH 03/10] Restyled by autopep8 --- src/python_testing/TC_SWTCH.py | 28 +++++++++++++------- src/python_testing/matter_testing_support.py | 22 ++++++++------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/python_testing/TC_SWTCH.py b/src/python_testing/TC_SWTCH.py index fbcdc5ba6250a9..58a60300f21425 100644 --- a/src/python_testing/TC_SWTCH.py +++ b/src/python_testing/TC_SWTCH.py @@ -41,6 +41,7 @@ logger = logging.getLogger(__name__) + class TC_SwitchTests(MatterBaseTest): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -85,9 +86,11 @@ def _ask_for_switch_idle(self): def _ask_for_long_press(self, endpoint_id: int, pressed_position: 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.") + 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} + command_dict = {"Name": "SimulateActionSwitchLongPress", "EndpointId": endpoint_id, + "ButtonId": pressed_position, "LongPressDelayMillis": 5000, "LongPressDurationMillis": 5500} self._send_named_pipe_command(command_dict) def _placeholder_for_step(self, step_id: str): @@ -120,7 +123,8 @@ def _await_sequence_of_reports(self, report_queue: queue.Queue, endpoint_id: int logging.info(f"Got expected attribute change {sequence_idx+1}/{len(sequence)} for attribute {attribute}") sequence_idx += 1 else: - asserts.assert_equal(item.value, expected_value, msg="Did not get expected attribute value in correct sequence.") + asserts.assert_equal(item.value, expected_value, + msg="Did not get expected attribute value in correct sequence.") # We are done waiting when we have accumulated all results. if sequence_idx == len(sequence): @@ -244,14 +248,17 @@ async def test_TC_SWTCH_2_4(self): # Step 4b: TH expects report of CurrentPosition 1, followed by a report of Current Position 0. self._placeholder_for_step("4b") - logging.info(f"Starting to wait for {post_prompt_settle_delay_seconds:.1f} seconds for CurrentPosition to go {switch_pressed_position}, then 0.") - self._await_sequence_of_reports(report_queue=attrib_listener.attribute_queue, endpoint_id=endpoint_id, attribute=cluster.Attributes.CurrentPosition, sequence = [switch_pressed_position, 0], timeout_sec=post_prompt_settle_delay_seconds) + logging.info( + f"Starting to wait for {post_prompt_settle_delay_seconds:.1f} seconds for CurrentPosition to go {switch_pressed_position}, then 0.") + self._await_sequence_of_reports(report_queue=attrib_listener.attribute_queue, endpoint_id=endpoint_id, attribute=cluster.Attributes.CurrentPosition, sequence=[ + switch_pressed_position, 0], timeout_sec=post_prompt_settle_delay_seconds) # Step 4c: TH expects at least InitialPress with NewPosition = 1 self._placeholder_for_step("4c") logging.info(f"Starting to wait for {post_prompt_settle_delay_seconds:.1f} seconds for InitialPress event.") expected_events = [cluster.Events.InitialPress(newPosition=switch_pressed_position)] - self._await_sequence_of_events(event_queue=event_listener.event_queue, endpoint_id=endpoint_id, sequence=expected_events, timeout_sec=post_prompt_settle_delay_seconds) + self._await_sequence_of_events(event_queue=event_listener.event_queue, endpoint_id=endpoint_id, + sequence=expected_events, timeout_sec=post_prompt_settle_delay_seconds) # Step 4d: For MSL/AS, expect to see LongPress/LongRelease in that order if not has_msl_feature and not has_as_feature: @@ -264,18 +271,21 @@ async def test_TC_SWTCH_2_4(self): expected_events = [] expected_events.append(cluster.Events.LongPress(newPosition=switch_pressed_position)) expected_events.append(cluster.Events.LongRelease(previousPosition=switch_pressed_position)) - self._await_sequence_of_events(event_queue=event_listener.event_queue, endpoint_id=endpoint_id, sequence=expected_events, timeout_sec=post_prompt_settle_delay_seconds) + self._await_sequence_of_events(event_queue=event_listener.event_queue, endpoint_id=endpoint_id, + sequence=expected_events, timeout_sec=post_prompt_settle_delay_seconds) # Step 4e: For MS & (!MSL & !AS & !MSR), expect no further events for 10 seconds. if not has_msl_feature and not has_as_feature and not has_msr_feature: self._placeholder_for_step("4e") - self._expect_no_events_for_cluster(event_queue=event_listener.event_queue, endpoint_id=endpoint_id, expected_cluster=cluster, timeout_sec=10.0) + self._expect_no_events_for_cluster(event_queue=event_listener.event_queue, + endpoint_id=endpoint_id, expected_cluster=cluster, timeout_sec=10.0) # Step 4f: For MSR & not MSL, expect to see ShortRelease. if not has_msl_feature and has_msr_feature: self._placeholder_for_step("4f") expected_events = [cluster.Events.ShortRelease(previousPosition=switch_pressed_position)] - self._await_sequence_of_events(event_queue=event_listener.event_queue, endpoint_id=endpoint_id, sequence=expected_events, timeout_sec=post_prompt_settle_delay_seconds) + self._await_sequence_of_events(event_queue=event_listener.event_queue, endpoint_id=endpoint_id, + sequence=expected_events, timeout_sec=post_prompt_settle_delay_seconds) if __name__ == "__main__": diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index d91853304504a7..181d5a0732410e 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -238,11 +238,12 @@ def __init__(self, expected_cluster: ClusterObjects.Cluster): self._q = queue.Queue() self._expected_cluster = expected_cluster - async def start(self, dev_ctrl, node_id: int, endpoint: int, fabric_filtered: bool=False, min_interval_sec: int=0, max_interval_sec: int=30) -> Any: + async def start(self, dev_ctrl, node_id: int, endpoint: int, fabric_filtered: bool = False, min_interval_sec: int = 0, max_interval_sec: int = 30) -> Any: """This starts a subscription for events on the specified node_id and endpoint. The cluster is specified when the class instance is created.""" urgent = True self._subscription = await dev_ctrl.ReadEvent(node_id, - events=[(endpoint, self._expected_cluster, urgent)], reportInterval=(min_interval_sec, max_interval_sec), + events=[(endpoint, self._expected_cluster, urgent)], reportInterval=( + min_interval_sec, max_interval_sec), fabricFiltered=fabric_filtered, keepSubscriptions=True, autoResubscribe=False) self._subscription.SetEventUpdateCallback(self.__call__) return self._subscription @@ -269,7 +270,7 @@ def wait_for_event_report(self, expected_event: ClusterObjects.ClusterEvent, tim @property def event_queue(self) -> queue.Queue: - return self._q + return self._q class AttributeChangeCallback: @@ -304,6 +305,7 @@ def wait_for_report(self): except KeyError: asserts.fail("[AttributeChangeCallback] Attribute {expected_attribute} not found in returned report") + @dataclass class AttributeValue: endpoint_id: int @@ -317,15 +319,15 @@ def __init__(self, expected_cluster: ClusterObjects.Cluster): self._expected_cluster = expected_cluster self._subscription = None - async def start(self, dev_ctrl, node_id: int, endpoint: int, fabric_filtered: bool=False, min_interval_sec: int=0, max_interval_sec: int=30) -> Any: + async def start(self, dev_ctrl, node_id: int, endpoint: int, fabric_filtered: bool = False, min_interval_sec: int = 0, max_interval_sec: int = 30) -> Any: """This starts a subscription for attributes on the specified node_id and endpoint. The cluster is specified when the class instance is created.""" self._subscription = await dev_ctrl.ReadAttribute( - nodeid=node_id, - attributes=[(endpoint, self._expected_cluster)], - reportInterval=(min_interval_sec, max_interval_sec), - fabricFiltered=fabric_filtered, - keepSubscriptions=True - ) + nodeid=node_id, + attributes=[(endpoint, self._expected_cluster)], + reportInterval=(min_interval_sec, max_interval_sec), + fabricFiltered=fabric_filtered, + keepSubscriptions=True + ) self._subscription.SetAttributeUpdateCallback(self.__call__) return self._subscription From 1b610edfe2a0d476fed02f09ac3db0ea89ce9150 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Thu, 18 Jul 2024 21:16:56 +0000 Subject: [PATCH 04/10] Restyled by isort --- src/python_testing/TC_SWTCH.py | 11 ++++++----- src/python_testing/matter_testing_support.py | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/python_testing/TC_SWTCH.py b/src/python_testing/TC_SWTCH.py index 58a60300f21425..d6644988a40d1b 100644 --- a/src/python_testing/TC_SWTCH.py +++ b/src/python_testing/TC_SWTCH.py @@ -26,18 +26,19 @@ # 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 # === END CI TEST ARGUMENTS === -import logging import json +import logging import queue import time - from typing import Any -from mobly import asserts + import chip.clusters as Clusters from chip.clusters import ClusterObjects as ClusterObjects -from chip.clusters.Attribute import TypedAttributePath, EventReadResult +from chip.clusters.Attribute import EventReadResult, TypedAttributePath from chip.clusters.Types import NullValue -from matter_testing_support import EventChangeCallback, ClusterAttributeChangeAccumulator, AttributeValue, MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from matter_testing_support import (AttributeValue, ClusterAttributeChangeAccumulator, EventChangeCallback, MatterBaseTest, + TestStep, async_test_body, default_matter_test_main) +from mobly import asserts logger = logging.getLogger(__name__) diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index 181d5a0732410e..0ae16151ba6006 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -34,7 +34,7 @@ from dataclasses import dataclass, field from datetime import datetime, timedelta, timezone from enum import Enum -from typing import List, Optional, Tuple, Any +from typing import Any, List, Optional, Tuple from chip.tlv import float32, uint From 261e2986447f58aed4fadaf18e76ccabbdb8a997 Mon Sep 17 00:00:00 2001 From: Tennessee Carmel-Veilleux Date: Thu, 18 Jul 2024 17:19:52 -0400 Subject: [PATCH 05/10] Enable TC_SWTCH.py in CI --- .github/workflows/tests.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index aed711e9906d68..9b4bf00e57d1e6 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -576,6 +576,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_RVCOPSTATE_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_RVCOPSTATE_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_RVCOPSTATE_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_SWTCH.py' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --script "src/python_testing/TestConformanceSupport.py" --script-args "--trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --script "src/python_testing/TestMatterTestingSupport.py" --script-args "--trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"' scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --script "src/python_testing/TestSpecParsingSupport.py" --script-args "--trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"' From 7641d71a20eca6d64cd4d74da5f38fe91e2e9363 Mon Sep 17 00:00:00 2001 From: "tennessee.carmelveilleux@gmail.com" Date: Fri, 19 Jul 2024 10:16:19 -0400 Subject: [PATCH 06/10] Fix lint --- .../linux/ButtonEventsSimulator.cpp | 14 +++++++------- src/python_testing/TC_SWTCH.py | 11 ++++------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp b/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp index af18e233c2d55d..44bf5657f5c2f6 100644 --- a/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp +++ b/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp @@ -56,8 +56,8 @@ void EmitInitialPress(EndpointId endpointId, uint8_t newPosition) } else { - ChipLogProgress(NotSpecified, "Logged InitialPress(%" PRIu8 ") with ID %" PRIu64 " on Endpoint %" PRIu16, newPosition, - eventNumber, endpointId); + ChipLogProgress(NotSpecified, "Logged InitialPress(%u) on Endpoint %u", static_cast(newPosition), + static_cast(endpointId)); } } @@ -73,8 +73,8 @@ void EmitLongPress(EndpointId endpointId, uint8_t newPosition) } else { - ChipLogProgress(NotSpecified, "Logged LongPress(%" PRIu8 ") with ID %" PRIu64 " on Endpoint %" PRIu16, newPosition, - eventNumber, endpointId); + ChipLogProgress(NotSpecified, "Logged LongPress(%u) on Endpoint %u", static_cast(newPosition), + static_cast(endpointId)); } } @@ -91,7 +91,7 @@ void EmitLongRelease(EndpointId endpointId, uint8_t previousPosition) } else { - ChipLogProgress(NotSpecified, "Logged LongRelease with ID %" PRIu64 " on Endpoint %" PRIu16, eventNumber, endpointId); + ChipLogProgress(NotSpecified, "Logged LongRelease on Endpoint %u", static_cast(endpointId)); } } @@ -109,8 +109,8 @@ void EmitMultiPressComplete(EndpointId endpointId, uint8_t previousPosition, uin } else { - ChipLogProgress(NotSpecified, "Logged MultiPressComplete(count=%" PRIu8 ") with ID %" PRIu64 " on Endpoint %" PRIu16, count, - eventNumber, endpointId); + ChipLogProgress(NotSpecified, "Logged MultiPressComplete(count=%u) on Endpoint %u", static_cast(count), + static_cast(endpointId)); } } diff --git a/src/python_testing/TC_SWTCH.py b/src/python_testing/TC_SWTCH.py index d6644988a40d1b..7876585e6cd753 100644 --- a/src/python_testing/TC_SWTCH.py +++ b/src/python_testing/TC_SWTCH.py @@ -35,9 +35,8 @@ import chip.clusters as Clusters from chip.clusters import ClusterObjects as ClusterObjects from chip.clusters.Attribute import EventReadResult, TypedAttributePath -from chip.clusters.Types import NullValue from matter_testing_support import (AttributeValue, ClusterAttributeChangeAccumulator, EventChangeCallback, MatterBaseTest, - TestStep, async_test_body, default_matter_test_main) + async_test_body, default_matter_test_main) from mobly import asserts logger = logging.getLogger(__name__) @@ -182,8 +181,6 @@ def _expect_no_events_for_cluster(self, event_queue: queue.Queue, endpoint_id: i elapsed = 0.0 time_remaining = timeout_sec - sequence_idx = 0 - logging.info(f"Waiting {timeout_sec:.1f} seconds for no more events for cluster {expected_cluster} on endpoint {endpoint_id}") while time_remaining > 0: try: @@ -217,7 +214,7 @@ async def test_TC_SWTCH_2_4(self): 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 - has_msm_feature = (feature_map & cluster.Bitmaps.Feature.kMomentarySwitchMultiPress) != 0 + # has_msm_feature = (feature_map & cluster.Bitmaps.Feature.kMomentarySwitchMultiPress) != 0 if not has_ms_feature: logging.info("Skipping rest of test: SWTCH.S.F01(MS) feature not present") @@ -229,8 +226,8 @@ async def test_TC_SWTCH_2_4(self): self._placeholder_for_step("1") event_listener = EventChangeCallback(cluster) attrib_listener = ClusterAttributeChangeAccumulator(cluster) - event_sub = await event_listener.start(self.default_controller, self.dut_node_id, endpoint=endpoint_id) - cluster_sub = await attrib_listener.start(self.default_controller, self.dut_node_id, endpoint=endpoint_id) + await event_listener.start(self.default_controller, self.dut_node_id, endpoint=endpoint_id) + await attrib_listener.start(self.default_controller, self.dut_node_id, endpoint=endpoint_id) # Step 2: Operator does not operate switch on the DUT self._placeholder_for_step("2") From fc4b4f87dc8bec130d20cc0039d45a7dbea84fc2 Mon Sep 17 00:00:00 2001 From: Tennessee Carmel-Veilleux Date: Sat, 20 Jul 2024 13:32:24 -0400 Subject: [PATCH 07/10] Fix typo that arose on file saving --- src/python_testing/matter_testing_support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index 0ae16151ba6006..a6fa4ee632dc9b 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -1067,7 +1067,7 @@ def mark_current_step_skipped(self): logging.info(f'**** Skipping: {num}') self.step_skipped = True - def skip_steps(self, step): + def skip_step(self, step): self.step(step) self.mark_current_step_skipped() From 7abe39e868ccbbdb3a6b2b0beda6c8d5de255d36 Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Wed, 24 Jul 2024 08:37:40 -0400 Subject: [PATCH 08/10] Handle EOF in reading user input in matter testing support --- src/python_testing/matter_testing_support.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index f9bb7c61d60ca0..85d61106592552 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -1156,7 +1156,7 @@ def get_setup_payload_info(self) -> List[SetupPayloadInfo]: def wait_for_user_input(self, prompt_msg: str, prompt_msg_placeholder: str = "Submit anything to continue", - default_value: str = "y") -> str: + default_value: str = "y") -> Optional[str]: """Ask for user input and wait for it. Args: @@ -1165,7 +1165,7 @@ def wait_for_user_input(self, default_value (str, optional): TH UI prompt default value. Defaults to "y". Returns: - str: User input + str: User input or none if input is closed. """ if self.runner_hook: self.runner_hook.show_prompt(msg=prompt_msg, @@ -1173,7 +1173,10 @@ def wait_for_user_input(self, default_value=default_value) logging.info("========= USER PROMPT =========") logging.info(f">>> {prompt_msg.rstrip()} (press enter to confirm)") - return input() + try: + return input() + except EOFError: + return None def generate_mobly_test_config(matter_test_config: MatterTestConfig): From e19846f4154dfe14a0d82674f8c066feb0cf1849 Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Wed, 24 Jul 2024 10:49:25 -0400 Subject: [PATCH 09/10] Do not provide a stdin on python test runner and handle EOF on matter_testing_support --- scripts/tests/run_python_test.py | 6 ++++-- src/python_testing/matter_testing_support.py | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/tests/run_python_test.py b/scripts/tests/run_python_test.py index 7d7500c10f5a11..91ee73e00d0e8d 100755 --- a/scripts/tests/run_python_test.py +++ b/scripts/tests/run_python_test.py @@ -169,7 +169,8 @@ def main_impl(app: str, factoryreset: bool, factoryreset_app_only: bool, app_arg app_args = [app] + shlex.split(app_args) logging.info(f"Execute: {app_args}") app_process = subprocess.Popen( - app_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0) + app_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, bufsize=0) + app_process.stdin.close() app_pid = app_process.pid DumpProgramOutputToQueue( log_cooking_threads, Fore.GREEN + "APP " + Style.RESET_ALL, app_process, stream_output, log_queue) @@ -192,7 +193,8 @@ def main_impl(app: str, factoryreset: bool, factoryreset_app_only: bool, app_arg logging.info(f"Execute: {final_script_command}") test_script_process = subprocess.Popen( - final_script_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + final_script_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + test_script_process.stdin.close() DumpProgramOutputToQueue(log_cooking_threads, Fore.GREEN + "TEST" + Style.RESET_ALL, test_script_process, stream_output, log_queue) diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index 85d61106592552..e3c8683acd52ae 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -1176,6 +1176,7 @@ def wait_for_user_input(self, try: return input() except EOFError: + logging.info("========= EOF on STDIN =========") return None From cea6f6c99d6e0b23a28a0a2f1301bc8b14f3c782 Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Wed, 24 Jul 2024 11:12:36 -0400 Subject: [PATCH 10/10] Fix up PICS execution for CI --- src/python_testing/TC_SWTCH.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python_testing/TC_SWTCH.py b/src/python_testing/TC_SWTCH.py index 7876585e6cd753..867897ec54ec9d 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 +# 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 # === END CI TEST ARGUMENTS === import json