diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index af9ee570102fac..05d2670edd665e 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -526,6 +526,18 @@ jobs: 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"' scripts/run_in_python_env.sh out/venv './scripts/tests/TestTimeSyncTrustedTimeSourceRunner.py' + scripts/run_in_python_env.sh out/venv './scripts/tests/run_python_test.py --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_OPSTATE_1_1.py" --script-args "--endpoint 1 --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json: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 --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_OPSTATE_2_1.py" --script-args "--endpoint 1 --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json: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 --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_OPSTATE_2_2.py" --script-args "--endpoint 1 --int-arg PIXIT.WAITTIME.COUNTDOWN:5 --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json: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 --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_OPSTATE_2_3.py" --script-args "--endpoint 1 --int-arg PIXIT.WAITTIME.COUNTDOWN:5 --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json: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 --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_OPSTATE_2_4.py" --script-args "--endpoint 1 --int-arg PIXIT.OPSTATE.ErrorEventGen:1 --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json: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 --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_OPSTATE_2_5.py" --script-args "--endpoint 1 --int-arg PIXIT.WAITTIME.REBOOT:5 --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json: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 --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_OVENOPSTATE_1_1.py" --script-args "--endpoint 1 --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json: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 --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_OVENOPSTATE_2_1.py" --script-args "--endpoint 1 --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json: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 --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_OVENOPSTATE_2_2.py" --script-args "--endpoint 1 --int-arg PIXIT.WAITTIME.COUNTDOWN:5 --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json: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 --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_OVENOPSTATE_2_3.py" --script-args "--endpoint 1 --int-arg PIXIT.WAITTIME.COUNTDOWN:5 --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json: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 --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_OVENOPSTATE_2_4.py" --script-args "--endpoint 1 --int-arg PIXIT.OVENOPSTATE.ErrorEventGen:1 --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json: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 --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace-to json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_OVENOPSTATE_2_5.py" --script-args "--endpoint 1 --int-arg PIXIT.WAITTIME.REBOOT:5 --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json: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 --app out/linux-x64-microwave-oven-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-microwave-oven-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace_file json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_MWOCTRL_2_2.py" --script-args "--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --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 --app out/linux-x64-microwave-oven-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-microwave-oven-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace_file json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_MWOCTRL_2_3.py" --script-args "--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --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 --app out/linux-x64-microwave-oven-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-microwave-oven-app --factoryreset --app-args "--discriminator 1234 --KVS kvs1 --trace_file json:out/trace_data/app-{SCRIPT_BASE_NAME}.json" --script "src/python_testing/TC_MWOCTRL_2_4.py" --script-args "--storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --trace-to json:out/trace_data/test-{SCRIPT_BASE_NAME}.json --trace-to perfetto:out/trace_data/test-{SCRIPT_BASE_NAME}.perfetto"' diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index 64d3a8f6283dba..fce05e34ff3b36 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -7831,8 +7831,11 @@ endpoint 1 { } server cluster OvenCavityOperationalState { + emits event OperationalError; + emits event OperationCompletion; callback attribute phaseList; callback attribute currentPhase; + callback attribute countdownTime; callback attribute operationalStateList; callback attribute operationalState; callback attribute operationalError; @@ -7842,6 +7845,12 @@ endpoint 1 { callback attribute attributeList; ram attribute featureMap default = 0; ram attribute clusterRevision default = 1; + + handle command Pause; + handle command Stop; + handle command Start; + handle command Resume; + handle command OperationalCommandResponse; } server cluster OvenMode { @@ -8076,7 +8085,7 @@ endpoint 1 { callback attribute acceptedCommandList; callback attribute attributeList; ram attribute featureMap default = 0; - ram attribute clusterRevision default = 1; + ram attribute clusterRevision default = 2; handle command Pause; handle command Stop; diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap index cb3e8ec2930b5e..7525e06449c6dd 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap @@ -17,6 +17,12 @@ } ], "package": [ + { + "pathRelativity": "relativeToZap", + "path": "../../../src/app/zap-templates/app-templates.json", + "type": "gen-templates-json", + "version": "chip-v1" + }, { "pathRelativity": "relativeToZap", "path": "../../../src/app/zap-templates/zcl/zcl-with-test-extensions.json", @@ -24,12 +30,6 @@ "category": "matter", "version": 1, "description": "Matter SDK ZCL data with some extensions" - }, - { - "pathRelativity": "relativeToZap", - "path": "../../../src/app/zap-templates/app-templates.json", - "type": "gen-templates-json", - "version": "chip-v1" } ], "endpointTypes": [ @@ -8227,6 +8227,48 @@ "side": "server", "enabled": 1, "apiMaturity": "provisional", + "commands": [ + { + "name": "Pause", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "Stop", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "Start", + "code": 2, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "Resume", + "code": 3, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "OperationalCommandResponse", + "code": 4, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + } + ], "attributes": [ { "name": "PhaseList", @@ -8260,6 +8302,22 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "CountdownTime", + "code": 2, + "mfgCode": null, + "side": "server", + "type": "elapsed_s", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "OperationalStateList", "code": 3, @@ -8404,6 +8462,22 @@ "maxInterval": 65534, "reportableChange": 0 } + ], + "events": [ + { + "name": "OperationalError", + "code": 0, + "mfgCode": null, + "side": "server", + "included": 1 + }, + { + "name": "OperationCompletion", + "code": 1, + "mfgCode": null, + "side": "server", + "included": 1 + } ] }, { @@ -11175,7 +11249,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "1", + "defaultValue": "2", "reportable": 1, "minInterval": 1, "maxInterval": 65534, diff --git a/examples/all-clusters-app/all-clusters-common/include/operational-state-delegate-impl.h b/examples/all-clusters-app/all-clusters-common/include/operational-state-delegate-impl.h index 5c6f70bd28a50c..60b6b09e9b6511 100644 --- a/examples/all-clusters-app/all-clusters-common/include/operational-state-delegate-impl.h +++ b/examples/all-clusters-app/all-clusters-common/include/operational-state-delegate-impl.h @@ -33,11 +33,15 @@ namespace OperationalState { class GenericOperationalStateDelegateImpl : public Delegate { public: + uint32_t mRunningTime = 0; + uint32_t mPausedTime = 0; + app::DataModel::Nullable mCountDownTime; + /** * Get the countdown time. This attribute is not used in this application. * @return The current countdown time. */ - app::DataModel::Nullable GetCountdownTime() override { return {}; }; + app::DataModel::Nullable GetCountdownTime() override; /** * Fills in the provided GenericOperationalState with the state at index `index` if there is one, @@ -104,11 +108,33 @@ class OperationalStateDelegate : public GenericOperationalStateDelegateImpl GenericOperationalState(to_underlying(OperationalStateEnum::kError)), }; + const uint32_t kExampleCountDown = 30; + public: OperationalStateDelegate() { GenericOperationalStateDelegateImpl::mOperationalStateList = Span(opStateList); } + + /** + * Handle Command Callback in application: Start + * @param[out] get operational error after callback. + */ + void HandleStartStateCallback(GenericOperationalError & err) override + { + mCountDownTime.SetNonNull(static_cast(kExampleCountDown)); + GenericOperationalStateDelegateImpl::HandleStartStateCallback(err); + } + + /** + * Handle Command Callback in application: Stop + * @param[out] get operational error after callback. + */ + void HandleStopStateCallback(GenericOperationalError & err) override + { + GenericOperationalStateDelegateImpl::HandleStopStateCallback(err); + mCountDownTime.SetNull(); + } }; Instance * GetOperationalStateInstance(); diff --git a/examples/all-clusters-app/all-clusters-common/include/oven-operational-state-delegate.h b/examples/all-clusters-app/all-clusters-common/include/oven-operational-state-delegate.h index f68eec20692d0b..89f9969ffb86a7 100644 --- a/examples/all-clusters-app/all-clusters-common/include/oven-operational-state-delegate.h +++ b/examples/all-clusters-app/all-clusters-common/include/oven-operational-state-delegate.h @@ -33,13 +33,17 @@ namespace OvenCavityOperationalState { class OvenCavityOperationalStateDelegate : public OperationalState::Delegate { private: - inline static const Clusters::OperationalState::GenericOperationalState mOperationalStateList[] = { + inline static const Clusters::OperationalState::GenericOperationalState opStateList[] = { OperationalState::GenericOperationalState(to_underlying(OperationalState::OperationalStateEnum::kStopped)), OperationalState::GenericOperationalState(to_underlying(OperationalState::OperationalStateEnum::kRunning)), OperationalState::GenericOperationalState(to_underlying(OperationalState::OperationalStateEnum::kPaused)), OperationalState::GenericOperationalState(to_underlying(OperationalState::OperationalStateEnum::kError)) }; + Span mOperationalStateList = + Span(opStateList); + Span mOperationalPhaseList; + public: /** * Get the countdown time. This attribute is not supported in our example app. @@ -73,43 +77,29 @@ class OvenCavityOperationalStateDelegate : public OperationalState::Delegate * Handle Command Callback in application: Pause * @param[out] get operational error after callback. */ - void HandlePauseStateCallback(Clusters::OperationalState::GenericOperationalError & err) override - { - // This command in not supported. - err.Set(to_underlying(ErrorStateEnum::kCommandInvalidInState)); - }; + void HandlePauseStateCallback(Clusters::OperationalState::GenericOperationalError & err) override; /** * Handle Command Callback in application: Resume * @param[out] get operational error after callback. */ - void HandleResumeStateCallback(Clusters::OperationalState::GenericOperationalError & err) override - { - // This command in not supported. - err.Set(to_underlying(ErrorStateEnum::kCommandInvalidInState)); - }; + void HandleResumeStateCallback(Clusters::OperationalState::GenericOperationalError & err) override; /** * Handle Command Callback in application: Start * @param[out] get operational error after callback. */ - void HandleStartStateCallback(Clusters::OperationalState::GenericOperationalError & err) override - { - // This command in not supported. - err.Set(to_underlying(ErrorStateEnum::kCommandInvalidInState)); - }; + void HandleStartStateCallback(Clusters::OperationalState::GenericOperationalError & err) override; /** * Handle Command Callback in application: Stop * @param[out] get operational error after callback. */ - void HandleStopStateCallback(Clusters::OperationalState::GenericOperationalError & err) override - { - // This command in not supported. - err.Set(to_underlying(ErrorStateEnum::kCommandInvalidInState)); - }; + void HandleStopStateCallback(Clusters::OperationalState::GenericOperationalError & err) override; }; +Instance * GetOperationalStateInstance(); + void Shutdown(); } // namespace OvenCavityOperationalState diff --git a/examples/all-clusters-app/all-clusters-common/src/operational-state-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/operational-state-delegate-impl.cpp index 3370d75bd65f35..2070e1b86754fe 100644 --- a/examples/all-clusters-app/all-clusters-common/src/operational-state-delegate-impl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/operational-state-delegate-impl.cpp @@ -16,6 +16,7 @@ * limitations under the License. */ #include +#include using namespace chip; using namespace chip::app; @@ -23,6 +24,16 @@ using namespace chip::app::Clusters; using namespace chip::app::Clusters::OperationalState; using namespace chip::app::Clusters::RvcOperationalState; +static void onOperationalStateTimerTick(System::Layer * systemLayer, void * data); + +DataModel::Nullable GenericOperationalStateDelegateImpl::GetCountdownTime() +{ + if (mCountDownTime.IsNull()) + return DataModel::NullNullable; + + return DataModel::MakeNullable((uint32_t) (mCountDownTime.Value() - mRunningTime)); +} + CHIP_ERROR GenericOperationalStateDelegateImpl::GetOperationalStateAtIndex(size_t index, GenericOperationalState & operationalState) { if (index >= mOperationalStateList.size()) @@ -44,6 +55,15 @@ CHIP_ERROR GenericOperationalStateDelegateImpl::GetOperationalPhaseAtIndex(size_ void GenericOperationalStateDelegateImpl::HandlePauseStateCallback(GenericOperationalError & err) { + OperationalState::OperationalStateEnum state = + static_cast(GetInstance()->GetCurrentOperationalState()); + + if (state == OperationalState::OperationalStateEnum::kStopped || state == OperationalState::OperationalStateEnum::kError) + { + err.Set(to_underlying(OperationalState::ErrorStateEnum::kCommandInvalidInState)); + return; + } + // placeholder implementation auto error = GetInstance()->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kPaused)); if (error == CHIP_NO_ERROR) @@ -58,6 +78,15 @@ void GenericOperationalStateDelegateImpl::HandlePauseStateCallback(GenericOperat void GenericOperationalStateDelegateImpl::HandleResumeStateCallback(GenericOperationalError & err) { + OperationalState::OperationalStateEnum state = + static_cast(GetInstance()->GetCurrentOperationalState()); + + if (state == OperationalState::OperationalStateEnum::kStopped || state == OperationalState::OperationalStateEnum::kError) + { + err.Set(to_underlying(OperationalState::ErrorStateEnum::kCommandInvalidInState)); + return; + } + // placeholder implementation auto error = GetInstance()->SetOperationalState(to_underlying(OperationalStateEnum::kRunning)); if (error == CHIP_NO_ERROR) @@ -72,10 +101,20 @@ void GenericOperationalStateDelegateImpl::HandleResumeStateCallback(GenericOpera void GenericOperationalStateDelegateImpl::HandleStartStateCallback(GenericOperationalError & err) { + OperationalState::GenericOperationalError current_err(to_underlying(OperationalState::ErrorStateEnum::kNoError)); + GetInstance()->GetCurrentOperationalError(current_err); + + if (current_err.errorStateID != to_underlying(OperationalState::ErrorStateEnum::kNoError)) + { + err.Set(to_underlying(OperationalState::ErrorStateEnum::kUnableToStartOrResume)); + return; + } + // placeholder implementation auto error = GetInstance()->SetOperationalState(to_underlying(OperationalStateEnum::kRunning)); if (error == CHIP_NO_ERROR) { + (void) DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds16(1), onOperationalStateTimerTick, this); err.Set(to_underlying(ErrorStateEnum::kNoError)); } else @@ -90,6 +129,18 @@ void GenericOperationalStateDelegateImpl::HandleStopStateCallback(GenericOperati auto error = GetInstance()->SetOperationalState(to_underlying(OperationalStateEnum::kStopped)); if (error == CHIP_NO_ERROR) { + (void) DeviceLayer::SystemLayer().CancelTimer(onOperationalStateTimerTick, this); + + OperationalState::GenericOperationalError current_err(to_underlying(OperationalState::ErrorStateEnum::kNoError)); + GetInstance()->GetCurrentOperationalError(current_err); + + Optional> totalTime((DataModel::Nullable(mRunningTime + mPausedTime))); + Optional> pausedTime((DataModel::Nullable(mPausedTime))); + + GetInstance()->OnOperationCompletionDetected(static_cast(current_err.errorStateID), totalTime, pausedTime); + + mRunningTime = 0; + mPausedTime = 0; err.Set(to_underlying(ErrorStateEnum::kNoError)); } else @@ -98,6 +149,38 @@ void GenericOperationalStateDelegateImpl::HandleStopStateCallback(GenericOperati } } +static void onOperationalStateTimerTick(System::Layer * systemLayer, void * data) +{ + GenericOperationalStateDelegateImpl * delegate = reinterpret_cast(data); + + OperationalState::Instance * instance = OperationalState::GetOperationalStateInstance(); + OperationalState::OperationalStateEnum state = + static_cast(instance->GetCurrentOperationalState()); + + auto countdown_time = delegate->GetCountdownTime(); + + if (countdown_time.IsNull() || (!countdown_time.IsNull() && countdown_time.Value() > 0)) + { + if (state == OperationalState::OperationalStateEnum::kRunning) + { + delegate->mRunningTime++; + } + else if (state == OperationalState::OperationalStateEnum::kPaused) + { + delegate->mPausedTime++; + } + } + + if (state == OperationalState::OperationalStateEnum::kRunning || state == OperationalState::OperationalStateEnum::kPaused) + { + (void) DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds16(1), onOperationalStateTimerTick, delegate); + } + else + { + (void) DeviceLayer::SystemLayer().CancelTimer(onOperationalStateTimerTick, delegate); + } +} + // Init Operational State cluster static OperationalState::Instance * gOperationalStateInstance = nullptr; diff --git a/examples/all-clusters-app/all-clusters-common/src/oven-operational-state-delegate.cpp b/examples/all-clusters-app/all-clusters-common/src/oven-operational-state-delegate.cpp index ecae5c88006ef4..726f326afbb6f8 100644 --- a/examples/all-clusters-app/all-clusters-common/src/oven-operational-state-delegate.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/oven-operational-state-delegate.cpp @@ -22,8 +22,13 @@ using namespace chip::app; using namespace chip::app::Clusters; using namespace chip::app::Clusters::OvenCavityOperationalState; -static OperationalState::Instance * gOvenCavityOperationalStateInstance = nullptr; -static OvenCavityOperationalStateDelegate * gOvenCavityOperationalStateDelegate = nullptr; +static OvenCavityOperationalState::Instance * gOvenCavityOperationalStateInstance = nullptr; +static OvenCavityOperationalStateDelegate * gOvenCavityOperationalStateDelegate = nullptr; + +OvenCavityOperationalState::Instance * OvenCavityOperationalState::GetOperationalStateInstance() +{ + return gOvenCavityOperationalStateInstance; +} void OvenCavityOperationalState::Shutdown() { @@ -58,7 +63,7 @@ CHIP_ERROR OvenCavityOperationalStateDelegate::GetOperationalStateAtIndex(size_t index, OperationalState::GenericOperationalState & operationalState) { - if (index >= ArraySize(mOperationalStateList)) + if (index >= mOperationalStateList.size()) { return CHIP_ERROR_NOT_FOUND; } @@ -69,5 +74,89 @@ OvenCavityOperationalStateDelegate::GetOperationalStateAtIndex(size_t index, CHIP_ERROR OvenCavityOperationalStateDelegate::GetOperationalPhaseAtIndex(size_t index, MutableCharSpan & operationalPhase) { - return CHIP_ERROR_NOT_FOUND; + if (index >= mOperationalPhaseList.size()) + { + return CHIP_ERROR_NOT_FOUND; + } + return CopyCharSpanToMutableCharSpan(mOperationalPhaseList[index], operationalPhase); +} + +void OvenCavityOperationalStateDelegate::HandlePauseStateCallback(OperationalState::GenericOperationalError & err) +{ + OperationalState::OperationalStateEnum state = + static_cast(GetInstance()->GetCurrentOperationalState()); + + if (state == OperationalState::OperationalStateEnum::kStopped || state == OperationalState::OperationalStateEnum::kError) + { + err.Set(to_underlying(OperationalState::ErrorStateEnum::kCommandInvalidInState)); + return; + } + + auto error = GetInstance()->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kPaused)); + if (error == CHIP_NO_ERROR) + { + err.Set(to_underlying(OperationalState::ErrorStateEnum::kNoError)); + } + else + { + err.Set(to_underlying(OperationalState::ErrorStateEnum::kUnableToCompleteOperation)); + } +} + +void OvenCavityOperationalStateDelegate::HandleResumeStateCallback(OperationalState::GenericOperationalError & err) +{ + + OperationalState::OperationalStateEnum state = + static_cast(GetInstance()->GetCurrentOperationalState()); + + if (state == OperationalState::OperationalStateEnum::kStopped || state == OperationalState::OperationalStateEnum::kError) + { + err.Set(to_underlying(OperationalState::ErrorStateEnum::kCommandInvalidInState)); + return; + } + + auto error = GetInstance()->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kRunning)); + if (error == CHIP_NO_ERROR) + { + err.Set(to_underlying(OperationalState::ErrorStateEnum::kNoError)); + } + else + { + err.Set(to_underlying(OperationalState::ErrorStateEnum::kUnableToCompleteOperation)); + } +} + +void OvenCavityOperationalStateDelegate::HandleStartStateCallback(OperationalState::GenericOperationalError & err) +{ + OperationalState::GenericOperationalError current_err(to_underlying(OperationalState::ErrorStateEnum::kNoError)); + GetInstance()->GetCurrentOperationalError(current_err); + + if (current_err.errorStateID != to_underlying(OperationalState::ErrorStateEnum::kNoError)) + { + err.Set(to_underlying(OperationalState::ErrorStateEnum::kUnableToStartOrResume)); + return; + } + + auto error = GetInstance()->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kRunning)); + if (error == CHIP_NO_ERROR) + { + err.Set(to_underlying(OperationalState::ErrorStateEnum::kNoError)); + } + else + { + err.Set(to_underlying(OperationalState::ErrorStateEnum::kUnableToCompleteOperation)); + } +} + +void OvenCavityOperationalStateDelegate::HandleStopStateCallback(OperationalState::GenericOperationalError & err) +{ + auto error = GetInstance()->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kStopped)); + if (error == CHIP_NO_ERROR) + { + err.Set(to_underlying(OperationalState::ErrorStateEnum::kNoError)); + } + else + { + err.Set(to_underlying(OperationalState::ErrorStateEnum::kUnableToCompleteOperation)); + } } diff --git a/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp b/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp index 3145bd605b518c..b2fe3a90b59f3f 100644 --- a/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp +++ b/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp @@ -30,7 +30,9 @@ #include #include #include +#include #include +#include #include using namespace chip; @@ -179,6 +181,12 @@ void AllClustersAppCommandHandler::HandleCommand(intptr_t context) self->OnAirQualityChange(static_cast(jsonAirQualityEnum.asUInt())); } } + else if (name == "OperationalStateChange") + { + std::string device = self->mJsonValue["Device"].asString(); + std::string operation = self->mJsonValue["Operation"].asString(); + self->OnOperationalStateChange(device, operation, self->mJsonValue["Param"]); + } else { ChipLogError(NotSpecified, "Unhandled command: Should never happens"); @@ -436,6 +444,54 @@ void AllClustersAppCommandHandler::OnModeChangeHandler(std::string device, std:: } } +void AllClustersAppCommandHandler::OnOperationalStateChange(std::string device, std::string operation, Json::Value param) +{ + OperationalState::Instance * operationalStateInstance = nullptr; + if (device == "Generic") + { + operationalStateInstance = OperationalState::GetOperationalStateInstance(); + } + else if (device == "Oven") + { + operationalStateInstance = OvenCavityOperationalState::GetOperationalStateInstance(); + } + else + { + ChipLogDetail(NotSpecified, "Invalid device type : %s", device.c_str()); + return; + } + + if (operation == "Start" || operation == "Resume") + { + operationalStateInstance->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kRunning)); + } + else if (operation == "Pause") + { + operationalStateInstance->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kPaused)); + } + else if (operation == "Stop") + { + operationalStateInstance->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kStopped)); + } + else if (operation == "OnFault") + { + + uint8_t event_id = to_underlying(OperationalState::ErrorStateEnum::kUnableToCompleteOperation); + if (!param.isNull()) + { + event_id = to_underlying(static_cast(param.asUInt())); + } + + OperationalState::GenericOperationalError err(event_id); + operationalStateInstance->OnOperationalErrorDetected(err); + } + else + { + ChipLogDetail(NotSpecified, "Invalid operation : %s", operation.c_str()); + return; + } +} + void AllClustersAppCommandHandler::OnAirQualityChange(uint32_t aNewValue) { AirQuality::Instance * airQualityInstance = AirQuality::GetInstance(); diff --git a/examples/all-clusters-app/linux/AllClustersCommandDelegate.h b/examples/all-clusters-app/linux/AllClustersCommandDelegate.h index 7998cd15581a74..4022b06e9f8565 100644 --- a/examples/all-clusters-app/linux/AllClustersCommandDelegate.h +++ b/examples/all-clusters-app/linux/AllClustersCommandDelegate.h @@ -98,6 +98,11 @@ class AllClustersAppCommandHandler * Should be called when it is necessary to change the air quality attribute. */ void OnAirQualityChange(uint32_t aEnum); + + /** + * Should be called when it is necessary to change the operational state as a manual operation. + */ + void OnOperationalStateChange(std::string device, std::string operation, Json::Value param); }; class AllClustersCommandDelegate : public NamedPipeCommandDelegate diff --git a/scripts/tests/chiptest/__init__.py b/scripts/tests/chiptest/__init__.py index 984cb38080c897..fd4ad6f52867b5 100644 --- a/scripts/tests/chiptest/__init__.py +++ b/scripts/tests/chiptest/__init__.py @@ -209,7 +209,6 @@ def _GetDarwinFrameworkToolUnsupportedTests() -> Set[str]: "Test_TC_GRPKEY_2_1", # darwin-framework-tool does not support writing readonly attributes by name "Test_TC_LCFG_2_1", # darwin-framework-tool does not support writing readonly attributes by name "Test_TC_OPCREDS_3_7", # darwin-framework-tool does not support the GetCommissionerRootCertificate command. - "Test_TC_OPSTATE_2_4", # darwin-framework-tool does not currently support reading or subscribing to Events "Test_TC_SMOKECO_2_2", # darwin-framework-tool does not currently support reading or subscribing to Events "Test_TC_SMOKECO_2_3", # darwin-framework-tool does not currently support reading or subscribing to Events "Test_TC_SMOKECO_2_4", # darwin-framework-tool does not currently support reading or subscribing to Events diff --git a/scripts/tests/run_python_test.py b/scripts/tests/run_python_test.py index fdea7767037aa6..1a9b54a3ad6736 100755 --- a/scripts/tests/run_python_test.py +++ b/scripts/tests/run_python_test.py @@ -125,6 +125,8 @@ def main(app: str, factoryreset: bool, factoryreset_app_only: bool, app_args: st log_cooking_threads = [] app_process = None + app_pid = 0 + if app: if not os.path.exists(app): if app is None: @@ -133,11 +135,12 @@ def main(app: str, factoryreset: bool, factoryreset_app_only: bool, app_args: st logging.info(f"Execute: {app_args}") app_process = subprocess.Popen( app_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0) + app_pid = app_process.pid DumpProgramOutputToQueue( log_cooking_threads, Fore.GREEN + "APP " + Style.RESET_ALL, app_process, log_queue) script_command = [script, "--paa-trust-store-path", os.path.join(DEFAULT_CHIP_ROOT, MATTER_DEVELOPMENT_PAA_ROOT_CERTS), - '--log-format', '%(message)s'] + shlex.split(script_args) + '--log-format', '%(message)s', "--app-pid", str(app_pid)] + shlex.split(script_args) if script_gdb: # diff --git a/src/app/tests/suites/certification/PICS.yaml b/src/app/tests/suites/certification/PICS.yaml index d8bc862ba6c62c..407851320ca31c 100644 --- a/src/app/tests/suites/certification/PICS.yaml +++ b/src/app/tests/suites/certification/PICS.yaml @@ -8858,6 +8858,94 @@ PICS: command?" id: OPSTATE.S.C04.Tx + # + #Oven Operational State + # + - label: + "Does the device implement the Operational State cluster as a server?" + id: OVENOPSTATE.S + + - label: + "Does the device implement the Operational State cluster as a client?" + id: OVENOPSTATE.C + + #ManuallyControlled + + - label: "Does the DUT support testing the Stopped(0x00) operational state" + id: OVENOPSTATE.S.M.ST_STOPPED + + - label: "Does the DUT support testing the Running(0x01) operational state" + id: OVENOPSTATE.S.M.ST_RUNNING + + - label: "Does the DUT support testing the Paused(0x02) operational state" + id: OVENOPSTATE.S.M.ST_PAUSED + + - label: "Does the DUT support testing the Error(0x03) operational state?" + id: OVENOPSTATE.S.M.ST_ERROR + + - label: "Does the DUT support testing the NoError(0x00) error state?" + id: OVENOPSTATE.S.M.ERR_NO_ERROR + + - label: + "Does the DUT support testing the UnableToStartOrResume(0x01) error + state?" + id: OVENOPSTATE.S.M.ERR_UNABLE_TO_START_OR_RESUME + + - label: + "Does the DUT support testing the UnableToCompleteOperation(0x02) + error state?" + id: OVENOPSTATE.S.M.ERR_UNABLE_TO_COMPLETE_OPERATION + + - label: + "Does the DUT support testing the CommandInvalidInState(0x03) error + state?" + id: OVENOPSTATE.S.M.ERR_COMMAND_INVALID_IN_STATE + + #Server Attributes + - label: "Does the device implement the PhaseList attribute?" + id: OVENOPSTATE.S.A0000 + + - label: "Does the device implement the CurrentPhase attribute?" + id: OVENOPSTATE.S.A0001 + + - label: "Does the device implement the CountdownTime attribute?" + id: OVENOPSTATE.S.A0002 + + - label: "Does the device implement the OperationalStateList attribute?" + id: OVENOPSTATE.S.A0003 + + - label: "Does the device implement the OperationalState attribute?" + id: OVENOPSTATE.S.A0004 + + - label: "Does the device implement the OperationalError attribute?" + id: OVENOPSTATE.S.A0005 + #Events Generated + + - label: "Does the device generate the OperationalError event?" + id: OVENOPSTATE.S.E00 + + - label: "Does the device generate the OperationCompltion event?" + id: OVENOPSTATE.S.E01 + + #Server Commands Received + - label: "Does the device implement receiving the Pause command?" + id: OVENOPSTATE.S.C00.Rsp + + - label: "Does the device implement receiving the Stop command?" + id: OVENOPSTATE.S.C01.Rsp + + - label: "Does the device implement receiving the Start command?" + id: OVENOPSTATE.S.C02.Rsp + + - label: "Does the device implement receiving the Resume command?" + id: OVENOPSTATE.S.C03.Rsp + + #Commands generated + - label: + "Does the device implement generating the OperationalCommandResponse + command?" + id: OVENOPSTATE.S.C04.Tx + # Smoke CO Alarm Cluster Test Plan - label: "Does the device implement the SMOKECO cluster as a server?" id: SMOKECO.S diff --git a/src/app/tests/suites/certification/Test_TC_OPSTATE_1_1.yaml b/src/app/tests/suites/certification/Test_TC_OPSTATE_1_1.yaml deleted file mode 100644 index 21d6c7e9785e07..00000000000000 --- a/src/app/tests/suites/certification/Test_TC_OPSTATE_1_1.yaml +++ /dev/null @@ -1,162 +0,0 @@ -# Copyright (c) 2023 Project CHIP Authors -# -# 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. - -name: 201.1.1. [TC-OPSTATE-1.1] Global Attributes with DUT as Server - -PICS: - - OPSTATE.S - -config: - nodeId: 0x12344321 - cluster: "Operational State" - endpoint: 1 - -tests: - - label: - "Step 1: Commission DUT to TH (can be skipped if done in a preceding - test)" - cluster: "DelayCommands" - command: "WaitForCommissionee" - arguments: - values: - - name: "nodeId" - value: nodeId - - - label: "Step 2: TH reads from the DUT the ClusterRevision attribute" - command: "readAttribute" - attribute: "ClusterRevision" - response: - value: 1 - constraints: - type: int16u - - - label: "Step 3: TH reads from the DUT the FeatureMap attribute" - command: "readAttribute" - attribute: "FeatureMap" - response: - value: 0 - constraints: - type: bitmap32 - - - label: "Step 4a: TH reads from the DUT the AttributeList attribute" - PICS: PICS_EVENT_LIST_ENABLED - command: "readAttribute" - attribute: "AttributeList" - response: - constraints: - type: list - contains: - [0, 1, 3, 4, 5, 65528, 65529, 65530, 65531, 65532, 65533] - - - label: "Step 4a: TH reads from the DUT the AttributeList attribute" - PICS: "!PICS_EVENT_LIST_ENABLED" - command: "readAttribute" - attribute: "AttributeList" - response: - constraints: - type: list - contains: [0, 1, 3, 4, 5, 65528, 65529, 65531, 65532, 65533] - - - label: - "Step 4b: TH reads from the DUT the optional attribute(CountdownTime) - in the AttributeList from the DUT" - PICS: OPSTATE.S.A0002 - command: "readAttribute" - attribute: "AttributeList" - response: - constraints: - type: list - contains: [2] - - - label: "Step 5a: TH reads from the DUT the EventList attribute" - PICS: PICS_EVENT_LIST_ENABLED - command: "readAttribute" - attribute: "EventList" - response: - constraints: - type: list - contains: [0x00] - - - label: - "Step 5b: TH reads from the DUT the optional - event(OperationCompletion) in EventList." - PICS: PICS_EVENT_LIST_ENABLED && OPSTATE.S.E01 - command: "readAttribute" - attribute: "EventList" - response: - constraints: - type: list - contains: [0x01] - - - label: - "Step 6a: TH reads the optional command(Start) in AcceptedCommandList" - PICS: OPSTATE.S.C02.Rsp - command: "readAttribute" - attribute: "AcceptedCommandList" - response: - constraints: - type: list - contains: [1, 2] - - - label: - "Step 6b: TH reads the optional command(Stop) in AcceptedCommandList" - PICS: OPSTATE.S.C01.Rsp - command: "readAttribute" - attribute: "AcceptedCommandList" - response: - constraints: - type: list - contains: [1] - - - label: - "Step 6c: TH reads the optional command(Pause) in AcceptedCommandList" - PICS: OPSTATE.S.C00.Rsp - command: "readAttribute" - attribute: "AcceptedCommandList" - response: - constraints: - type: list - contains: [0, 3] - - - label: - "Step 6d: TH reads the optional command(Resume) in AcceptedCommandList" - PICS: OPSTATE.S.C03.Rsp - command: "readAttribute" - attribute: "AcceptedCommandList" - response: - constraints: - type: list - contains: [0, 3] - - - label: "Step 7: TH reads from the DUT the AcceptedCommandList attribute" - PICS: - "!OPSTATE.S.C00.Rsp && !OPSTATE.S.C01.Rsp && !OPSTATE.S.C02.Rsp && - !OPSTATE.S.C03.Rsp" - command: "readAttribute" - attribute: "GeneratedCommandList" - response: - value: [] - constraints: - type: list - - - label: "Step 7: TH reads from the DUT the GeneratedCommandList attribute" - PICS: - (OPSTATE.S.C00.Rsp || OPSTATE.S.C01.Rsp || OPSTATE.S.C02.Rsp || - OPSTATE.S.C03.Rsp) - command: "readAttribute" - attribute: "GeneratedCommandList" - response: - constraints: - type: list - contains: [4] diff --git a/src/app/tests/suites/certification/Test_TC_OPSTATE_2_2.yaml b/src/app/tests/suites/certification/Test_TC_OPSTATE_2_2.yaml deleted file mode 100644 index 43dc0dda3dc01e..00000000000000 --- a/src/app/tests/suites/certification/Test_TC_OPSTATE_2_2.yaml +++ /dev/null @@ -1,335 +0,0 @@ -# Copyright (c) 2023 Project CHIP Authors -# -# 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. -# Auto-generated scripts for harness use only, please review before automation. The endpoints and cluster names are currently set to default - -name: 202.2.2. [TC-OPSTATE-2.2] Start and Stop commands with DUT as Server - -PICS: - - OPSTATE.S - -config: - nodeId: 0x12344321 - cluster: "Basic Information" - endpoint: 0 - -tests: - - label: "Note" - verification: | - This is a simulated example log for instructional purposes only. In real scenarios, the actual log may vary depending on the feature implementation in Reference App. - disabled: true - - - label: - "Step 1: Commission DUT to TH (can be skipped if done in a preceding - test)" - verification: | - - disabled: true - - - label: - "Step 2: Manually put the DUT into a state wherein it can receive a - Start Command" - verification: | - Manually put the DUT into a state wherein it can receive a Start Command - disabled: true - - - label: "Step 3: TH reads from the DUT the OperationalStateList attribute" - PICS: OPSTATE.S.A0003 - verification: | - ./chip-tool operationalstate read operational-state-list 1 1 - - Via the TH (chip-tool), verify: - - all entries include an ID (enum8) and a label (string) - - all provided IDs are in the allowed range - - the list includes IDs for Error (0x03), Running (0x01), and Stopped (0x00) - - [1689674049.504261][17222:17224] CHIP:DMG: } - [1689674049.504390][17222:17224] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Attribute 0x0000_0003 DataVersion: 2102885775 - [1689674049.504440][17222:17224] CHIP:TOO: OperationalStateList: 4 entries - [1689674049.504462][17222:17224] CHIP:TOO: [1]: { - [1689674049.504469][17222:17224] CHIP:TOO: OperationalStateID: 0 - [1689674049.504476][17222:17224] CHIP:TOO: } - [1689674049.504484][17222:17224] CHIP:TOO: [2]: { - [1689674049.504490][17222:17224] CHIP:TOO: OperationalStateID: 1 - [1689674049.504495][17222:17224] CHIP:TOO: } - [1689674049.504503][17222:17224] CHIP:TOO: [3]: { - [1689674049.504508][17222:17224] CHIP:TOO: OperationalStateID: 2 - [1689674049.504514][17222:17224] CHIP:TOO: } - [1689674049.504521][17222:17224] CHIP:TOO: [4]: { - [1689674049.504527][17222:17224] CHIP:TOO: OperationalStateID: 3 - [1689674049.504533][17222:17224] CHIP:TOO: } - [1689674049.504605][17222:17224] CHIP:EM: <<< [E:22830i S:37151 M:4250114 (Ack:140781365)] (S) Msg TX to 1:0000000000000001 [BFDE] --- Type 0000:10 (SecureChannel:StandaloneAck) - [1689674049.504620][17222:17224] CHIP:IN: (S) Sending msg 4250114 on secure session with LSID: 37151 - disabled: true - - - label: "Step 4: TH sends Start command to the DUT" - PICS: OPSTATE.S.C02.Rsp && OPSTATE.S.C04.Tx - verification: | - ./chip-tool operationalstate start 1 1 - - Via the TH (chip-tool), verify: - - the response is an instance of OperationalCommandResponse - - The ErrorStateID field is set to 0x00 (NoError) - - [1689674139.018639][17233:17235] CHIP:DMG: Received Command Response Data, Endpoint=1 Cluster=0x0000_0060 Command=0x0000_0004 - [1689674139.018658][17233:17235] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Command 0x0000_0004 - [1689674139.018704][17233:17235] CHIP:TOO: OperationalCommandResponse: { - [1689674139.018712][17233:17235] CHIP:TOO: commandResponseState: { - [1689674139.018719][17233:17235] CHIP:TOO: ErrorStateID: 0 - [1689674139.018726][17233:17235] CHIP:TOO: } - [1689674139.018732][17233:17235] CHIP:TOO: } - [1689674139.018755][17233:17235] CHIP:DMG: ICR moving to [AwaitingDe] - [1689674139.018818][17233:17235] CHIP:EM: <<< [E:26021i S:33879 M:235550100 (Ack:58905970)] (S) Msg TX to 1:0000000000000001 [BFDE] --- Type 0000:10 (SecureChannel:StandaloneAck) - [1689674139.018837][17233:17235] CHIP:IN: (S) Sending msg 235550100 on secure session with LSID: 33879 - [1689674139.018885][17233:17235] CHIP:EM: Flushed pending ack for MessageCounter:58905970 on exchange 26021i - disabled: true - - - label: "Step 5: TH reads from the DUT the OperationalState attribute" - PICS: OPSTATE.S.A0004 - verification: | - ./chip-tool operationalstate read operational-state 1 1 - - Via the TH (chip-tool), verify: - - the response has an OperationalStateID field that is set to 0x01 (Running) - - [1689674196.878722][17249:17251] CHIP:DMG: InteractionModelRevision = 1 - [1689674196.878727][17249:17251] CHIP:DMG: } - [1689674196.878800][17249:17251] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Attribute 0x0000_0004 DataVersion: 2102885775 - [1689674196.878834][17249:17251] CHIP:TOO: OperationalState: { - [1689674196.878841][17249:17251] CHIP:TOO: OperationalStateID: 1 - [1689674196.878847][17249:17251] CHIP:TOO: } - [1689674196.878914][17249:17251] CHIP:EM: <<< [E:56939i S:28614 M:63040141 (Ack:57012545)] (S) Msg TX to 1:0000000000000001 [BFDE] --- Type 0000:10 (SecureChannel:StandaloneAck) - [1689674196.878928][17249:17251] CHIP:IN: (S) Sending msg 63040141 on secure session with LSID: 28614 - disabled: true - - - label: "Step 6: TH reads from the DUT the OperationalError attribute" - PICS: OPSTATE.S.A0005 - verification: | - ./chip-tool operationalstate read operational-error 1 1 - - Via the TH (chip-tool), verify: - - the response contains the ErrorStateId set to NoError(0x00) - - [1689674342.832448][17274:17276] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Attribute 0x0000_0005 DataVersion: 2102885775 - [1689674342.832482][17274:17276] CHIP:TOO: OperationalError: { - [1689674342.832500][17274:17276] CHIP:TOO: ErrorStateID: 0 - [1689674342.832509][17274:17276] CHIP:TOO: } - [1689674342.832570][17274:17276] CHIP:EM: <<< [E:37158i S:10451 M:72875113 (Ack:195983315)] (S) Msg TX to 1:0000000000000001 [BFDE] --- Type 0000:10 (SecureChannel:StandaloneAck) - [1689674342.832585][17274:17276] CHIP:IN: (S) Sending msg 72875113 on secure session with LSID: 10451 - [1689674342.832614][17274:17276] CHIP:EM: Flushed pending ack for MessageCounter:195983315 on exchange 37158i - disabled: true - - - label: "Step 7: TH reads from the DUT the CountdownTime attribute" - PICS: OPSTATE.S.A0002 - verification: | - ./chip-tool operationalstate read countdown-time 1 1 - - Via the TH (chip-tool), verify: - - that CountdownTime attribute contains either null our a uint32 value - - if non-null, verify that the value is in the range 1 to 259200 - - store the value in 'initialcountdown-time' - - [1689674384.271623][17278:17280] CHIP:DMG: InteractionModelRevision = 1 - [1689674384.271625][17278:17280] CHIP:DMG: } - [1689674384.271649][17278:17280] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Attribute 0x0000_0002 DataVersion: 2102885775 - [1689674384.271662][17278:17280] CHIP:TOO: CountdownTime: null - [1689674384.271683][17278:17280] CHIP:EM: <<< [E:24665i S:47371 M:757241 (Ack:152992659)] (S) Msg TX to 1:0000000000000001 [BFDE] --- Type 0000:10 (SecureChannel:StandaloneAck) - [1689674384.271687][17278:17280] CHIP:IN: (S) Sending msg 757241 on secure session with LSID: 47371 - [1689674384.271696][17278:17280] CHIP:EM: Flushed pending ack for MessageCounter:152992659 on exchange 24665i - disabled: true - - - label: "Step 8: TH reads from the DUT the PhaseList attribute" - PICS: OPSTATE.S.A0000 - verification: | - ./chip-tool operationalstate read phase-list 1 1 - - Via the TH (chip-tool), verify: - - that PhaseList attribute value contains either null or a list of strings. - - If not null, receord the number of entries in the list as 'phase-list-size'; execute step 7. - If null, go to step 8. - - [1689674447.761859][17290:17292] CHIP:DMG: InteractionModelRevision = 1 - [1689674447.761865][17290:17292] CHIP:DMG: } - [1689674447.761938][17290:17292] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Attribute 0x0000_0000 DataVersion: 2102885775 - [1689674447.761972][17290:17292] CHIP:TOO: PhaseList: null - [1689674447.762041][17290:17292] CHIP:EM: <<< [E:58737i S:13847 M:251354926 (Ack:137738036)] (S) Msg TX to 1:0000000000000001 [BFDE] --- Type 0000:10 (SecureChannel:StandaloneAck) - [1689674447.762055][17290:17292] CHIP:IN: (S) Sending msg 251354926 on secure session with LSID: 13847 - [1689674447.762109][17290:17292] CHIP:EM: Flushed pending ack for MessageCounter:137738036 on exchange 58737i - disabled: true - - - label: "Step 9: TH reads from the DUT the CurrentPhase attribute" - PICS: OPSTATE.S.A0001 - verification: | - ./chip-tool operationalstate read current-phase 1 1 - - Via the TH (chip-tool), verify: - - that the CurrentPhase attribute value contains contains a uint8 value - - that the value is between 0 and 'phase-list-size - 1'. - - [1689674497.950563][17299:17301] CHIP:DMG: } - [1689674497.950635][17299:17301] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Attribute 0x0000_0001 DataVersion: 2102885775 - [1689674497.950664][17299:17301] CHIP:TOO: CurrentPhase: null - [1689674497.950737][17299:17301] CHIP:EM: <<< [E:64019i S:52010 M:245677798 (Ack:138696372)] (S) Msg TX to 1:0000000000000001 [BFDE] --- Type 0000:10 (SecureChannel:StandaloneAck) - [1689674497.950752][17299:17301] CHIP:IN: (S) Sending msg 245677798 on secure session with LSID: 52010 - [1689674497.950798][17299:17301] CHIP:EM: Flushed pending ack for MessageCounter:138696372 on exchange 64019i - [1689674497.950899][17299:17299] CHIP:CTL: Shutting down the commissioner - disabled: true - - - label: - "Step 10: TH waits for a vendor defined wait time, this being a period - of time less than the expected duration of the operation that has been - started" - verification: | - TH waits for a vendor defined wait time, this being a period of time less than the expected duration of the operation that has been started - disabled: true - - - label: "Step 11: TH reads from the DUT the CountdownTime attribute" - PICS: OPSTATE.S.A0002 - verification: | - ./chip-tool operationalstate read countdown-time 1 1 - - - Via the TH (chip-tool), verify: - - that CountdownTime attribute contains either null our a uint32 value - - if non-null, verify that the value is in the range 1 to 259200 - - that the value is approximately 'initialcountdown-time minus the vendor defined wait time' - - - [1689674623.673661][17320:17322] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Attribute 0x0000_0002 DataVersion: 2102885775 - [1689674623.673697][17320:17322] CHIP:TOO: CountdownTime: null - [1689674623.673755][17320:17322] CHIP:EM: <<< [E:42152i S:37580 M:19654175 (Ack:176515710)] (S) Msg TX to 1:0000000000000001 [BFDE] --- Type 0000:10 (SecureChannel:StandaloneAck) - [1689674623.673768][17320:17322] CHIP:IN: (S) Sending msg 19654175 on secure session with LSID: 37580 - [1689674623.673795][17320:17322] CHIP:EM: Flushed pending ack for MessageCounter:176515710 on exchange 42152i - disabled: true - - - label: "Step 12: TH sends Start command to the DUT" - PICS: OPSTATE.S.C02.Rsp && OPSTATE.S.C04.Tx - verification: | - ./chip-tool operationalstate start 1 1 - - Via the TH (chip-tool), verify: - - the response is an instance of OperationalCommandResponse - - The ErrorStateID field is set to 0x00 (NoError) - - [1689674637.555734][17326:17328] CHIP:DMG: - [1689674637.555742][17326:17328] CHIP:DMG: InteractionModelRevision = 1 - [1689674637.555751][17326:17328] CHIP:DMG: }, - [1689674637.555784][17326:17328] CHIP:DMG: Received Command Response Data, Endpoint=1 Cluster=0x0000_0060 Command=0x0000_0004 - [1689674637.555805][17326:17328] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Command 0x0000_0004 - [1689674637.555853][17326:17328] CHIP:TOO: OperationalCommandResponse: { - [1689674637.555862][17326:17328] CHIP:TOO: commandResponseState: { - [1689674637.555872][17326:17328] CHIP:TOO: ErrorStateID: 0 - [1689674637.555883][17326:17328] CHIP:TOO: } - [1689674637.555891][17326:17328] CHIP:TOO: } - [1689674637.555913][17326:17328] CHIP:DMG: ICR moving to [AwaitingDe] - [1689674637.555956][17326:17328] CHIP:EM: <<< [E:28742i S:49023 M:139320570 (Ack:91983883)] (S) Msg TX to 1:0000000000000001 [BFDE] --- Type 0000:10 (SecureChannel:StandaloneAck) - [1689674637.555971][17326:17328] CHIP:IN: (S) Sending msg 139320570 on secure session with LSID: 49023 - [1689674637.556001][17326:17328] CHIP:EM: Flushed pending ack for MessageCounter:91983883 on exchange 28742i - disabled: true - - - label: "Step 13: TH sends Stop command to the DUT" - PICS: OPSTATE.S.C01.Rsp && OPSTATE.S.C04.Tx - verification: | - ./chip-tool operationalstate stop 1 1 - - Via the TH (chip-tool), verify: - - the response is an instance of OperationalCommandResponse - - The ErrorStateID field is set to 0x00 (NoError) - - [1689674653.322963][17330:17332] CHIP:DMG: }, - [1689674653.322994][17330:17332] CHIP:DMG: Received Command Response Data, Endpoint=1 Cluster=0x0000_0060 Command=0x0000_0004 - [1689674653.323014][17330:17332] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Command 0x0000_0004 - [1689674653.323058][17330:17332] CHIP:TOO: OperationalCommandResponse: { - [1689674653.323066][17330:17332] CHIP:TOO: commandResponseState: { - [1689674653.323076][17330:17332] CHIP:TOO: ErrorStateID: 0 - [1689674653.323085][17330:17332] CHIP:TOO: } - [1689674653.323094][17330:17332] CHIP:TOO: } - [1689674653.323113][17330:17332] CHIP:DMG: ICR moving to [AwaitingDe] - [1689674653.323154][17330:17332] CHIP:EM: <<< [E:62878i S:64455 M:173921517 (Ack:216732582)] (S) Msg TX to 1:0000000000000001 [BFDE] --- Type 0000:10 (SecureChannel:StandaloneAck) - [1689674653.323168][17330:17332] CHIP:IN: (S) Sending msg 173921517 on secure session with LSID: 64455 - [1689674653.323195][17330:17332] CHIP:EM: Flushed pending ack for MessageCounter:216732582 on exchange 62878i - [1689674653.323284][17330:17330] CHIP:CTL: Shutting down the commissioner - disabled: true - - - label: "Step 14: TH reads from the DUT the OperationalState attribute" - PICS: OPSTATE.S.A0004 - verification: | - ./chip-tool operationalstate read operational-state 1 1 - - Via the TH (chip-tool), verify: - - the response has an OperationalStateID field that is set to 0x00 (Stopped) - - [1689674675.459656][17333:17335] CHIP:DMG: } - [1689674675.459738][17333:17335] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Attribute 0x0000_0004 DataVersion: 2102885775 - [1689674675.459772][17333:17335] CHIP:TOO: OperationalState: { - [1689674675.459790][17333:17335] CHIP:TOO: OperationalStateID: 0 - [1689674675.459799][17333:17335] CHIP:TOO: } - [1689674675.459869][17333:17335] CHIP:EM: <<< [E:17771i S:16165 M:1572532 (Ack:102448631)] (S) Msg TX to 1:0000000000000001 [BFDE] --- Type 0000:10 (SecureChannel:StandaloneAck) - [1689674675.459886][17333:17335] CHIP:IN: (S) Sending msg 1572532 on secure session with LSID: 16165 - [1689674675.459930][17333:17335] CHIP:EM: Flushed pending ack for MessageCounter:102448631 on exchange 17771i - disabled: true - - - label: "Step 15: TH sends Stop command to the DUT" - PICS: OPSTATE.S.C01.Rsp && OPSTATE.S.C04.Tx - verification: | - ./chip-tool operationalstate stop 1 1 - - Via the TH (chip-tool), verify: - - the response is an instance of OperationalCommandResponse - - The ErrorStateID field is set to 0x00 (NoError) - - [1689674689.588712][17337:17339] CHIP:DMG: Received Command Response Data, Endpoint=1 Cluster=0x0000_0060 Command=0x0000_0004 - [1689674689.588722][17337:17339] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Command 0x0000_0004 - [1689674689.588745][17337:17339] CHIP:TOO: OperationalCommandResponse: { - [1689674689.588749][17337:17339] CHIP:TOO: commandResponseState: { - [1689674689.588757][17337:17339] CHIP:TOO: ErrorStateID: 0 - [1689674689.588762][17337:17339] CHIP:TOO: } - [1689674689.588765][17337:17339] CHIP:TOO: } - [1689674689.588775][17337:17339] CHIP:DMG: ICR moving to [AwaitingDe] - [1689674689.588802][17337:17339] CHIP:EM: <<< [E:63921i S:35027 M:16881995 (Ack:220265764)] (S) Msg TX to 1:0000000000000001 [BFDE] --- Type 0000:10 (SecureChannel:StandaloneAck) - [1689674689.588810][17337:17339] CHIP:IN: (S) Sending msg 16881995 on secure session with LSID: 35027 - disabled: true - - - label: - "Step 16: Manually put the DUT into a state wherein it cannot receive - a Start Command" - PICS: OPSTATE.S.M.ERR_UNABLE_TO_START_OR_RESUME - verification: | - Manually put the DUT into a state wherein it cannot receive a Start Command - disabled: true - - - label: "Step 17: TH sends Start command to the DUT" - PICS: - OPSTATE.S.M.ERR_UNABLE_TO_START_OR_RESUME && OPSTATE.S.C02.Rsp && - OPSTATE.S.C04.Tx - verification: | - ./chip-tool operationalstate start 1 1 - - Via the TH (chip-tool), verify: - - the response is an instance of OperationalCommandResponse - - The ErrorStateID field is set to 0x01 (UnableToStartOrResume) - - [1689674700.385183][17340:17342] CHIP:DMG: }, - [1689674700.385214][17340:17342] CHIP:DMG: Received Command Response Data, Endpoint=1 Cluster=0x0000_0060 Command=0x0000_0004 - [1689674700.385233][17340:17342] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Command 0x0000_0004 - [1689674700.385266][17340:17342] CHIP:TOO: OperationalCommandResponse: { - [1689674700.385274][17340:17342] CHIP:TOO: commandResponseState: { - [1689674700.385281][17340:17342] CHIP:TOO: ErrorStateID: 1 - [1689674700.385289][17340:17342] CHIP:TOO: } - [1689674700.385295][17340:17342] CHIP:TOO: } - [1689674700.385311][17340:17342] CHIP:DMG: ICR moving to [AwaitingDe] - [1689674700.385361][17340:17342] CHIP:EM: <<< [E:55029i S:46795 M:80501191 (Ack:176711722)] (S) Msg TX to 1:0000000000000001 [BFDE] --- Type 0000:10 (SecureChannel:StandaloneAck) - [1689674700.385375][17340:17342] CHIP:IN: (S) Sending msg 80501191 on secure session with LSID: 46795 - [1689674700.385419][17340:17342] CHIP:EM: Flushed pending ack for MessageCounter:176711722 on exchange 55029i - disabled: true diff --git a/src/app/tests/suites/certification/Test_TC_OPSTATE_2_4.yaml b/src/app/tests/suites/certification/Test_TC_OPSTATE_2_4.yaml deleted file mode 100644 index f8d07da34446a9..00000000000000 --- a/src/app/tests/suites/certification/Test_TC_OPSTATE_2_4.yaml +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2023 Project CHIP Authors -# -# 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. - -name: 202.2.4. [TC-OPSTATE-2.4] Mandatory Events with DUT as Server - -PICS: - - OPSTATE.S - -config: - nodeId: 0x12344321 - cluster: "Operational State" - endpoint: 1 - -tests: - - label: "Step 1: Commission DUT to TH" - cluster: "DelayCommands" - command: "WaitForCommissionee" - arguments: - values: - - name: "nodeId" - value: nodeId - - - label: "Step 2: Set up a subscription to the OperationalError event" - PICS: OPSTATE.S.E00 - command: "subscribeEvent" - event: "OperationalError" - minInterval: 10 - maxInterval: 5000 - - - label: - "Step 3: At the DUT take the vendor defined action to generate an - OperationalError event" - PICS: OPSTATE.S.E00 && PICS_SKIP_SAMPLE_APP - verification: | - After the subscription interval via the TH (chip-tool) verify: - - reception of an OperationalError Event - - ErrorStateID is a defined error - - If the ErrorStateID is manufacturer specific, inclusion of an ErrorStateLabel, not present otherwise - - Optionally an ErrorStateDetails - - NOTE : There's a PIXIT value controlling the ability to execute that ({PIXIT_ERROREVENTGEN}), which as the test case notes, requires vendor specific actions to generated the error at the DUT. This is dependent on the vendor app supporting this. The SDK as it stands won't provide the error, this may be testable by some of the vendors depending on their implementations. - - - [1692859386.707176][6658:6660] CHIP:EM: Rxd Ack; Removing MessageCounter:212756536 from Retrans Table on exchange 36888i - [1692859386.707295][6658:6660] CHIP:DMG: ReportDataMessage = - [1692859386.707360][6658:6660] CHIP:DMG: { - [1692859386.707416][6658:6660] CHIP:DMG: SubscriptionId = 0x97bb846c, - [1692859386.707477][6658:6660] CHIP:DMG: InteractionModelRevision = 10 - [1692859386.707533][6658:6660] CHIP:DMG: } - [1692859386.707621][6658:6660] CHIP:DMG: MoveToState ReadClient[0xffff80011a90]: Moving to [AwaitingSu] - [1692859386.707815][6658:6660] CHIP:EM: <<< [E:36888i S:54523 M:212756537 (Ack:12760634)] (S) Msg TX to 1:0000000000000001 [853D] --- Type 0001:01 (IM:StatusResponse) - [1692859386.707901][6658:6660] CHIP:IN: (S) Sending msg 212756537 on secure session with LSID: 54523 - [1692859386.708167][6658:6660] CHIP:DL: HandlePlatformSpecificBLEEvent 32793 - [1692859386.708237][6658:6660] CHIP:DL: HandlePlatformSpecificBLEEvent 32793 - [1692859386.749217][6658:6660] CHIP:EM: >>> [E:36888i S:54523 M:12760635 (Ack:212756537)] (S) Msg RX from 1:0000000000000001 [853D] --- Type 0001:04 (IM:SubscribeResponse) - [1692859386.749285][6658:6660] CHIP:EM: Found matching exchange: 36888i, Delegate: 0xffff80011aa0 - [1692859386.749350][6658:6660] CHIP:EM: Rxd Ack; Removing MessageCounter:212756537 from Retrans Table on exchange 36888i - [1692859386.749417][6658:6660] CHIP:DMG: SubscribeResponse is received - [1692859386.749489][6658:6660] CHIP:DMG: SubscribeResponseMessage = - [1692859386.749538][6658:6660] CHIP:DMG: { - [1692859386.749584][6658:6660] CHIP:DMG: SubscriptionId = 0x97bb846c, - [1692859386.749637][6658:6660] CHIP:DMG: MaxInterval = 0x6, - [1692859386.749687][6658:6660] CHIP:DMG: InteractionModelRevision = 10 - [1692859386.749734][6658:6660] CHIP:DMG: } - [1692859386.749787][6658:6660] CHIP:DMG: Subscription established with SubscriptionID = 0x97bb846c MinInterval = 5s MaxInterval = 6s Peer = 01:0000000000000001 - [1692859386.749843][6658:6660] CHIP:DMG: MoveToState ReadClient[0xffff80011a90]: Moving to [Subscripti] - cluster: "LogCommands" - command: "UserPrompt" - arguments: - values: - - name: "message" - value: "Enter 'y' after success" - - name: "expectedValue" - value: "y" - - - label: "Step 4: TH reads from the DUT the OperationalState attribute" - PICS: OPSTATE.S.A0004 && PICS_SKIP_SAMPLE_APP - command: "readAttribute" - attribute: "OperationalState" - response: - value: 0x03 - constraints: - type: enum8 diff --git a/src/app/tests/suites/certification/Test_TC_OPSTATE_2_5.yaml b/src/app/tests/suites/certification/Test_TC_OPSTATE_2_5.yaml deleted file mode 100644 index 6dcc447fbf3dd1..00000000000000 --- a/src/app/tests/suites/certification/Test_TC_OPSTATE_2_5.yaml +++ /dev/null @@ -1,353 +0,0 @@ -# Copyright (c) 2023 Project CHIP Authors -# -# 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. -# Auto-generated scripts for harness use only, please review before automation. The endpoints and cluster names are currently set to default - -name: 204.2.5. [TC-OPSTATE-2.5] Optional Events with DUT as Server - -PICS: - - OPSTATE.S - -config: - nodeId: 0x12344321 - cluster: "Basic Information" - endpoint: 0 - -tests: - - label: "Note" - verification: | - This test case can be continuted only when countdown-time read in step 5 is not null - disabled: true - - - label: "Step 1: Commission DUT to TH" - verification: | - - disabled: true - - - label: "Step 2: Set up a subscription to the OperationCompletion event" - PICS: OPSTATE.S.E01 - verification: | - ./chip-tool interactive start - - operationalstate read-event operation-completion 1 1 - - [1702376916.193452][4893:4896] CHIP:DMG: ReportDataMessage = - [1702376916.193461][4893:4896] CHIP:DMG: { - [1702376916.193469][4893:4896] CHIP:DMG: SubscriptionId = 0x85d7a607, - [1702376916.193497][4893:4896] CHIP:DMG: InteractionModelRevision = 11 - [1702376916.193504][4893:4896] CHIP:DMG: } - disabled: true - - - label: "Step 3: TH reads from the DUT the OperationalState attribute" - PICS: OPSTATE.S.A0004 - verification: | - ./chip-tool operationalstate read operational-state 1 1 - - Via the TH (chip-tool), verify: - - the response includes an ID (enum8) amd a label (string) - - the provided ID is found in the set provided in step 4 - - the provided ID is in the allowed range - - [1689673213.434610][16591:16593] CHIP:DMG: } - [1689673213.434686][16591:16593] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Attribute 0x0000_0004 DataVersion: 2102885775 - [1689673213.434721][16591:16593] CHIP:TOO: OperationalState: { - [1689673213.434728][16591:16593] CHIP:TOO: OperationalStateID: 0 - [1689673213.434735][16591:16593] CHIP:TOO: } - disabled: true - - - label: - "Step 4: Manually put the DUT into a state wherein it can receive a - Start Command" - verification: | - - disabled: true - - - label: "Step 5: TH reads from the DUT the CountdownTime attribute" - PICS: OPSTATE.S.A0002 - verification: | - ./chip-tool operationalstate read countdown-time 1 1 - - Via the TH (chip-tool), verify: - - that CountdownTime attribute contains either null our a uint32 value - - if non-null, verify that the value is in the range 1 to 259200 - - store the value in 'initialcountdown-time' - - [1690457637.895405][18808:18810] CHIP:DMG: } - [1690457637.895474][18808:18810] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Attribute 0x0000_0002 DataVersion: 4112784416 - [1690457637.895503][18808:18810] CHIP:TOO: CountdownTime: null - disabled: true - - - label: "Step 6: TH sends Start command to the DUT" - PICS: OPSTATE.S.C02.Rsp && OPSTATE.S.C04.Tx - verification: | - ./chip-tool operationalstate start 1 1 - - Via the TH (chip-tool), verify: - - the response is an instance of OperationalCommandResponse - - The ErrorStateID field is set to 0x00 (NoError) - - [1689674637.555734][17326:17328] CHIP:DMG: - [1689674637.555742][17326:17328] CHIP:DMG: InteractionModelRevision = 1 - [1689674637.555751][17326:17328] CHIP:DMG: }, - [1689674637.555784][17326:17328] CHIP:DMG: Received Command Response Data, Endpoint=1 Cluster=0x0000_0060 Command=0x0000_0004 - [1689674637.555805][17326:17328] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Command 0x0000_0004 - [1689674637.555853][17326:17328] CHIP:TOO: OperationalCommandResponse: { - [1689674637.555862][17326:17328] CHIP:TOO: commandResponseState: { - [1689674637.555872][17326:17328] CHIP:TOO: ErrorStateID: 0 - [1689674637.555883][17326:17328] CHIP:TOO: } - disabled: true - - - label: "Step 7: TH waits for {PIXIT.WAITTIME}" - verification: | - - disabled: true - - - label: "Step 8: TH reads from the DUT the OperationalState attribute" - PICS: OPSTATE.S.A0004 - verification: | - ./chip-tool operationalstate read operational-state 1 1 - - Via the TH (chip-tool), verify: - - the response has an OperationalStateID field that is set to 0x01 (Running) - - [1689674196.878722][17249:17251] CHIP:DMG: InteractionModelRevision = 1 - [1689674196.878727][17249:17251] CHIP:DMG: } - [1689674196.878800][17249:17251] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Attribute 0x0000_0004 DataVersion: 2102885775 - [1689674196.878834][17249:17251] CHIP:TOO: OperationalState: { - [1689674196.878841][17249:17251] CHIP:TOO: OperationalStateID: 1 - [1689674196.878847][17249:17251] CHIP:TOO: } - disabled: true - - - label: "Step 9: TH waits for initial-countdown-time" - verification: | - As the initial-countdown-time in null this test cannot be executed further in case of RPI platform. - Note: The below log are for instructional purpose. In real scenarios, the actual log may vary depending on the feature implementation in the Reference App. - disabled: true - - - label: "Step 10: TH sends Stop command to the DUT" - PICS: OPSTATE.S.C01.Rsp && OPSTATE.S.C04.Tx - verification: | - ./chip-tool operationalstate stop 1 1 - - Via the TH (chip-tool), verify: - - the response is an instance of OperationalCommandResponse - - The ErrorStateID field is set to 0x00 (NoError) - - [1689674653.322963][17330:17332] CHIP:DMG: }, - [1689674653.322994][17330:17332] CHIP:DMG: Received Command Response Data, Endpoint=1 Cluster=0x0000_0060 Command=0x0000_0004 - [1689674653.323014][17330:17332] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Command 0x0000_0004 - [1689674653.323058][17330:17332] CHIP:TOO: OperationalCommandResponse: { - [1689674653.323066][17330:17332] CHIP:TOO: commandResponseState: { - [1689674653.323076][17330:17332] CHIP:TOO: ErrorStateID: 0 - [1689674653.323085][17330:17332] CHIP:TOO: } - [1689674653.323094][17330:17332] CHIP:TOO: } - disabled: true - - - label: - "Step 11: erify TH receives an OperationCompletion event from the DUT - with the following fields populated as follows: 1)CompletionErrorCode - set to NoError(0x00) 2)TotalOperationalTime is approximately - initial-countdown-time or null 3)PausedTime is zero" - PICS: OPSTATE.S.E01 - verification: | - operationalstate read-event operation-completion 1 1 - - [1657193007.841358][5422:5427] CHIP:TOO: Endpoint: 0 Endpoint: 1 Cluster: 0x0000_0060 Event 0x0000_0001 - [1657193007.841387][5422:5427] CHIP:TOO: Event number: 1 - [1657193007.841409][5422:5427] CHIP:TOO: Priority: INFO - [1657193007.841431][5422:5427] CHIP:TOO: Timestamp: 132146 - [1657193007.841531][5422:5427] CHIP:TOO: OperationCompletion: { - [1657193007.841570][5422:5427] CHIP:TOO: CompletionErrorCode: 0x00 - TotalOperationalTime: null - PausedTime: 0 - [1657193007.841594][5422:5427] CHIP:TOO: } - disabled: true - - - label: "Step 12: TH reads from the DUT the OperationalState attribute" - PICS: OPSTATE.S.A0004 - verification: | - ./chip-tool operationalstate read operational-state 1 1 - - Via the TH (chip-tool), verify: - - the response has an OperationalStateID field that is set to 0x00 (Stopped) - - [1689674675.459656][17333:17335] CHIP:DMG: } - [1689674675.459738][17333:17335] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Attribute 0x0000_0004 DataVersion: 2102885775 - [1689674675.459772][17333:17335] CHIP:TOO: OperationalState: { - [1689674675.459790][17333:17335] CHIP:TOO: OperationalStateID: 0 - [1689674675.459799][17333:17335] CHIP:TOO: } - disabled: true - - - label: "Step 13: Restart DUT and repeat step 5" - verification: | - - disabled: true - - - label: "Step 14: TH sends Start command to the DUT" - PICS: OPSTATE.S.C02.Rsp && OPSTATE.S.C04.Tx - verification: | - ./chip-tool operationalstate start 1 1 - - Via the TH (chip-tool), verify: - - the response is an instance of OperationalCommandResponse - - The ErrorStateID field is set to 0x00 (NoError) - - [1689674637.555734][17326:17328] CHIP:DMG: - [1689674637.555742][17326:17328] CHIP:DMG: InteractionModelRevision = 1 - [1689674637.555751][17326:17328] CHIP:DMG: }, - [1689674637.555784][17326:17328] CHIP:DMG: Received Command Response Data, Endpoint=1 Cluster=0x0000_0060 Command=0x0000_0004 - [1689674637.555805][17326:17328] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Command 0x0000_0004 - [1689674637.555853][17326:17328] CHIP:TOO: OperationalCommandResponse: { - [1689674637.555862][17326:17328] CHIP:TOO: commandResponseState: { - [1689674637.555872][17326:17328] CHIP:TOO: ErrorStateID: 0 - [1689674637.555883][17326:17328] CHIP:TOO: } - [1689674637.555891][17326:17328] CHIP:TOO: } - disabled: true - - - label: "Step 15: TH waits for {PIXIT.WAITTIME}" - verification: | - - disabled: true - - - label: "Step 16: TH reads from the DUT the OperationalState attribute" - PICS: OPSTATE.S.A0004 - verification: | - ./chip-tool operationalstate read operational-state 1 1 - - Via the TH (chip-tool), verify: - - the response has an OperationalStateID field that is set to 0x01 (Running) - - [1689674196.878722][17249:17251] CHIP:DMG: InteractionModelRevision = 1 - [1689674196.878727][17249:17251] CHIP:DMG: } - [1689674196.878800][17249:17251] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Attribute 0x0000_0004 DataVersion: 2102885775 - [1689674196.878834][17249:17251] CHIP:TOO: OperationalState: { - [1689674196.878841][17249:17251] CHIP:TOO: OperationalStateID: 1 - [1689674196.878847][17249:17251] CHIP:TOO: } - disabled: true - - - label: "Step 17: TH sends Pause command to the DUT" - PICS: OPSTATE.S.C00.Rsp && OPSTATE.S.C04.Tx - verification: | - ./chip-tool operationalstate pause 1 1 - - Via the TH (chip-tool), verify: - - the response is an instance of OperationalCommandResponse - - The ErrorStateID field is set to 0x00 (NoError) - - [1690457565.893634][18795:18797] CHIP:DMG: }, - [1690457565.893663][18795:18797] CHIP:DMG: Received Command Response Data, Endpoint=1 Cluster=0x0000_0060 Command=0x0000_0004 - [1690457565.893681][18795:18797] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Command 0x0000_0004 - [1690457565.893714][18795:18797] CHIP:TOO: OperationalCommandResponse: { - [1690457565.893729][18795:18797] CHIP:TOO: commandResponseState: { - [1690457565.893736][18795:18797] CHIP:TOO: ErrorStateID: 0 - [1690457565.893744][18795:18797] CHIP:TOO: } - [1690457565.893750][18795:18797] CHIP:TOO: } - disabled: true - - - label: "Step 18: TH reads from the DUT the OperationalState attribute" - PICS: OPSTATE.S.A0004 - verification: | - ./chip-tool operationalstate read operational-state 1 1 - - Via the TH (chip-tool), verify: - - the response has an OperationalStateID field that is set to 0x02 (Paused) - - [1690457601.103082][18800:18802] CHIP:DMG: InteractionModelRevision = 1 - [1690457601.103090][18800:18802] CHIP:DMG: } - [1690457601.103210][18800:18802] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Attribute 0x0000_0004 DataVersion: 4112784416 - [1690457601.103259][18800:18802] CHIP:TOO: OperationalState: 2 - disabled: true - - - label: "Step 19: TH waits for half of initial-countdown-time" - verification: | - - disabled: true - - - label: "Step 20: TH sends Resume command to the DUT" - PICS: OPSTATE.S.C03.Rsp && OPSTATE.S.C04.Tx - verification: | - ./chip-tool operationalstate resume 1 1 - - Via the TH (chip-tool), verify: - - the response is an instance of OperationalCommandResponse - - The ErrorStateID field is set to 0x03 (CommandInvalidInState) - - [1690457852.049135][18877:18879] CHIP:DMG: }, - [1690457852.049164][18877:18879] CHIP:DMG: Received Command Response Data, Endpoint=1 Cluster=0x0000_0060 Command=0x0000_0004 - [1690457852.049183][18877:18879] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Command 0x0000_0004 - [1690457852.049217][18877:18879] CHIP:TOO: OperationalCommandResponse: { - [1690457852.049224][18877:18879] CHIP:TOO: commandResponseState: { - [1690457852.049238][18877:18879] CHIP:TOO: ErrorStateID: 3 - [1690457852.049246][18877:18879] CHIP:TOO: } - [1690457852.049252][18877:18879] CHIP:TOO: } - disabled: true - - - label: "Step 21: TH reads from the DUT the OperationalState attribute" - PICS: OPSTATE.S.A0004 - verification: | - ./chip-tool operationalstate read operational-state 1 1 - - Via the TH (chip-tool), verify: - - the response has an OperationalStateID field that is set to 0x01 (Running) - - [1689674196.878722][17249:17251] CHIP:DMG: InteractionModelRevision = 1 - [1689674196.878727][17249:17251] CHIP:DMG: } - [1689674196.878800][17249:17251] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Attribute 0x0000_0004 DataVersion: 2102885775 - [1689674196.878834][17249:17251] CHIP:TOO: OperationalState: { - [1689674196.878841][17249:17251] CHIP:TOO: OperationalStateID: 1 - [1689674196.878847][17249:17251] CHIP:TOO: } - disabled: true - - - label: "Step 22: TH waits for initial-countdown-time" - verification: | - TH waits for initial-countdown-time - disabled: true - - - label: "Step 23: TH sends Stop command to the DUT" - PICS: OPSTATE.S.C01.Rsp && OPSTATE.S.C04.Tx - verification: | - ./chip-tool operationalstate stop 1 1 - - Via the TH (chip-tool), verify: - - the response is an instance of OperationalCommandResponse - - The ErrorStateID field is set to 0x00 (NoError) - - [1689674653.322963][17330:17332] CHIP:DMG: }, - [1689674653.322994][17330:17332] CHIP:DMG: Received Command Response Data, Endpoint=1 Cluster=0x0000_0060 Command=0x0000_0004 - [1689674653.323014][17330:17332] CHIP:TOO: Endpoint: 1 Cluster: 0x0000_0060 Command 0x0000_0004 - [1689674653.323058][17330:17332] CHIP:TOO: OperationalCommandResponse: { - [1689674653.323066][17330:17332] CHIP:TOO: commandResponseState: { - [1689674653.323076][17330:17332] CHIP:TOO: ErrorStateID: 0 - [1689674653.323085][17330:17332] CHIP:TOO: } - [1689674653.323094][17330:17332] CHIP:TOO: } - disabled: true - - - label: - "Step 24: Verify TH receives an OperationCompletion event with the - following fields populated as follows: 1. CompletionErrorCode set to - NoError(0x00) 2. TotalOperationalTime is approximately 1.5 times the - initial-countdown-time or null 3. PausedTime is 0.5 times the - initial-countdown-time" - PICS: OPSTATE.S.E01 - verification: | - operationalstate read-event operation-completion 1 1 - - [1657193007.841358][5422:5427] CHIP:TOO: Endpoint: 0 Endpoint: 1 Cluster: 0x0000_0060 Event 0x0000_0001 - [1657193007.841387][5422:5427] CHIP:TOO: Event number: 1 - [1657193007.841409][5422:5427] CHIP:TOO: Priority: INFO - [1657193007.841431][5422:5427] CHIP:TOO: Timestamp: 155146 - [1657193007.841531][5422:5427] CHIP:TOO: OperationCompletion: { - [1657193007.841570][5422:5427] CHIP:TOO: CompletionErrorCode: 0x00 - TotalOperationalTime: null - } - disabled: true diff --git a/src/app/tests/suites/certification/ci-pics-values b/src/app/tests/suites/certification/ci-pics-values index edc5c30faea79d..fd11f3ae2ac007 100644 --- a/src/app/tests/suites/certification/ci-pics-values +++ b/src/app/tests/suites/certification/ci-pics-values @@ -1868,6 +1868,39 @@ OPSTATE.S.C04.Tx=1 OPSTATE.S.E00=1 OPSTATE.S.E01=1 + +# Oven Operational State + +OVENOPSTATE.S=1 +OVENOPSTATE.C=1 +#ManuallyControlled + +OVENOPSTATE.S.M.ST_STOPPED=1 +OVENOPSTATE.S.M.ST_RUNNING=1 +OVENOPSTATE.S.M.ST_PAUSED=1 +OVENOPSTATE.S.M.ST_ERROR=1 +OVENOPSTATE.S.M.ERR_NO_ERROR=1 +OVENOPSTATE.S.M.ERR_UNABLE_TO_START_OR_RESUME=1 +OVENOPSTATE.S.M.ERR_UNABLE_TO_COMPLETE_OPERATION=1 +OVENOPSTATE.S.M.ERR_COMMAND_INVALID_IN_STATE=1 + +# Server Attributes +OVENOPSTATE.S.A0000=1 +OVENOPSTATE.S.A0001=1 +OVENOPSTATE.S.A0002=1 +OVENOPSTATE.S.A0003=1 +OVENOPSTATE.S.A0004=1 +OVENOPSTATE.S.A0005=1 +OVENOPSTATE.S.C00.Rsp=1 +OVENOPSTATE.S.C01.Rsp=1 +OVENOPSTATE.S.C02.Rsp=1 +OVENOPSTATE.S.C03.Rsp=1 +OVENOPSTATE.S.C04.Tx=1 + +# Server Events +OVENOPSTATE.S.E00=1 +OVENOPSTATE.S.E01=1 + # Thermostat # Server TSTAT.S=1 @@ -2762,7 +2795,7 @@ DEM.S.A0005=0 DEM.S.A0006=0 DEM.S.A0007=0 -# Features +# Features DEM.S.F00=0 DEM.S.F01=0 DEM.S.F02=0 diff --git a/src/app/tests/suites/ciTests.json b/src/app/tests/suites/ciTests.json index 9594ea623d1717..cbd655299f8422 100644 --- a/src/app/tests/suites/ciTests.json +++ b/src/app/tests/suites/ciTests.json @@ -177,7 +177,6 @@ "Test_TC_OO_2_2", "Test_TC_OO_2_4" ], - "OperationalState": ["Test_TC_OPSTATE_1_1", "Test_TC_OPSTATE_2_4"], "PowerSource": ["Test_TC_PS_1_1", "Test_TC_PS_2_1"], "PressureMeasurement": [ "Test_TC_PRS_1_1", diff --git a/src/app/tests/suites/manualTests.json b/src/app/tests/suites/manualTests.json index 0263fc258b8db0..1c66bfccfbb9b0 100644 --- a/src/app/tests/suites/manualTests.json +++ b/src/app/tests/suites/manualTests.json @@ -276,7 +276,6 @@ "LevelControl": [], "LaundryWasherMode": ["Test_TC_LWM_1_2"], "OnOff": ["Test_TC_OO_2_3"], - "OperationalState": ["Test_TC_OPSTATE_2_2", "Test_TC_OPSTATE_2_5"], "RelativeHumidityMeasurement": ["Test_TC_RH_2_2"], "SmokeCOAlarm": [], "RefrigeratorAlarm": ["Test_TC_REFALM_2_2", "Test_TC_REFALM_2_3"], @@ -354,7 +353,6 @@ "LaundryWasherMode", "OnOff", "OvenMode", - "OperationalState", "OvenMode", "RelativeHumidityMeasurement", "SmokeCOAlarm", diff --git a/src/controller/python/test/test_scripts/mobile-device-test.py b/src/controller/python/test/test_scripts/mobile-device-test.py index 87e0a9acb6569b..9ceaa35d24c291 100755 --- a/src/controller/python/test/test_scripts/mobile-device-test.py +++ b/src/controller/python/test/test_scripts/mobile-device-test.py @@ -251,8 +251,12 @@ def do_tests(controller_nodeid, device_nodeid, address, timeout, discriminator, multiple=True, default=[], help="Trace location") +@click.option('--app-pid', + type=int, + default=0, + help="The PID of the app against which the test is going to run") def run(controller_nodeid, device_nodeid, address, timeout, discriminator, setup_pin, enable_test, disable_test, log_level, - log_format, print_test_list, paa_trust_store_path, trace_to): + log_format, print_test_list, paa_trust_store_path, trace_to, app_pid): coloredlogs.install(level=log_level, fmt=log_format, logger=logger) if print_test_list: diff --git a/src/python_testing/TC_OPSTATE_1_1.py b/src/python_testing/TC_OPSTATE_1_1.py new file mode 100644 index 00000000000000..67caefc85862b5 --- /dev/null +++ b/src/python_testing/TC_OPSTATE_1_1.py @@ -0,0 +1,53 @@ +# +# 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. +# + + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from TC_OpstateCommon import TC_OPSTATE_BASE, TestInfo + + +class TC_OPSTATE_1_1(MatterBaseTest, TC_OPSTATE_BASE): + def __init__(self, *args): + super().__init__(*args) + + test_info = TestInfo( + pics_code="OPSTATE", + cluster=Clusters.OperationalState + ) + + super().setup_base(test_info=test_info) + + def steps_TC_OPSTATE_1_1(self) -> list[TestStep]: + return self.STEPS_TC_OPSTATE_BASE_1_1() + + def pics_TC_OPSTATE_1_1(self) -> list[str]: + return ["OPSTATE.S"] + + @async_test_body + async def test_TC_OPSTATE_1_1(self): + endpoint = self.matter_test_config.endpoint + cluster_revision = 2 + feature_map = 0 + + await self.TEST_TC_OPSTATE_BASE_1_1(endpoint=endpoint, + cluster_revision=cluster_revision, + feature_map=feature_map) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_OPSTATE_2_1.py b/src/python_testing/TC_OPSTATE_2_1.py index 98dbe83dc09057..0827202af71c29 100644 --- a/src/python_testing/TC_OPSTATE_2_1.py +++ b/src/python_testing/TC_OPSTATE_2_1.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2023 Project CHIP Authors +# Copyright (c) 2024 Project CHIP Authors # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,173 +15,33 @@ # limitations under the License. # -import logging import chip.clusters as Clusters -from chip.clusters.Types import NullValue -from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main -from mobly import asserts +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from TC_OpstateCommon import TC_OPSTATE_BASE, TestInfo -# This test requires several additional command line arguments -# run with -# --int-arg PIXIT_ENDPOINT: +class TC_OPSTATE_2_1(MatterBaseTest, TC_OPSTATE_BASE): + def __init__(self, *args): + super().__init__(*args) -class TC_OPSTATE_2_1(MatterBaseTest): + test_info = TestInfo( + pics_code="OPSTATE", + cluster=Clusters.OperationalState + ) - async def read_mod_attribute_expect_success(self, endpoint, attribute): - cluster = Clusters.Objects.OperationalState - return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + super().setup_base(test_info=test_info) - async def read_and_validate_opstate(self, step, expected_state): - self.print_step(step, "Read OperationalState attribute") - operational_state = await self.read_mod_attribute_expect_success( - endpoint=self.endpoint, attribute=Clusters.OperationalState.Attributes.OperationalState) - logging.info("OperationalState: %s" % (operational_state)) - asserts.assert_equal(operational_state, expected_state, - "OperationalState(%s) should equal %s" % (operational_state, expected_state)) - - async def read_and_validate_operror(self, step, expected_error): - self.print_step(step, "Read OperationalError attribute") - operational_error = await self.read_mod_attribute_expect_success( - endpoint=self.endpoint, attribute=Clusters.OperationalState.Attributes.OperationalError) - logging.info("OperationalError: %s" % (operational_error)) - asserts.assert_equal(operational_error.errorStateID, expected_error, - "errorStateID(%s) should equal %s" % (operational_error.errorStateID, expected_error)) + def steps_TC_OPSTATE_2_1(self) -> list[TestStep]: + return self.STEPS_TC_OPSTATE_BASE_2_1() def pics_TC_OPSTATE_2_1(self) -> list[str]: return ["OPSTATE.S"] @async_test_body async def test_TC_OPSTATE_2_1(self): - - asserts.assert_true('PIXIT_ENDPOINT' in self.matter_test_config.global_test_params, - "PIXIT_ENDPOINT must be included on the command line in " - "the --int-arg flag as PIXIT_ENDPOINT:") - - self.endpoint = self.matter_test_config.global_test_params['PIXIT_ENDPOINT'] - - attributes = Clusters.OperationalState.Attributes - - self.print_step(1, "Commissioning, already done") - - if self.check_pics("OPSTATE.S.A0000"): - self.print_step(2, "Read PhaseList attribute") - phase_list = await self.read_mod_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.PhaseList) - - if phase_list == NullValue: - logging.info("PhaseList is null") - else: - logging.info("PhaseList: %s" % (phase_list)) - - phase_list_len = len(phase_list) - - asserts.assert_less_equal(phase_list_len, 32, - "PhaseList length(%d) must be less than 32!" % phase_list_len) - - if self.check_pics("OPSTATE.S.A0001"): - self.print_step(3, "Read CurrentPhase attribute") - current_phase = await self.read_mod_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.CurrentPhase) - logging.info("CurrentPhase: %s" % (current_phase)) - - if phase_list == NullValue: - asserts.assert_true(current_phase == NullValue, "CurrentPhase(%s) should be null" % current_phase) - else: - asserts.assert_true(0 <= current_phase and current_phase < phase_list_len, - "CurrentPhase(%s) must be between 0 and %s" % (current_phase, (phase_list_len - 1))) - - if self.check_pics("OPSTATE.S.A0002"): - self.print_step(4, "Read CountdownTime attribute") - countdown_time = await self.read_mod_attribute_expect_success(endpoint=self.endpoint, - attribute=attributes.CountdownTime) - - logging.info("CountdownTime: %s" % (countdown_time)) - if countdown_time is not NullValue: - asserts.assert_true(0 <= countdown_time <= 259200, - "CountdownTime(%s) must be between 0 and 259200" % countdown_time) - - if self.check_pics("OPSTATE.S.A0003"): - self.print_step(5, "Read OperationalStateList attribute") - operational_state_list = await self.read_mod_attribute_expect_success(endpoint=self.endpoint, - attribute=attributes.OperationalStateList) - - logging.info("OperationalStateList: %s" % (operational_state_list)) - - defined_states = [state.value for state in Clusters.OperationalState.Enums.OperationalStateEnum - if state is not Clusters.OperationalState.Enums.OperationalStateEnum.kUnknownEnumValue] - - for state in operational_state_list: - in_range = (0x80 <= state.operationalStateID <= 0xBF) - asserts.assert_true(state.operationalStateID in defined_states or in_range, - "Found a OperationalStateList entry with invalid ID value!") - if in_range: - asserts.assert_true(state.operationalStateLabel is not None, - "The OperationalStateLabel should be populated") - if state.operationalStateID == Clusters.OperationalState.Enums.OperationalStateEnum.kError: - error_state_present = True - - asserts.assert_true(error_state_present, "The OperationalStateList does not have an ID entry of Error(0x03)") - - if self.check_pics("OPSTATE.S.A0004"): - self.print_step(6, "Read OperationalState attribute") - operational_state = await self.read_mod_attribute_expect_success(endpoint=self.endpoint, - attribute=attributes.OperationalState) - - logging.info("OperationalState: %s" % (operational_state)) - - in_range = (0x80 <= operational_state <= 0xBF) - asserts.assert_true(operational_state in defined_states or in_range, "OperationalState has an invalid ID value!") - - if self.check_pics("OPSTATE.S.M.ST_STOPPED"): - self.print_step("6a", "Manually put the device in the stopped state") - input("Press Enter when done.\n") - await self.read_and_validate_opstate(step="6b", expected_state=Clusters.OperationalState.Enums.OperationalStateEnum.kStopped) - if self.check_pics("OPSTATE.S.M.ST_RUNNING"): - self.print_step("6c", "Manually put the device in the running state") - input("Press Enter when done.\n") - await self.read_and_validate_opstate(step="6d", expected_state=Clusters.OperationalState.Enums.OperationalStateEnum.kRunning) - if self.check_pics("OPSTATE.S.M.ST_PAUSED"): - self.print_step("6e", "Manually put the device in the paused state") - input("Press Enter when done.\n") - await self.read_and_validate_opstate(step="6f", expected_state=Clusters.OperationalState.Enums.OperationalStateEnum.kPaused) - if self.check_pics("OPSTATE.S.M.ST_ERROR"): - self.print_step("6g", "Manually put the device in the error state") - input("Press Enter when done.\n") - await self.read_and_validate_opstate(step="6h", expected_state=Clusters.OperationalState.Enums.OperationalStateEnum.kError) - - if self.check_pics("OPSTATE.S.A0005"): - self.print_step(7, "Read OperationalError attribute") - operational_error = await self.read_mod_attribute_expect_success(endpoint=self.endpoint, - attribute=attributes.OperationalError) - - logging.info("OperationalError: %s" % (operational_error)) - - # Defined Errors - defined_errors = [error.value for error in Clusters.OperationalState.Enums.ErrorStateEnum - if error is not Clusters.OperationalState.Enums.ErrorStateEnum.kUnknownEnumValue] - - in_range = (0x80 <= operational_error.errorStateID <= 0xBF) - asserts.assert_true(operational_error.errorStateID in defined_errors - or in_range, "OperationalError has an invalid ID value!") - if in_range: - asserts.assert_true(operational_error.errorStateLabel is not None, "ErrorStateLabel should be populated") - - if self.check_pics("OPSTATE.S.M.ERR_NO_ERROR"): - self.print_step("7a", "Manually put the device in the no error state") - input("Press Enter when done.\n") - await self.read_and_validate_operror(step="7b", expected_error=Clusters.OperationalState.Enums.ErrorStateEnum.kNoError) - if self.check_pics("OPSTATE.S.M.ERR_UNABLE_TO_START_OR_RESUME"): - self.print_step("7c", "Manually put the device in the unable to start or resume error state") - input("Press Enter when done.\n") - await self.read_and_validate_operror(step="7d", expected_error=Clusters.OperationalState.Enums.ErrorStateEnum.kUnableToStartOrResume) - if self.check_pics("OPSTATE.S.M.ERR_UNABLE_TO_COMPLETE_OPERATION"): - self.print_step("7e", "Manually put the device in the unable to complete operation error state") - input("Press Enter when done.\n") - await self.read_and_validate_operror(step="7f", expected_error=Clusters.OperationalState.Enums.ErrorStateEnum.kUnableToCompleteOperation) - if self.check_pics("OPSTATE.S.M.ERR_COMMAND_INVALID_IN_STATE"): - self.print_step("7g", "Manually put the device in the command invalid error state") - input("Press Enter when done.\n") - await self.read_and_validate_operror(step="7h", expected_error=Clusters.OperationalState.Enums.ErrorStateEnum.kCommandInvalidInState) + endpoint = self.matter_test_config.endpoint + await self.TEST_TC_OPSTATE_BASE_2_1(endpoint) if __name__ == "__main__": diff --git a/src/python_testing/TC_OPSTATE_2_2.py b/src/python_testing/TC_OPSTATE_2_2.py new file mode 100644 index 00000000000000..59d36b56cfa66d --- /dev/null +++ b/src/python_testing/TC_OPSTATE_2_2.py @@ -0,0 +1,50 @@ +# +# 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. +# + + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from TC_OpstateCommon import TC_OPSTATE_BASE, TestInfo + + +class TC_OPSTATE_2_2(MatterBaseTest, TC_OPSTATE_BASE): + + def __init__(self, *args): + super().__init__(*args) + + test_info = TestInfo( + pics_code="OPSTATE", + cluster=Clusters.OperationalState + ) + + super().setup_base(test_info=test_info) + + def steps_TC_OPSTATE_2_2(self) -> list[TestStep]: + return self.STEPS_TC_OPSTATE_BASE_2_2() + + def pics_TC_OPSTATE_2_2(self) -> list[str]: + return ["OPSTATE.S"] + + @async_test_body + async def test_TC_OPSTATE_2_2(self): + endpoint = self.matter_test_config.endpoint + + await self.TEST_TC_OPSTATE_BASE_2_2(endpoint=endpoint) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_OPSTATE_2_3.py b/src/python_testing/TC_OPSTATE_2_3.py index 57189c60382f08..e05aef4f607263 100644 --- a/src/python_testing/TC_OPSTATE_2_3.py +++ b/src/python_testing/TC_OPSTATE_2_3.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2023 Project CHIP Authors +# Copyright (c) 2024 Project CHIP Authors # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,160 +15,34 @@ # limitations under the License. # -import logging -import time import chip.clusters as Clusters -from chip.clusters.Types import NullValue -from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main, type_matches -from mobly import asserts +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from TC_OpstateCommon import TC_OPSTATE_BASE, TestInfo -# This test requires several additional command line arguments -# run with -# --int-arg PIXIT_ENDPOINT: +class TC_OPSTATE_2_3(MatterBaseTest, TC_OPSTATE_BASE): + def __init__(self, *args): + super().__init__(*args) -class TC_OPSTATE_2_3(MatterBaseTest): + test_info = TestInfo( + pics_code="OPSTATE", + cluster=Clusters.OperationalState + ) - async def read_mod_attribute_expect_success(self, endpoint, attribute): - cluster = Clusters.Objects.OperationalState - return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + super().setup_base(test_info=test_info) - async def send_pause_cmd(self) -> Clusters.Objects.OperationalState.Commands.Pause: - ret = await self.send_single_cmd(cmd=Clusters.Objects.OperationalState.Commands.Pause(), endpoint=self.endpoint) - asserts.assert_true(type_matches(ret, Clusters.Objects.OperationalState.Commands.OperationalCommandResponse), - "Unexpected return type for Pause") - return ret - - async def send_resume_cmd(self) -> Clusters.Objects.OperationalState.Commands.Resume: - ret = await self.send_single_cmd(cmd=Clusters.Objects.OperationalState.Commands.Resume(), endpoint=self.endpoint) - asserts.assert_true(type_matches(ret, Clusters.Objects.OperationalState.Commands.OperationalCommandResponse), - "Unexpected return type for Resume") - return ret + def steps_TC_OPSTATE_2_3(self) -> list[TestStep]: + return self.STEPS_TC_OPSTATE_BASE_2_3() def pics_TC_OPSTATE_2_3(self) -> list[str]: return ["OPSTATE.S"] @async_test_body async def test_TC_OPSTATE_2_3(self): + endpoint = self.matter_test_config.endpoint - asserts.assert_true('PIXIT_ENDPOINT' in self.matter_test_config.global_test_params, - "PIXIT_ENDPOINT must be included on the command line in " - "the --int-arg flag as PIXIT_ENDPOINT:") - - self.endpoint = self.matter_test_config.global_test_params['PIXIT_ENDPOINT'] - - asserts.assert_true(self.check_pics("OPSTATE.S.A0003"), "OPSTATE.S.A0003 must be supported") - asserts.assert_true(self.check_pics("OPSTATE.S.A0004"), "OPSTATE.S.A0004 must be supported") - asserts.assert_true(self.check_pics("OPSTATE.S.C00.Rsp"), "OPSTATE.S.C00.Rsp must be supported") - asserts.assert_true(self.check_pics("OPSTATE.S.C03.Rsp"), "OPSTATE.S.C03.Rsp must be supported") - # This command SHALL be supported by an implementation if any of the other commands are supported (6.5) - asserts.assert_true(self.check_pics("OPSTATE.S.C04.Rsp"), "OPSTATE.S.C04.Rsp must be supported") - - attributes = Clusters.OperationalState.Attributes - - self.print_step(1, "Commissioning, already done") - - self.print_step(2, "Manually put the device in a state where it can receive a Pause command") - input("Press Enter when done.\n") - - self.print_step(3, "Read OperationalStateList attribute") - op_state_list = await self.read_mod_attribute_expect_success(endpoint=self.endpoint, - attribute=attributes.OperationalStateList) - - logging.info("OperationalStateList: %s" % (op_state_list)) - - defined_states = [state.value for state in Clusters.OperationalState.Enums.OperationalStateEnum - if state is not Clusters.OperationalState.Enums.OperationalStateEnum.kUnknownEnumValue] - - state_ids = set([s.operationalStateID for s in op_state_list]) - - asserts.assert_true(all(id in state_ids for id in defined_states), "OperationalStateList is missing a required entry") - - self.print_step(4, "Send Pause command") - ret = await self.send_pause_cmd() - asserts.assert_equal(ret.commandResponseState.errorStateID, Clusters.OperationalState.Enums.ErrorStateEnum.kNoError, - "errorStateID(%s) should be NoError(0x00)" % ret.commandResponseState.errorStateID) - - self.print_step(5, "Read OperationalState attribute") - operational_state = await self.read_mod_attribute_expect_success(endpoint=self.endpoint, - attribute=attributes.OperationalState) - logging.info("OperationalState: %s" % (operational_state)) - asserts.assert_equal(operational_state, Clusters.OperationalState.Enums.OperationalStateEnum.kPaused, - "OperationalState ID should be Paused(0x02)") - - if self.check_pics("OPSTATE.S.A0002"): - self.print_step(6, "Read CountdownTime attribute") - initial_countdown_time = await self.read_mod_attribute_expect_success(endpoint=self.endpoint, - attribute=attributes.CountdownTime) - logging.info("CountdownTime: %s" % (initial_countdown_time)) - if initial_countdown_time is not NullValue: - in_range = (1 <= initial_countdown_time <= 259200) - asserts.assert_true(initial_countdown_time is NullValue or in_range, - "invalid CountdownTime(%s). Must be in between 1 and 259200, or null " % initial_countdown_time) - - self.print_step(7, "Waiting for 5 seconds") - time.sleep(5) - - self.print_step(8, "Read CountdownTime attribute") - countdown_time = await self.read_mod_attribute_expect_success(endpoint=self.endpoint, attribute=attributes.CountdownTime) - logging.info("CountdownTime: %s" % (countdown_time)) - asserts.assert_true(countdown_time != 0 or countdown_time == NullValue, - "invalid CountdownTime(%s). Must be a non zero integer, or null" % countdown_time) - asserts.assert_equal(countdown_time, initial_countdown_time, "CountdownTime(%s) does not equal to the intial CountdownTime(%s)" - % (countdown_time, initial_countdown_time)) - - self.print_step(9, "Send Pause command") - ret = await self.send_pause_cmd() - asserts.assert_equal(ret.commandResponseState.errorStateID, Clusters.OperationalState.Enums.ErrorStateEnum.kNoError, - "errorStateID(%s) should be NoError(0x00)" % ret.commandResponseState.errorStateID) - - self.print_step(10, "Send Resume command") - ret = await self.send_resume_cmd() - asserts.assert_equal(ret.commandResponseState.errorStateID, Clusters.OperationalState.Enums.ErrorStateEnum.kNoError, - "errorStateID(%s) should be NoError(0x00)" % ret.commandResponseState.errorStateID) - - self.print_step(11, "Read OperationalState attribute") - operational_state = await self.read_mod_attribute_expect_success(endpoint=self.endpoint, - attribute=attributes.OperationalState) - logging.info("OperationalState: %s" % (operational_state)) - asserts.assert_equal(operational_state, Clusters.OperationalState.Enums.OperationalStateEnum.kRunning, - "OperationalState(%s) should be Running(0x01)" % operational_state) - - self.print_step(12, "Send Resume command") - ret = await self.send_resume_cmd() - asserts.assert_equal(ret.commandResponseState.errorStateID, Clusters.OperationalState.Enums.ErrorStateEnum.kNoError, - "errorStateID(%s) should be NoError(0x00)" % ret.commandResponseState.errorStateID) - - self.print_step(13, "Manually put the device in the Stopped(0x00) operational state") - input("Press Enter when done.\n") - - self.print_step(14, "Send Pause command") - ret = await self.send_pause_cmd() - asserts.assert_equal(ret.commandResponseState.errorStateID, - Clusters.OperationalState.Enums.ErrorStateEnum.kCommandInvalidInState, - "errorStateID(%s) should be CommandInvalidInState(0x03)" % ret.commandResponseState.errorStateID) - - self.print_step(15, "Send Resume command") - ret = await self.send_resume_cmd() - asserts.assert_equal(ret.commandResponseState.errorStateID, - Clusters.OperationalState.Enums.ErrorStateEnum.kCommandInvalidInState, - "errorStateID(%s) should be CommandInvalidInState(0x03)" % ret.commandResponseState.errorStateID) - - self.print_step(16, "Manually put the device in the Error(0x03) operational state") - input("Press Enter when done.\n") - - self.print_step(17, "Send Pause command") - ret = await self.send_pause_cmd() - asserts.assert_equal(ret.commandResponseState.errorStateID, - Clusters.OperationalState.Enums.ErrorStateEnum.kCommandInvalidInState, - "errorStateID(%s) should be CommandInvalidInState(0x03)" % ret.commandResponseState.errorStateID) - - self.print_step(18, "Send Resume command") - ret = await self.send_resume_cmd() - asserts.assert_equal(ret.commandResponseState.errorStateID, - Clusters.OperationalState.Enums.ErrorStateEnum.kCommandInvalidInState, - "errorStateID(%s) should be CommandInvalidInState(0x03)" % ret.commandResponseState.errorStateID) + await self.TEST_TC_OPSTATE_BASE_2_3(endpoint=endpoint) if __name__ == "__main__": diff --git a/src/python_testing/TC_OPSTATE_2_4.py b/src/python_testing/TC_OPSTATE_2_4.py new file mode 100644 index 00000000000000..7ad8fbccd9cd42 --- /dev/null +++ b/src/python_testing/TC_OPSTATE_2_4.py @@ -0,0 +1,49 @@ +# +# 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. +# + + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from TC_OpstateCommon import TC_OPSTATE_BASE, TestInfo + + +class TC_OPSTATE_2_4(MatterBaseTest, TC_OPSTATE_BASE): + def __init__(self, *args): + super().__init__(*args) + + test_info = TestInfo( + pics_code="OPSTATE", + cluster=Clusters.OperationalState + ) + + super().setup_base(test_info=test_info) + + def steps_TC_OPSTATE_2_4(self) -> list[TestStep]: + return self.STEPS_TC_OPSTATE_BASE_2_4() + + def pics_TC_OPSTATE_2_4(self) -> list[str]: + return ["OPSTATE.S"] + + @async_test_body + async def test_TC_OPSTATE_2_4(self): + endpoint = self.matter_test_config.endpoint + + await self.TEST_TC_OPSTATE_BASE_2_4(endpoint=endpoint) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_OPSTATE_2_5.py b/src/python_testing/TC_OPSTATE_2_5.py new file mode 100644 index 00000000000000..ffaceaeb2863a0 --- /dev/null +++ b/src/python_testing/TC_OPSTATE_2_5.py @@ -0,0 +1,49 @@ +# +# 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. +# + + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from TC_OpstateCommon import TC_OPSTATE_BASE, TestInfo + + +class TC_OPSTATE_2_5(MatterBaseTest, TC_OPSTATE_BASE): + def __init__(self, *args): + super().__init__(*args) + + test_info = TestInfo( + pics_code="OPSTATE", + cluster=Clusters.OperationalState + ) + + super().setup_base(test_info=test_info) + + def steps_TC_OPSTATE_2_5(self) -> list[TestStep]: + return self.STEPS_TC_OPSTATE_BASE_2_5() + + def pics_TC_OPSTATE_2_5(self) -> list[str]: + return ["OPSTATE.S"] + + @async_test_body + async def test_TC_OPSTATE_2_5(self): + endpoint = self.matter_test_config.endpoint + + await self.TEST_TC_OPSTATE_BASE_2_5(endpoint=endpoint) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_OVENOPSTATE_1_1.py b/src/python_testing/TC_OVENOPSTATE_1_1.py new file mode 100644 index 00000000000000..3e6b0a27fbab5d --- /dev/null +++ b/src/python_testing/TC_OVENOPSTATE_1_1.py @@ -0,0 +1,53 @@ +# +# 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. +# + + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from TC_OpstateCommon import TC_OPSTATE_BASE, TestInfo + + +class TC_OVENOPSTATE_1_1(MatterBaseTest, TC_OPSTATE_BASE): + def __init__(self, *args): + super().__init__(*args) + + test_info = TestInfo( + pics_code="OVENOPSTATE", + cluster=Clusters.OvenCavityOperationalState + ) + + super().setup_base(test_info=test_info) + + def steps_TC_OVENOPSTATE_1_1(self) -> list[TestStep]: + return self.STEPS_TC_OPSTATE_BASE_1_1() + + def pics_TC_OVENOPSTATE_1_1(self) -> list[str]: + return ["OVENOPSTATE.S"] + + @async_test_body + async def test_TC_OVENOPSTATE_1_1(self): + endpoint = self.matter_test_config.endpoint + cluster_revision = 1 + feature_map = 0 + + await self.TEST_TC_OPSTATE_BASE_1_1(endpoint=endpoint, + cluster_revision=cluster_revision, + feature_map=feature_map) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_OVENOPSTATE_2_1.py b/src/python_testing/TC_OVENOPSTATE_2_1.py new file mode 100644 index 00000000000000..e6847f22e94f39 --- /dev/null +++ b/src/python_testing/TC_OVENOPSTATE_2_1.py @@ -0,0 +1,48 @@ +# +# 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. +# + + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from TC_OpstateCommon import TC_OPSTATE_BASE, TestInfo + + +class TC_OVENOPSTATE_2_1(MatterBaseTest, TC_OPSTATE_BASE): + def __init__(self, *args): + super().__init__(*args) + + test_info = TestInfo( + pics_code="OVENOPSTATE", + cluster=Clusters.OvenCavityOperationalState + ) + + super().setup_base(test_info=test_info) + + def steps_TC_OVENOPSTATE_2_1(self) -> list[TestStep]: + return self.STEPS_TC_OPSTATE_BASE_2_1() + + def pics_TC_OVENOPSTATE_2_1(self) -> list[str]: + return ["OVENOPSTATE.S"] + + @async_test_body + async def test_TC_OVENOPSTATE_2_1(self): + endpoint = self.matter_test_config.endpoint + await self.TEST_TC_OPSTATE_BASE_2_1(endpoint) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_OVENOPSTATE_2_2.py b/src/python_testing/TC_OVENOPSTATE_2_2.py new file mode 100644 index 00000000000000..7789f5076a7d0a --- /dev/null +++ b/src/python_testing/TC_OVENOPSTATE_2_2.py @@ -0,0 +1,50 @@ +# +# 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. +# + + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from TC_OpstateCommon import TC_OPSTATE_BASE, TestInfo + + +class TC_OVENOPSTATE_2_2(MatterBaseTest, TC_OPSTATE_BASE): + + def __init__(self, *args): + super().__init__(*args) + + test_info = TestInfo( + pics_code="OVENOPSTATE", + cluster=Clusters.OvenCavityOperationalState + ) + + super().setup_base(test_info=test_info) + + def steps_TC_OVENOPSTATE_2_2(self) -> list[TestStep]: + return self.STEPS_TC_OPSTATE_BASE_2_2() + + def pics_TC_OVENOPSTATE_2_2(self) -> list[str]: + return ["OVENOPSTATE.S"] + + @async_test_body + async def test_TC_OVENOPSTATE_2_2(self): + endpoint = self.matter_test_config.endpoint + + await self.TEST_TC_OPSTATE_BASE_2_2(endpoint=endpoint) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_OVENOPSTATE_2_3.py b/src/python_testing/TC_OVENOPSTATE_2_3.py new file mode 100644 index 00000000000000..25434978320b46 --- /dev/null +++ b/src/python_testing/TC_OVENOPSTATE_2_3.py @@ -0,0 +1,49 @@ +# +# 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. +# + + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from TC_OpstateCommon import TC_OPSTATE_BASE, TestInfo + + +class TC_OVENOPSTATE_2_3(MatterBaseTest, TC_OPSTATE_BASE): + def __init__(self, *args): + super().__init__(*args) + + test_info = TestInfo( + pics_code="OVENOPSTATE", + cluster=Clusters.OvenCavityOperationalState + ) + + super().setup_base(test_info=test_info) + + def steps_TC_OVENOPSTATE_2_3(self) -> list[TestStep]: + return self.STEPS_TC_OPSTATE_BASE_2_3() + + def pics_TC_OVENOPSTATE_2_3(self) -> list[str]: + return ["OVENOPSTATE.S"] + + @async_test_body + async def test_TC_OVENOPSTATE_2_3(self): + endpoint = self.matter_test_config.endpoint + + await self.TEST_TC_OPSTATE_BASE_2_3(endpoint=endpoint) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_OVENOPSTATE_2_4.py b/src/python_testing/TC_OVENOPSTATE_2_4.py new file mode 100644 index 00000000000000..4bc2040638b820 --- /dev/null +++ b/src/python_testing/TC_OVENOPSTATE_2_4.py @@ -0,0 +1,49 @@ +# +# 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. +# + + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from TC_OpstateCommon import TC_OPSTATE_BASE, TestInfo + + +class TC_OVENOPSTATE_2_4(MatterBaseTest, TC_OPSTATE_BASE): + def __init__(self, *args): + super().__init__(*args) + + test_info = TestInfo( + pics_code="OVENOPSTATE", + cluster=Clusters.OvenCavityOperationalState + ) + + super().setup_base(test_info=test_info) + + def steps_TC_OVENOPSTATE_2_4(self) -> list[TestStep]: + return self.STEPS_TC_OPSTATE_BASE_2_4() + + def pics_TC_OVENOPSTATE_2_4(self) -> list[str]: + return ["OVENOPSTATE.S"] + + @async_test_body + async def test_TC_OVENOPSTATE_2_4(self): + endpoint = self.matter_test_config.endpoint + + await self.TEST_TC_OPSTATE_BASE_2_4(endpoint=endpoint) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_OVENOPSTATE_2_5.py b/src/python_testing/TC_OVENOPSTATE_2_5.py new file mode 100644 index 00000000000000..7754f0a624ffd3 --- /dev/null +++ b/src/python_testing/TC_OVENOPSTATE_2_5.py @@ -0,0 +1,49 @@ +# +# 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. +# + + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from TC_OpstateCommon import TC_OPSTATE_BASE, TestInfo + + +class TC_OVENOPSTATE_2_5(MatterBaseTest, TC_OPSTATE_BASE): + def __init__(self, *args): + super().__init__(*args) + + test_info = TestInfo( + pics_code="OVENOPSTATE", + cluster=Clusters.OvenCavityOperationalState + ) + + super().setup_base(test_info=test_info) + + def steps_TC_OVENOPSTATE_2_5(self) -> list[TestStep]: + return self.STEPS_TC_OPSTATE_BASE_2_5() + + def pics_TC_OVENOPSTATE_2_5(self) -> list[str]: + return ["OVENOPSTATE.S"] + + @async_test_body + async def test_TC_OVENOPSTATE_2_5(self): + endpoint = self.matter_test_config.endpoint + + await self.TEST_TC_OPSTATE_BASE_2_5(endpoint=endpoint) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_OpstateCommon.py b/src/python_testing/TC_OpstateCommon.py new file mode 100644 index 00000000000000..9e32aac00ec846 --- /dev/null +++ b/src/python_testing/TC_OpstateCommon.py @@ -0,0 +1,1217 @@ +# +# 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. +# + +import json +import logging +import queue +import time +from dataclasses import dataclass +from typing import Any + +import chip.clusters as Clusters +import psutil +from chip.clusters import ClusterObjects as ClusterObjects +from chip.clusters.Attribute import EventReadResult, SubscriptionTransaction +from chip.clusters.Types import NullValue +from chip.interaction_model import InteractionModelError, Status +from matter_testing_support import EventChangeCallback, TestStep +from mobly import asserts + + +def get_pid(name): + pid = None + + for proc in psutil.process_iter(): + if name in proc.name(): + pid = proc.pid + break + + return pid + + +@dataclass +class TestInfo: + pics_code: str + cluster: Clusters + + +class EventSpecificChangeCallback: + def __init__(self, expected_event: ClusterObjects.ClusterEvent): + """This class creates a queue to store received event callbacks, that can be checked by the test script + expected_event: is the expected event + """ + self._q = queue.Queue() + self._expected_cluster_id = expected_event.cluster_id + self._expected_event = expected_event + + async def start(self, dev_ctrl, node_id: int, endpoint: int): + """This starts a subscription for events on the specified node_id and endpoint. The event is specified when the class instance is created.""" + self._subscription = await dev_ctrl.ReadEvent(node_id, + events=[(endpoint, self._expected_event, True)], reportInterval=(1, 5), + fabricFiltered=False, keepSubscriptions=True, autoResubscribe=False) + self._subscription.SetEventUpdateCallback(self.__call__) + + def __call__(self, res: EventReadResult, transaction: SubscriptionTransaction): + """This is the subscription callback when an event is received. + It checks the if the event is the expected one and then posts it into the queue for later processing.""" + if res.Status == Status.Success and res.Header.ClusterId == self._expected_cluster_id and res.Header.EventId == self._expected_event.event_id: + logging.info( + f'Got subscription report for event {self._expected_event.event_id} on cluster {self._expected_cluster_id}: {res.Data}') + self._q.put(res) + + def wait_for_event_report(self, timeout: int = 10): + """This function allows a test script to block waiting for the specific event to arrive with a timeout. + It returns the event data so that the values can be checked.""" + try: + res = self._q.get(block=True, timeout=timeout) + except queue.Empty: + asserts.fail(f"Failed to receive a report for the event {self._expected_event}") + + asserts.assert_equal(res.Header.ClusterId, self._expected_cluster_id, "Expected cluster ID not found in event report") + asserts.assert_equal(res.Header.EventId, self._expected_event.event_id, "Expected event ID not found in event report") + return res.Data + + +class TC_OPSTATE_BASE(): + def setup_base(self, test_info=None, app_pipe="/tmp/chip_all_clusters_fifo_"): + + asserts.assert_true(test_info is not None, + "You shall define the test info!") + + self.test_info = test_info + self.app_pipe = app_pipe + + if self.test_info.cluster == Clusters.OperationalState: + self.device = "Generic" + elif self.test_info.cluster == Clusters.OvenCavityOperationalState: + self.device = "Oven" + else: + asserts.fail(f"This provided cluster ({self.test_info.cluster}) is not supported!") + + def init_test(self): + self.is_ci = self.check_pics("PICS_SDK_CI_ONLY") + if self.is_ci: + app_pid = self.matter_test_config.app_pid + if app_pid == 0: + app_pid = get_pid("chip-all-clusters-app") + if app_pid is None: + asserts.fail("The --app-pid flag must be set when PICS_SDK_CI_ONLY is set") + self.app_pipe = self.app_pipe + str(app_pid) + + # Sends and out-of-band command to test-app + def write_to_app_pipe(self, command): + with open(self.app_pipe, "w") as app_pipe: + app_pipe.write(command + "\n") + + def send_raw_manual_or_pipe_command(self, command): + if self.is_ci: + self.write_to_app_pipe(command) + time.sleep(0.1) + else: + input("Press Enter when done.\n") + + def send_manual_or_pipe_command(self, device: str, name: str, operation: str, param: Any = None): + command = { + "Name": name, + "Device": device, + "Operation": operation, + } + + if param is not None: + command["Param"] = param + + self.send_raw_manual_or_pipe_command(json.dumps(command)) + + async def send_cmd(self, endpoint, cmd, timedRequestTimeoutMs=None): + logging.info(f"##### Command {cmd}") + + try: + return await self.send_single_cmd(cmd=cmd, + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) + + except InteractionModelError as e: + asserts.fail(f"Unexpected error returned: {e.status}") + + async def send_cmd_expect_response(self, endpoint, cmd, expected_response, timedRequestTimeoutMs=None): + ret = await self.send_cmd(endpoint=endpoint, + cmd=cmd, + timedRequestTimeoutMs=timedRequestTimeoutMs) + + asserts.assert_equal(ret.commandResponseState.errorStateID, + expected_response, + f"Command response ({ret.commandResponseState}) mismatched from expectation for {cmd} on {endpoint}") + + async def read_expect_success(self, endpoint, attribute): + logging.info(f"##### Read {attribute}") + attr_value = await self.read_single_attribute_check_success(endpoint=endpoint, + cluster=self.test_info.cluster, + attribute=attribute) + logging.info(f"## {attribute}: {attr_value}") + + return attr_value + + async def read_and_expect_value(self, endpoint, attribute, expected_value): + attr_value = await self.read_expect_success( + endpoint=endpoint, + attribute=attribute) + + asserts.assert_equal(attr_value, expected_value, + f"Value mismatched from expectation for {attribute} on {endpoint}") + + async def read_and_expect_property_value(self, endpoint, attribute, attr_property, expected_value): + attr_value = await self.read_expect_success( + endpoint=endpoint, + attribute=attribute) + field_value = getattr(attr_value, attr_property) + + asserts.assert_equal(field_value, expected_value, + f"Property '{attr_property}' value mismatched from expectation for {attribute} on {endpoint}") + + async def read_and_expect_array_contains(self, endpoint, attribute, expected_contains): + attr_value = await self.read_expect_success( + endpoint=endpoint, + attribute=attribute) + attr_value.sort() + expected_contains.sort() + + logging.info("## Current value: [%s]" % attr_value) + logging.info("## Expected value: [%s]" % expected_contains) + + for item in expected_contains: + if item not in attr_value: + asserts.fail("Entry (%s), not found! The returned value SHALL include all the entries: [%s]!" % ( + item, expected_contains)) + + ############################ + # TEST CASE 1.1 + ############################ + def STEPS_TC_OPSTATE_BASE_1_1(self) -> list[TestStep]: + steps = [TestStep(1, "Commissioning, already done", is_commissioning=True), + TestStep(2, "TH reads from the DUT the ClusterRevision attribute"), + TestStep(3, "TH reads from the DUT the FeatureMap attribute"), + TestStep(4, "TH reads from the DUT the AttributeList attribute"), + TestStep(5, "TH reads from the DUT the EventList attribute"), + TestStep(6, "TH reads from the DUT the AcceptedCommandList attribute"), + TestStep(7, "TH reads from the DUT the GeneratedCommandList attribute") + ] + return steps + + async def TEST_TC_OPSTATE_BASE_1_1(self, endpoint=1, cluster_revision=1, feature_map=0): + cluster = self.test_info.cluster + attributes = cluster.Attributes + events = cluster.Events + commands = cluster.Commands + + self.init_test() + + # STEP 1: Commission DUT to TH (can be skipped if done in a preceding test) + self.step(1) + + # STEP 2: TH reads from the DUT the ClusterRevision attribute + self.step(2) + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.ClusterRevision, + expected_value=cluster_revision) + + # STEP 3: TH reads from the DUT the FeatureMap attribute + self.step(3) + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.FeatureMap, + expected_value=feature_map) + + # STEP 4: TH reads from the DUT the AttributeList attribute + self.step(4) + expected_value = [ + attributes.PhaseList.attribute_id, + attributes.CurrentPhase.attribute_id, + attributes.OperationalStateList.attribute_id, + attributes.OperationalState.attribute_id, + attributes.OperationalError.attribute_id, + attributes.GeneratedCommandList.attribute_id, + attributes.AcceptedCommandList.attribute_id, + attributes.AttributeList.attribute_id, + attributes.FeatureMap.attribute_id, + attributes.ClusterRevision.attribute_id + ] + + if self.check_pics(f"{self.test_info.pics_code}.S.A0002"): + expected_value.append(attributes.CountdownTime.attribute_id) + + await self.read_and_expect_array_contains(endpoint=endpoint, + attribute=attributes.AttributeList, + expected_contains=expected_value) + + # STEP 5: TH reads from the DUT the EventList attribute + self.step(5) + if self.pics_guard(self.check_pics("PICS_EVENT_LIST_ENABLED")): + expected_value = [ + events.OperationalError.event_id, + ] + + if self.check_pics(f"{self.test_info.pics_code}.S.E01"): + expected_value.append(events.OperationCompletion.event_id) + + await self.read_and_expect_array_contains(endpoint=endpoint, + attribute=attributes.EventList, + expected_contains=expected_value) + + # STEP 6: TH reads from the DUT the AcceptedCommandList attribute + self.step(6) + expected_value = [] + + if (self.check_pics(f"{self.test_info.pics_code}.S.C00.Rsp") or + self.check_pics(f"{self.test_info.pics_code}.S.C03.Rsp")): + expected_value.append(commands.Pause.command_id) + + if (self.check_pics(f"{self.test_info.pics_code}.S.C01.Rsp") or + self.check_pics(f"{self.test_info.pics_code}.S.C02.Rsp")): + expected_value.append(commands.Stop.command_id) + + if self.check_pics(f"{self.test_info.pics_code}.S.C02.Rsp"): + expected_value.append(commands.Start.command_id) + + if (self.check_pics(f"{self.test_info.pics_code}.S.C03.Rsp") or + self.check_pics(f"{self.test_info.pics_code}.S.C00.Rsp")): + expected_value.append(commands.Resume.command_id) + + await self.read_and_expect_array_contains(endpoint=endpoint, + attribute=attributes.AcceptedCommandList, + expected_contains=expected_value) + + # STEP 7: TH reads from the DUT the AcceptedCommandList attribute + self.step(7) + expected_value = [] + + if (self.check_pics(f"{self.test_info.pics_code}.S.C00.Rsp") or + self.check_pics(f"{self.test_info.pics_code}.S.C01.Rsp") or + self.check_pics(f"{self.test_info.pics_code}.S.C02.Rsp") or + self.check_pics(f"{self.test_info.pics_code}.S.C03.Rsp")): + expected_value.append(commands.OperationalCommandResponse.command_id) + + await self.read_and_expect_array_contains(endpoint=endpoint, + attribute=attributes.GeneratedCommandList, + expected_contains=expected_value) + + ############################ + # TEST CASE 2.1 + ############################ + def STEPS_TC_OPSTATE_BASE_2_1(self) -> list[TestStep]: + steps = [TestStep(1, "Commissioning, already done", is_commissioning=True), + TestStep(2, "TH reads from the DUT the PhaseList attribute"), + TestStep(3, "TH reads from the DUT the CurrentPhase attribute"), + TestStep(4, "TH reads from the DUT the CountdownTime attribute"), + TestStep(5, "TH reads from the DUT the OperationalStateList attribute"), + TestStep(6, "TH reads from the DUT the OperationalState attribute"), + TestStep("6a", "Manually put the device in the Stopped(0x00) operational state"), + TestStep("6b", "TH reads from the DUT the OperationalState attribute"), + TestStep("6c", "Manually put the device in the Running(0x01) operational state"), + TestStep("6d", "TH reads from the DUT the OperationalState attribute"), + TestStep("6e", "Manually put the device in the Paused(0x02) operational state"), + TestStep("6f", "TH reads from the DUT the OperationalState attribute"), + TestStep("6g", "Manually put the device in the Error(0x03) operational state"), + TestStep("6h", "TH reads from the DUT the OperationalState attribute"), + TestStep(7, "TH reads from the DUT the OperationalError attribute"), + TestStep("7a", "Manually put the device in the NoError(0x00) error state"), + TestStep("7b", "TH reads from the DUT the OperationalError attribute"), + TestStep("7c", "Manually put the device in the UnableToStartOrResume(0x01) error state"), + TestStep("7d", "TH reads from the DUT the OperationalError attribute"), + TestStep("7e", "Manually put the device in the UnableToCompleteOperation(0x02) error state"), + TestStep("7f", "TH reads from the DUT the OperationalError attribute"), + TestStep("7g", "Manually put the device in the CommandInvalidInState(0x03) error state"), + TestStep("7h", "TH reads from the DUT the OperationalError attribute") + ] + return steps + + async def TEST_TC_OPSTATE_BASE_2_1(self, endpoint=1): + cluster = self.test_info.cluster + attributes = cluster.Attributes + + self.init_test() + + # STEP 1: Commission DUT to TH (can be skipped if done in a preceding test) + self.step(1) + + # STEP 2: TH reads from the DUT the PhaseList attribute + self.step(2) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0000")): + phase_list = await self.read_expect_success(endpoint=endpoint, + attribute=attributes.PhaseList) + if phase_list is not NullValue: + phase_list_len = len(phase_list) + asserts.assert_less_equal(phase_list_len, 32, + f"PhaseList length({phase_list_len}) must be less than 32!") + + # STEP 3: TH reads from the DUT the CurrentPhase attribute + self.step(3) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0001")): + current_phase = await self.read_expect_success(endpoint=endpoint, + attribute=attributes.CurrentPhase) + if (phase_list == NullValue) or (not phase_list): + asserts.assert_true(current_phase == NullValue, f"CurrentPhase({current_phase}) should be null") + else: + asserts.assert_true(0 <= current_phase and current_phase < phase_list_len, + f"CurrentPhase({current_phase}) must be between 0 and {(phase_list_len - 1)}") + + # STEP 4: TH reads from the DUT the CountdownTime attribute + self.step(4) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0002")): + countdown_time = await self.read_expect_success(endpoint=endpoint, + attribute=attributes.CountdownTime) + if countdown_time is not NullValue: + asserts.assert_true(0 <= countdown_time <= 259200, + f"CountdownTime({countdown_time}) must be between 0 and 259200") + + # STEP 5: TH reads from the DUT the OperationalStateList attribute + self.step(5) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0003")): + operational_state_list = await self.read_expect_success(endpoint=endpoint, + attribute=attributes.OperationalStateList) + defined_states = [state.value for state in cluster.Enums.OperationalStateEnum + if state != cluster.Enums.OperationalStateEnum.kUnknownEnumValue] + + for state in operational_state_list: + in_range = (0x80 <= state.operationalStateID <= 0xBF) + asserts.assert_true(state.operationalStateID in defined_states or in_range, + "Found a OperationalStateList entry with invalid ID value!") + if in_range: + asserts.assert_true(state.operationalStateLabel is not None, + "The OperationalStateLabel should be populated") + + if state.operationalStateID == cluster.Enums.OperationalStateEnum.kError: + error_state_present = True + + asserts.assert_true(error_state_present, "The OperationalStateList does not have an ID entry of Error(0x03)") + + # STEP 6: TH reads from the DUT the OperationalState attribute + self.step(6) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0004")): + operational_state = await self.read_expect_success(endpoint=endpoint, + attribute=attributes.OperationalState) + in_range = (0x80 <= operational_state <= 0xBF) + asserts.assert_true(operational_state in defined_states or in_range, + "OperationalState has an invalid ID value!") + + # STEP 6a: Manually put the device in the Stopped(0x00) operational state + self.step("6a") + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ST_STOPPED")): + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="Stop") + # STEP 6b: TH reads from the DUT the OperationalState attribute + self.step("6b") + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.OperationalState, + expected_value=cluster.Enums.OperationalStateEnum.kStopped) + else: + self.skip_step("6b") + + # STEP 6c: Manually put the device in the Running(0x01) operational state + self.step("6c") + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ST_RUNNING")): + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="Start") + # STEP 6d: TH reads from the DUT the OperationalState attribute + self.step("6d") + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.OperationalState, + expected_value=cluster.Enums.OperationalStateEnum.kRunning) + else: + self.skip_step("6d") + + # STEP 6e: Manually put the device in the Paused(0x02) operational state + self.step("6e") + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ST_PAUSED")): + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="Pause") + # STEP 6f: TH reads from the DUT the OperationalState attribute + self.step("6f") + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.OperationalState, + expected_value=cluster.Enums.OperationalStateEnum.kPaused) + else: + self.skip_step("6f") + + # STEP 6g: Manually put the device in the Error(0x03) operational state + self.step("6g") + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ST_ERROR")): + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="OnFault", + param=cluster.Enums.ErrorStateEnum.kUnableToStartOrResume) + # STEP 6h: TH reads from the DUT the OperationalState attribute + self.step("6h") + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.OperationalState, + expected_value=cluster.Enums.OperationalStateEnum.kError) + else: + self.skip_step("6h") + + # STEP 7: TH reads from the DUT the OperationalError attribute + self.step(7) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0005")): + operational_error = await self.read_expect_success(endpoint=endpoint, + attribute=attributes.OperationalError) + # Defined Errors + defined_errors = [error.value for error in cluster.Enums.ErrorStateEnum + if error != cluster.Enums.ErrorStateEnum.kUnknownEnumValue] + + in_range = (0x80 <= operational_error.errorStateID <= 0xBF) + asserts.assert_true(operational_error.errorStateID in defined_errors + or in_range, "OperationalError has an invalid ID value!") + if in_range: + asserts.assert_true(operational_error.errorStateLabel is not None, "ErrorStateLabel should be populated") + + # STEP 7a: Manually put the device in the NoError(0x00) error state + self.step("7a") + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ERR_NO_ERROR")): + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="OnFault", + param=cluster.Enums.ErrorStateEnum.kNoError) + # STEP 7b: TH reads from the DUT the OperationalError attribute + self.step("7b") + await self.read_and_expect_property_value(endpoint=endpoint, + attribute=attributes.OperationalError, + attr_property="errorStateID", + expected_value=cluster.Enums.ErrorStateEnum.kNoError) + else: + self.skip_step("7b") + + # STEP 7c: Manually put the device in the UnableToStartOrResume(0x01) error state + self.step("7c") + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ERR_UNABLE_TO_START_OR_RESUME")): + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="OnFault", + param=cluster.Enums.ErrorStateEnum.kUnableToStartOrResume) + # STEP 7d: TH reads from the DUT the OperationalError attribute + self.step("7d") + await self.read_and_expect_property_value(endpoint=endpoint, + attribute=attributes.OperationalError, + attr_property="errorStateID", + expected_value=cluster.Enums.ErrorStateEnum.kUnableToStartOrResume) + else: + self.skip_step("7d") + + # STEP 7e: Manually put the device in the UnableToCompleteOperation(0x02) error state + self.step("7e") + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ERR_UNABLE_TO_COMPLETE_OPERATION")): + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="OnFault", + param=cluster.Enums.ErrorStateEnum.kUnableToCompleteOperation) + # STEP 7f: TH reads from the DUT the OperationalError attribute + self.step("7f") + await self.read_and_expect_property_value(endpoint=endpoint, + attribute=attributes.OperationalError, + attr_property="errorStateID", + expected_value=cluster.Enums.ErrorStateEnum.kUnableToCompleteOperation) + else: + self.skip_step("7f") + + # STEP 7g: Manually put the device in the CommandInvalidInState(0x03) error state + self.step("7g") + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ERR_COMMAND_INVALID_IN_STATE")): + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="OnFault", + param=cluster.Enums.ErrorStateEnum.kCommandInvalidInState) + # STEP 7h: TH reads from the DUT the OperationalError attribute + self.step("7h") + await self.read_and_expect_property_value(endpoint=endpoint, + attribute=attributes.OperationalError, + attr_property="errorStateID", + expected_value=cluster.Enums.ErrorStateEnum.kCommandInvalidInState) + else: + self.skip_step("7h") + + ############################ + # TEST CASE 2.2 + ############################ + def STEPS_TC_OPSTATE_BASE_2_2(self) -> list[TestStep]: + steps = [TestStep(1, "Commissioning, already done", is_commissioning=True), + TestStep(2, "Manually put the DUT into a state wherein it can receive a Start Command"), + TestStep(3, "TH reads from the DUT the OperationalStateList attribute"), + TestStep(4, "TH sends Start command to the DUT"), + TestStep(5, "TH reads from the DUT the OperationalState attribute"), + TestStep(6, "TH reads from the DUT the OperationalError attribute"), + TestStep(7, "TH reads from the DUT the CountdownTime attribute"), + TestStep(8, "TH reads from the DUT the PhaseList attribute"), + TestStep(9, "TH reads from the DUT the CurrentPhase attribute"), + TestStep(10, "TH waits for {PIXIT.WAITTIME.COUNTDOWN}"), + TestStep(11, "TH reads from the DUT the CountdownTime attribute"), + TestStep(12, "TH sends Start command to the DUT"), + TestStep(13, "TH sends Stop command to the DUT"), + TestStep(14, "TH reads from the DUT the OperationalState attribute"), + TestStep(15, "TH sends Stop command to the DUT"), + TestStep(16, "Manually put the DUT into a state wherein it cannot receive a Start Command"), + TestStep(17, "TH sends Start command to the DUT") + ] + return steps + + async def TEST_TC_OPSTATE_BASE_2_2(self, endpoint=1): + cluster = self.test_info.cluster + attributes = cluster.Attributes + commands = cluster.Commands + + self.init_test() + + asserts.assert_true('PIXIT.WAITTIME.COUNTDOWN' in self.matter_test_config.global_test_params, + "PIXIT.WAITTIME.COUNTDOWN must be included on the command line in " + "the --int-arg flag as PIXIT.WAITTIME.COUNTDOWN:") + + wait_time = self.matter_test_config.global_test_params['PIXIT.WAITTIME.COUNTDOWN'] + + if wait_time == 0: + asserts.fail("PIXIT.WAITTIME.COUNTDOWN shall be higher than 0.") + + # STEP 1: Commission DUT to TH (can be skipped if done in a preceding test) + self.step(1) + + # STEP 2: Manually put the DUT into a state wherein it can receive a Start Command + self.step(2) + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="OnFault", + param=cluster.Enums.ErrorStateEnum.kNoError) + + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="Stop") + + # STEP 3: TH reads from the DUT the OperationalStateList attribute + self.step(3) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0003")): + operational_state_list = await self.read_expect_success(endpoint=endpoint, + attribute=attributes.OperationalStateList) + + operational_state_list_ids = [op_state.operationalStateID for op_state in operational_state_list] + + defined_states = [state.value for state in cluster.Enums.OperationalStateEnum + if state != cluster.Enums.OperationalStateEnum.kUnknownEnumValue] + + for state in defined_states: + if state not in operational_state_list_ids: + asserts.fail(f"The list shall include structs with the following OperationalStateIds: {defined_states}") + + # STEP 4: TH sends Start command to the DUT + self.step(4) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C02.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Start(), + expected_response=cluster.Enums.ErrorStateEnum.kNoError) + + # STEP 5: TH reads from the DUT the OperationalState attribute + self.step(5) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0004")): + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.OperationalState, + expected_value=cluster.Enums.OperationalStateEnum.kRunning) + + # STEP 6: TH reads from the DUT the OperationalError attribute + self.step(6) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0005")): + await self.read_and_expect_property_value(endpoint=endpoint, + attribute=attributes.OperationalError, + attr_property="errorStateID", + expected_value=cluster.Enums.ErrorStateEnum.kNoError) + + # STEP 7: TH reads from the DUT the CountdownTime attribute + self.step(7) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0002")): + initial_countdown_time = await self.read_expect_success(endpoint=endpoint, + attribute=attributes.CountdownTime) + if initial_countdown_time is not NullValue: + asserts.assert_true(0 <= initial_countdown_time <= 259200, + f"CountdownTime({initial_countdown_time}) must be between 0 and 259200") + + # STEP 8: TH reads from the DUT the PhaseList attribute + self.step(8) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0000")): + phase_list = await self.read_expect_success(endpoint=endpoint, + attribute=attributes.PhaseList) + phase_list_len = 0 + if phase_list is not NullValue: + phase_list_len = len(phase_list) + asserts.assert_less_equal(phase_list_len, 32, + f"PhaseList length({phase_list_len}) must be less than 32!") + + # STEP 9: TH reads from the DUT the CurrentPhase attribute + self.step(9) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0001")): + current_phase = await self.read_expect_success(endpoint=endpoint, + attribute=attributes.CurrentPhase) + if (phase_list == NullValue) or (not phase_list): + asserts.assert_equal(current_phase, NullValue, f"CurrentPhase({current_phase}) should be null") + else: + asserts.assert_less_equal(0, current_phase, + f"CurrentPhase({current_phase}) must be greater or equal than 0") + asserts.assert_less(current_phase < phase_list_len, + f"CurrentPhase({current_phase}) must be less then {(phase_list_len - 1)}") + + # STEP 10: TH waits for {PIXIT.WAITTIME.COUNTDOWN} + self.step(10) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0002")): + time.sleep(wait_time) + + # STEP 11: TH reads from the DUT the CountdownTime attribute + self.step(11) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0002")): + countdown_time = await self.read_expect_success(endpoint=endpoint, + attribute=attributes.CountdownTime) + + if (countdown_time is not NullValue) and (initial_countdown_time is not NullValue): + asserts.assert_less_equal(countdown_time, (initial_countdown_time - wait_time), + f"The countdown time shall have decreased at least {wait_time:.1f} since start command") + + # STEP 12: TH sends Start command to the DUT + self.step(12) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C02.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Start(), + expected_response=cluster.Enums.ErrorStateEnum.kNoError) + + # STEP 13: TH sends Stop command to the DUT + self.step(13) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C01.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Stop(), + expected_response=cluster.Enums.ErrorStateEnum.kNoError) + + # STEP 14: TH reads from the DUT the OperationalState attribute + self.step(14) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0004")): + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.OperationalState, + expected_value=cluster.Enums.OperationalStateEnum.kStopped) + + # STEP 15: TH sends Stop command to the DUT + self.step(15) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C01.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Stop(), + expected_response=cluster.Enums.ErrorStateEnum.kNoError) + + # STEP 16: Manually put the DUT into a state wherein it cannot receive a Start Command + self.step(16) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ERR_UNABLE_TO_START_OR_RESUME")): + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="OnFault", + param=cluster.Enums.ErrorStateEnum.kUnableToCompleteOperation) + + # STEP 17: TH sends Start command to the DUT + self.step(17) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.M.ERR_UNABLE_TO_START_OR_RESUME") and + self.check_pics(f"{self.test_info.pics_code}.S.C02.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Start(), + expected_response=cluster.Enums.ErrorStateEnum.kUnableToStartOrResume) + + ############################ + # TEST CASE 2.3 + ############################ + def STEPS_TC_OPSTATE_BASE_2_3(self) -> list[TestStep]: + steps = [TestStep(1, "Commissioning, already done", is_commissioning=True), + TestStep(2, "Manually put the DUT into a state wherein it can receive a Pause Command"), + TestStep(3, "TH reads from the DUT the OperationalStateList attribute"), + TestStep(4, "TH sends Pause command to the DUT"), + TestStep(5, "TH reads from the DUT the OperationalState attribute"), + TestStep(6, "TH reads from the DUT the CountdownTime attribute"), + TestStep(7, "TH waits for {PIXIT.WAITTIME.COUNTDOWN}"), + TestStep(8, "TH reads from the DUT the CountdownTime attribute"), + TestStep(9, "TH sends Pause command to the DUT"), + TestStep(10, "TH sends Resume command to the DUT"), + TestStep(11, "TH reads from the DUT the OperationalState attribute"), + TestStep(12, "TH sends Resume command to the DUT"), + TestStep(13, "Manually put the device in the Stopped(0x00) operational state"), + TestStep(14, "TH sends Pause command to the DUT"), + TestStep(15, "TH sends Resume command to the DUT"), + TestStep(16, "Manually put the device in the Error(0x03) operational state"), + TestStep(17, "TH sends Pause command to the DUT"), + TestStep(18, "TH sends Resume command to the DUT") + ] + return steps + + async def TEST_TC_OPSTATE_BASE_2_3(self, endpoint=1): + cluster = self.test_info.cluster + attributes = cluster.Attributes + commands = cluster.Commands + + self.init_test() + + asserts.assert_true('PIXIT.WAITTIME.COUNTDOWN' in self.matter_test_config.global_test_params, + "PIXIT.WAITTIME.COUNTDOWN must be included on the command line in " + "the --int-arg flag as PIXIT.WAITTIME.COUNTDOWN:") + + wait_time = self.matter_test_config.global_test_params['PIXIT.WAITTIME.COUNTDOWN'] + + if wait_time == 0: + asserts.fail("PIXIT.WAITTIME.COUNTDOWN shall be higher than 0.") + + # STEP 1: Commission DUT to TH (can be skipped if done in a preceding test) + self.step(1) + + # STEP 2: Manually put the DUT into a state wherein it can receive a Pause Command + self.step(2) + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="OnFault", + param=cluster.Enums.ErrorStateEnum.kNoError) + + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="Start") + + # STEP 3: TH reads from the DUT the OperationalStateList attribute + self.step(3) + if self.pics_guard(self.check_pics((f"{self.test_info.pics_code}.S.A0003"))): + operational_state_list = await self.read_expect_success(endpoint=endpoint, + attribute=attributes.OperationalStateList) + + operational_state_list_ids = [op_state.operationalStateID for op_state in operational_state_list] + + defined_states = [state.value for state in cluster.Enums.OperationalStateEnum + if state != cluster.Enums.OperationalStateEnum.kUnknownEnumValue] + + for state in defined_states: + if state not in operational_state_list_ids: + asserts.fail(f"The list shall include structs with the following OperationalStateIds: {defined_states}") + + # STEP 4: TH sends Pause command to the DUT + self.step(4) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C00.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Pause(), + expected_response=cluster.Enums.ErrorStateEnum.kNoError) + + # STEP 5: TH reads from the DUT the OperationalState attribute + self.step(5) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0004")): + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.OperationalState, + expected_value=cluster.Enums.OperationalStateEnum.kPaused) + + # STEP 6: TH reads from the DUT the CountdownTime attribute + self.step(6) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0002")): + initial_countdown_time = await self.read_expect_success(endpoint=endpoint, + attribute=attributes.CountdownTime) + if initial_countdown_time is not NullValue: + asserts.assert_true(0 <= initial_countdown_time <= 259200, + f"CountdownTime({initial_countdown_time}) must be between 0 and 259200") + + # STEP 7: TH waits for {PIXIT.WAITTIME.COUNTDOWN} + self.step(7) + time.sleep(wait_time) + + # STEP 8: TH reads from the DUT the CountdownTime attribute + self.step(8) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0002")): + countdown_time = await self.read_expect_success(endpoint=endpoint, + attribute=attributes.CountdownTime) + + if (countdown_time is not NullValue) and (initial_countdown_time is not NullValue): + asserts.assert_equal(countdown_time, initial_countdown_time, + "The countdown time shall be equal since pause command") + + # STEP 9: TH sends Pause command to the DUT + self.step(9) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C00.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Pause(), + expected_response=cluster.Enums.ErrorStateEnum.kNoError) + + # STEP 10: TH sends Resume command to the DUT + self.step(10) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C03.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Resume(), + expected_response=cluster.Enums.ErrorStateEnum.kNoError) + + # STEP 11: TH reads from the DUT the OperationalState attribute + self.step(11) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0004")): + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.OperationalState, + expected_value=cluster.Enums.OperationalStateEnum.kRunning) + + # STEP 12: TH sends Resume command to the DUT + self.step(12) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C03.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Resume(), + expected_response=cluster.Enums.ErrorStateEnum.kNoError) + + # STEP 13: Manually put the device in the Stopped(0x00) operational state + self.step(13) + if self.pics_guard(self.check_pics((f"{self.test_info.pics_code}.S.M.ST_STOPPED"))): + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="Stop") + + # STEP 14: TH sends Pause command to the DUT + self.step(14) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C00.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Pause(), + expected_response=cluster.Enums.ErrorStateEnum.kCommandInvalidInState) + + # STEP 15: TH sends Resume command to the DUT + self.step(15) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C03.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Resume(), + expected_response=cluster.Enums.ErrorStateEnum.kCommandInvalidInState) + + # STEP 16: Manually put the device in the Error(0x03) operational state + self.step(16) + if self.pics_guard(self.check_pics((f"{self.test_info.pics_code}.S.M.ST_ERROR"))): + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="OnFault", + param=cluster.Enums.ErrorStateEnum.kUnableToStartOrResume) + + # STEP 17: TH sends Pause command to the DUT + self.step(17) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C00.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Pause(), + expected_response=cluster.Enums.ErrorStateEnum.kCommandInvalidInState) + + # STEP 18: TH sends Resume command to the DUT + self.step(18) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C03.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Resume(), + expected_response=cluster.Enums.ErrorStateEnum.kCommandInvalidInState) + + ############################ + # TEST CASE 2.4 + ############################ + def STEPS_TC_OPSTATE_BASE_2_4(self) -> list[TestStep]: + steps = [TestStep(1, "Commissioning, already done", is_commissioning=True), + TestStep(2, "Set up a subscription to the OperationalError event"), + TestStep(3, "At the DUT take the vendor defined action to generate an OperationalError event"), + TestStep(4, "TH reads from the DUT the OperationalState attribute") + ] + return steps + + async def TEST_TC_OPSTATE_BASE_2_4(self, endpoint=1): + cluster = self.test_info.cluster + attributes = cluster.Attributes + events = cluster.Events + + self.init_test() + + pixit_var_name = f'PIXIT.{self.test_info.pics_code}.ErrorEventGen' + print(pixit_var_name in self.matter_test_config.global_test_params) + asserts.assert_true(pixit_var_name in self.matter_test_config.global_test_params, + f"{pixit_var_name} must be included on the command line in the --int-arg flag as {pixit_var_name}:<0 or 1>") + + error_event_gen = self.matter_test_config.global_test_params[pixit_var_name] + + # STEP 1: Commission DUT to TH (can be skipped if done in a preceding test) + self.step(1) + + if self.pics_guard(error_event_gen): + # STEP 2: Set up a subscription to the OperationalError event + self.step(2) + # Subscribe to Events and when they are sent push them to a queue for checking later + events_callback = EventChangeCallback(cluster) + await events_callback.start(self.default_controller, + self.dut_node_id, + endpoint) + + # STEP 3: At the DUT take the vendor defined action to generate an OperationalError event + self.step(3) + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="OnFault", + param=cluster.Enums.ErrorStateEnum.kUnableToStartOrResume) + event_data = events_callback.wait_for_event_report(events.OperationalError).errorState + + # Defined Errors + defined_errors = [error.value for error in cluster.Enums.ErrorStateEnum + if (error != cluster.Enums.ErrorStateEnum.kUnknownEnumValue or + error != cluster.Enums.ErrorStateEnum.kNoError)] + + in_range = (0x80 <= event_data.errorStateID <= 0xBF) + asserts.assert_true(event_data.errorStateID in defined_errors + or in_range, "Event has an invalid ID value!") + if in_range: + asserts.assert_true(event_data.errorStateLabel is not None, "ErrorStateLabel should be populated") + + # STEP 4: TH reads from the DUT the OperationalState attribute + self.step(4) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0004")): + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.OperationalState, + expected_value=cluster.Enums.OperationalStateEnum.kError) + else: + self.skip_step(2) + self.skip_step(3) + self.skip_step(4) + + ############################ + # TEST CASE 2.5 + ############################ + def STEPS_TC_OPSTATE_BASE_2_5(self) -> list[TestStep]: + steps = [TestStep(1, "Commissioning, already done", is_commissioning=True), + TestStep(2, "Set up a subscription to the OperationCompletion event"), + TestStep(3, "Manually put the DUT into a state wherein it can receive a Start Command"), + TestStep(4, "TH sends Start command to the DUT"), + TestStep(5, "TH reads from the DUT the CountdownTime attribute"), + TestStep(6, "TH reads from the DUT the OperationalState attribute"), + TestStep(7, "TH waits for initial-countdown-time"), + TestStep(8, "TH sends Stop command to the DUT"), + TestStep(9, "TH waits for OperationCompletion event"), + TestStep(10, "TH reads from the DUT the OperationalState attribute"), + TestStep(11, "Restart DUT"), + TestStep(12, "TH waits for {PIXIT.WAITTIME.REBOOT}"), + TestStep(13, "TH sends Start command to the DUT"), + TestStep(14, "TH reads from the DUT the OperationalState attribute"), + TestStep(15, "TH sends Pause command to the DUT"), + TestStep(16, "TH reads from the DUT the OperationalState attribute"), + TestStep(17, "TH waits for half of initial-countdown-time"), + TestStep(18, "TH sends Resume command to the DUT"), + TestStep(19, "TH reads from the DUT the OperationalState attribute"), + TestStep(20, "TH waits for initial-countdown-time"), + TestStep(21, "TH sends Stop command to the DUT"), + TestStep(22, "TH waits for OperationCompletion event") + ] + return steps + + async def TEST_TC_OPSTATE_BASE_2_5(self, endpoint=1): + cluster = self.test_info.cluster + attributes = cluster.Attributes + commands = cluster.Commands + events = cluster.Events + + self.init_test() + + asserts.assert_true('PIXIT.WAITTIME.REBOOT' in self.matter_test_config.global_test_params, + "PIXIT.WAITTIME.REBOOT must be included on the command line in " + "the --int-arg flag as PIXIT.WAITTIME.REBOOT:") + + wait_time_reboot = self.matter_test_config.global_test_params['PIXIT.WAITTIME.REBOOT'] + + if wait_time_reboot == 0: + asserts.fail("PIXIT.WAITTIME.REBOOT shall be higher than 0.") + + # STEP 1: Commission DUT to TH (can be skipped if done in a preceding test) + self.step(1) + + # STEP 2: Set up a subscription to the OperationCompletion event + self.step(2) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.E01")): + # Subscribe to Events and when they are sent push them to a queue for checking later + events_callback = EventSpecificChangeCallback(events.OperationCompletion) + await events_callback.start(self.default_controller, + self.dut_node_id, + endpoint) + + # STEP 3: Manually put the DUT into a state wherein it can receive a Start Command + self.step(3) + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="OnFault", + param=cluster.Enums.ErrorStateEnum.kNoError) + + self.send_manual_or_pipe_command(name="OperationalStateChange", + device=self.device, + operation="Stop") + + # STEP 4: TH sends Start command to the DUT + self.step(4) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C02.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Start(), + expected_response=cluster.Enums.ErrorStateEnum.kNoError) + + # STEP 5: TH reads from the DUT the CountdownTime attribute + self.step(5) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0002")): + initial_countdown_time = await self.read_expect_success(endpoint=endpoint, + attribute=attributes.CountdownTime) + + if initial_countdown_time is not NullValue: + # STEP 6: TH reads from the DUT the OperationalState attribute + self.step(6) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0004")): + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.OperationalState, + expected_value=cluster.Enums.OperationalStateEnum.kRunning) + + # STEP 7: TH waits for initial-countdown-time + self.step(7) + time.sleep(initial_countdown_time) + + # STEP 8: TH sends Stop command to the DUT + self.step(8) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C01.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Stop(), + expected_response=cluster.Enums.ErrorStateEnum.kNoError) + + # STEP 9: TH waits for OperationCompletion event + self.step(9) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.E01")): + event_data = events_callback.wait_for_event_report() + + asserts.assert_equal(event_data.completionErrorCode, cluster.Enums.ErrorStateEnum.kNoError, + f"Completion event error code mismatched from expectation on endpoint {endpoint}.") + + if event_data.totalOperationalTime is not NullValue: + asserts.assert_less_equal(initial_countdown_time, event_data.totalOperationalTime, + f"The total operation time shall be at least {initial_countdown_time:.1f}") + + asserts.assert_equal(0, event_data.pausedTime, + f"Paused time ({event_data.pausedTime}) shall be zero") + + # STEP 10: TH reads from the DUT the OperationalState attribute + self.step(10) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0004")): + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.OperationalState, + expected_value=cluster.Enums.OperationalStateEnum.kStopped) + + # STEP 11: Restart DUT + self.step(11) + # In CI environment, the STOP coommand (step 8) already resets the variables. Only ask for + # reboot outside CI environment. + if not self.is_ci: + input("Press Enter when done.\n") + + # STEP 12: TH waits for {PIXIT.WAITTIME.REBOOT} + self.step(12) + time.sleep(wait_time_reboot) + + # STEP 13: TH sends Start command to the DUT + self.step(13) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C02.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Start(), + expected_response=cluster.Enums.ErrorStateEnum.kNoError) + + # STEP 14: TH reads from the DUT the OperationalState attribute + self.step(14) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0004")): + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.OperationalState, + expected_value=cluster.Enums.OperationalStateEnum.kRunning) + + # STEP 15: TH sends Pause command to the DUT + self.step(15) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C00.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Pause(), + expected_response=cluster.Enums.ErrorStateEnum.kNoError) + + # STEP 16: TH reads from the DUT the OperationalState attribute + self.step(16) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0004")): + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.OperationalState, + expected_value=cluster.Enums.OperationalStateEnum.kPaused) + + # STEP 17: TH waits for half of initial-countdown-time + self.step(17) + time.sleep((initial_countdown_time / 2)) + + # STEP 18: TH sends Resume command to the DUT + self.step(18) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C03.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Resume(), + expected_response=cluster.Enums.ErrorStateEnum.kNoError) + + # STEP 19: TH reads from the DUT the OperationalState attribute + self.step(19) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.A0004")): + await self.read_and_expect_value(endpoint=endpoint, + attribute=attributes.OperationalState, + expected_value=cluster.Enums.OperationalStateEnum.kRunning) + + # STEP 20: TH waits for initial-countdown-time + self.step(20) + time.sleep(initial_countdown_time) + + # STEP 21: TH sends Stop command to the DUT + self.step(21) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.C01.Rsp") and + self.check_pics(f"{self.test_info.pics_code}.S.C04.Tx")): + await self.send_cmd_expect_response(endpoint=endpoint, + cmd=commands.Stop(), + expected_response=cluster.Enums.ErrorStateEnum.kNoError) + + # STEP 22: TH waits for OperationCompletion event + self.step(22) + if self.pics_guard(self.check_pics(f"{self.test_info.pics_code}.S.E01")): + event_data = events_callback.wait_for_event_report() + + asserts.assert_equal(event_data.completionErrorCode, cluster.Enums.ErrorStateEnum.kNoError, + f"Completion event error code mismatched from expectation on endpoint {endpoint}.") + + if event_data.totalOperationalTime is not NullValue: + expected_value = (1.5 * initial_countdown_time) + + asserts.assert_less_equal(expected_value, event_data.totalOperationalTime, + f"The total operation time shall be at least {expected_value:.1f}") + + expected_value = (0.5 * initial_countdown_time) + asserts.assert_less_equal(expected_value, event_data.pausedTime, + f"Paused time ({event_data.pausedTime}) shall be at least {expected_value:.1f}") + else: + self.skip_step(6) + self.skip_step(7) + self.skip_step(8) + self.skip_step(9) + self.skip_step(10) + self.skip_step(11) + self.skip_step(12) + self.skip_step(13) + self.skip_step(14) + self.skip_step(15) + self.skip_step(16) + self.skip_step(17) + self.skip_step(18) + self.skip_step(19) + self.skip_step(20) + self.skip_step(21) + self.skip_step(22)