Implement TC-SWTCH-2.4 and all-clusters button simulator (#34406)
* 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
* Restyled by clang-format
* Restyled by autopep8
* Restyled by isort
* Enable TC_SWTCH.py in CI
* Fix lint
* Fix typo that arose on file saving
* Handle EOF in reading user input in matter testing support
* Do not provide a stdin on python test runner and handle EOF on matter_testing_support
* Fix up PICS execution for CI
---------
Co-authored-by: Restyled.io <commits@restyled.io>
Co-authored-by: Andrei Litvin <andreilitvin@google.com>
diff --git a/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp b/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp
new file mode 100644
index 0000000..44bf565
--- /dev/null
+++ b/examples/all-clusters-app/linux/ButtonEventsSimulator.cpp
@@ -0,0 +1,210 @@
+/*
+ *
+ * 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 <functional>
+#include <inttypes.h>
+#include <stdint.h>
+#include <utility>
+
+#include <app-common/zap-generated/attributes/Accessors.h>
+#include <app-common/zap-generated/cluster-objects.h>
+#include <app/EventLogging.h>
+#include <lib/core/CHIPError.h>
+#include <lib/core/DataModelTypes.h>
+#include <lib/support/CodeUtils.h>
+#include <lib/support/logging/CHIPLogging.h>
+#include <platform/CHIPDeviceLayer.h>
+#include <system/SystemClock.h>
+#include <system/SystemLayer.h>
+
+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(%u) on Endpoint %u", static_cast<unsigned>(newPosition),
+ static_cast<unsigned>(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(%u) on Endpoint %u", static_cast<unsigned>(newPosition),
+ static_cast<unsigned>(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 on Endpoint %u", static_cast<unsigned>(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=%u) on Endpoint %u", static_cast<unsigned>(count),
+ static_cast<unsigned>(endpointId));
+ }
+}
+
+} // namespace
+
+void ButtonEventsSimulator::OnTimerDone(System::Layer * layer, void * appState)
+{
+ ButtonEventsSimulator * that = reinterpret_cast<ButtonEventsSimulator *>(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<unsigned>(oldState),
+ static_cast<unsigned>(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