From 1550556f871ce14cf217de8cdf436f4b965d75ff Mon Sep 17 00:00:00 2001 From: Ricardo Casallas <77841255+rcasallas-silabs@users.noreply.github.com> Date: Tue, 6 Jun 2023 17:32:07 -0400 Subject: [PATCH] ICD Management cluster server. (#26809) --- .github/workflows/examples-nrfconnect.yaml | 2 +- .../all-clusters-app.matter | 2 +- .../all-clusters-common/all-clusters-app.zap | 5 +- .../templates/tests/ciTests.json | 2 +- scripts/tests/chiptest/__init__.py | 1 - src/app/chip_data_model.cmake | 4 +- src/app/chip_data_model.gni | 4 +- .../icd-management-server.cpp | 194 ++++++- src/app/tests/BUILD.gn | 32 +- .../TestClientMonitoringRegistrationTable.cpp | 162 ------ src/app/tests/TestIcdMonitoringTable.cpp | 372 +++++++++++++ .../suites/TestClientMonitoringCluster.yaml | 102 ---- .../suites/TestIcdManagementCluster.yaml | 304 ++++++++++ src/app/tests/suites/ciTests.json | 1 + .../ClientMonitoringRegistrationTable.cpp | 83 --- .../util/ClientMonitoringRegistrationTable.h | 102 ---- src/app/util/IcdMonitoringTable.cpp | 111 ++++ src/app/util/IcdMonitoringTable.h | 124 +++++ src/controller/data_model/BUILD.gn | 5 - src/lib/core/CHIPConfig.h | 10 - src/lib/support/DefaultStorageKeyAllocator.h | 6 +- src/lib/support/PersistentData.h | 33 +- .../chip-tool/zap-generated/test/Commands.h | 526 ++++++++++++++++++ 23 files changed, 1650 insertions(+), 537 deletions(-) delete mode 100644 src/app/tests/TestClientMonitoringRegistrationTable.cpp create mode 100644 src/app/tests/TestIcdMonitoringTable.cpp delete mode 100644 src/app/tests/suites/TestClientMonitoringCluster.yaml create mode 100644 src/app/tests/suites/TestIcdManagementCluster.yaml delete mode 100644 src/app/util/ClientMonitoringRegistrationTable.cpp delete mode 100644 src/app/util/ClientMonitoringRegistrationTable.h create mode 100644 src/app/util/IcdMonitoringTable.cpp create mode 100644 src/app/util/IcdMonitoringTable.h diff --git a/.github/workflows/examples-nrfconnect.yaml b/.github/workflows/examples-nrfconnect.yaml index caba78329e53c6..cb1c94f5915eae 100644 --- a/.github/workflows/examples-nrfconnect.yaml +++ b/.github/workflows/examples-nrfconnect.yaml @@ -240,7 +240,7 @@ jobs: - name: Build example nRF Connect SDK All Clusters App on nRF7002 PDK timeout-minutes: 20 run: | - scripts/examples/nrfconnect_example.sh all-clusters-app nrf7002dk_nrf5340_cpuapp + scripts/examples/nrfconnect_example.sh all-clusters-app nrf7002dk_nrf5340_cpuapp -DCONF_FILE=prj_release.conf .environment/pigweed-venv/bin/python3 scripts/tools/memory/gh_sizes.py \ nrfconnect nrf7002dk_nrf5340_cpuapp all-clusters-app \ examples/all-clusters-app/nrfconnect/build/zephyr/zephyr.elf \ 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 198775a942fd49..578f9b492b60e7 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 @@ -5934,7 +5934,7 @@ endpoint 0 { ram attribute activeModeThreshold default = 300; callback attribute registeredClients; ram attribute ICDCounter default = 0; - ram attribute clientsSupportedPerFabric default = 1; + ram attribute clientsSupportedPerFabric default = 2; callback attribute generatedCommandList; callback attribute acceptedCommandList; callback attribute eventList; 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 2f71043449420b..253bca0a74f5b0 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 @@ -6432,7 +6432,7 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "1", + "defaultValue": "2", "reportable": 1, "minInterval": 1, "maxInterval": 65534, @@ -31934,5 +31934,6 @@ "endpointVersion": 1, "deviceIdentifier": 61442 } - ] + ], + "log": [] } \ No newline at end of file diff --git a/examples/darwin-framework-tool/templates/tests/ciTests.json b/examples/darwin-framework-tool/templates/tests/ciTests.json index d96481371393f7..109ffe2c10e07c 100644 --- a/examples/darwin-framework-tool/templates/tests/ciTests.json +++ b/examples/darwin-framework-tool/templates/tests/ciTests.json @@ -1,7 +1,7 @@ { "include": "../../../../src/app/tests/suites/ciTests.json", "disable": [ - "TestClientMonitoringCluster", + "TestIcdManagementCluster", "Test_TC_SC_4_2", "Test_TC_SC_5_2", "TestClusterComplexTypes", diff --git a/scripts/tests/chiptest/__init__.py b/scripts/tests/chiptest/__init__.py index 143fd7ec477dac..945d5ddbb39019 100644 --- a/scripts/tests/chiptest/__init__.py +++ b/scripts/tests/chiptest/__init__.py @@ -133,7 +133,6 @@ def _GetInDevelopmentTests() -> Set[str]: "Test_AddNewFabricFromExistingFabric.yaml", # chip-repl does not support GetCommissionerRootCertificate and IssueNocChain command "TestEqualities.yaml", # chip-repl does not support pseudo-cluster commands that return a value "TestExampleCluster.yaml", # chip-repl does not load custom pseudo clusters - "TestClientMonitoringCluster.yaml", # Client Monitoring Tests need a rework after the XML update "Test_TC_TIMESYNC_1_1.yaml", # Time sync SDK is not yet ready "TestAttributesById.yaml", # chip-repl does not support AnyCommands (06/06/2023) "TestCommandsById.yaml", # chip-repl does not support AnyCommands (06/06/2023) diff --git a/src/app/chip_data_model.cmake b/src/app/chip_data_model.cmake index 80ad1ded379b0c..2b61dded83b576 100644 --- a/src/app/chip_data_model.cmake +++ b/src/app/chip_data_model.cmake @@ -133,9 +133,7 @@ function(chip_configure_data_model APP_TARGET) ${CHIP_APP_BASE_DIR}/util/attribute-storage.cpp ${CHIP_APP_BASE_DIR}/util/attribute-table.cpp ${CHIP_APP_BASE_DIR}/util/binding-table.cpp - # Disable CM cluster table tests until update is done - # https://github.com/project-chip/connectedhomeip/issues/24425 - # ${CHIP_APP_BASE_DIR}/util/ClientMonitoringRegistrationTable.cpp + ${CHIP_APP_BASE_DIR}/util/IcdMonitoringTable.cpp ${CHIP_APP_BASE_DIR}/util/DataModelHandler.cpp ${CHIP_APP_BASE_DIR}/util/ember-compatibility-functions.cpp ${CHIP_APP_BASE_DIR}/util/error-mapping.cpp diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 48dfa7da9428b2..7d90aab90698eb 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -164,9 +164,9 @@ template("chip_data_model") { "${_app_root}/clusters/scenes-server/SceneTable.h", "${_app_root}/clusters/scenes-server/SceneTableImpl.h", "${_app_root}/clusters/scenes-server/scenes-server.h", - "${_app_root}/util/ClientMonitoringRegistrationTable.cpp", - "${_app_root}/util/ClientMonitoringRegistrationTable.h", "${_app_root}/util/DataModelHandler.cpp", + "${_app_root}/util/IcdMonitoringTable.cpp", + "${_app_root}/util/IcdMonitoringTable.h", "${_app_root}/util/attribute-size-util.cpp", "${_app_root}/util/attribute-storage.cpp", "${_app_root}/util/attribute-table.cpp", diff --git a/src/app/clusters/icd-management-server/icd-management-server.cpp b/src/app/clusters/icd-management-server/icd-management-server.cpp index 0855514da8449a..8062aa7e06dab2 100644 --- a/src/app/clusters/icd-management-server/icd-management-server.cpp +++ b/src/app/clusters/icd-management-server/icd-management-server.cpp @@ -18,12 +18,15 @@ #include "icd-management-server.h" #include "app/server/Server.h" +#include +#include #include #include #include #include #include #include +#include #include #include @@ -32,8 +35,7 @@ using namespace chip::app; using namespace chip::app::Clusters; using namespace chip::app::Clusters::IcdManagement; using namespace Protocols; - -//============================================================================== +using namespace chip::Access; namespace { @@ -69,61 +71,199 @@ CHIP_ERROR IcdManagementAttributeAccess::Read(const ConcreteReadAttributePath & CHIP_ERROR IcdManagementAttributeAccess::ReadRegisteredClients(EndpointId endpoint, AttributeValueEncoder & encoder) { - return encoder.EncodeEmptyList(); + uint16_t supported_clients = 0; + VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == Attributes::ClientsSupportedPerFabric::Get(endpoint, &supported_clients), + CHIP_ERROR_NOT_FOUND); + + return encoder.EncodeList([supported_clients](const auto & subEncoder) -> CHIP_ERROR { + IcdMonitoringEntry e; + + const auto & fabricTable = Server::GetInstance().GetFabricTable(); + for (const auto & fabricInfo : fabricTable) + { + PersistentStorageDelegate & storage = chip::Server::GetInstance().GetPersistentStorage(); + IcdMonitoringTable table(storage, fabricInfo.GetFabricIndex(), supported_clients); + for (uint16_t i = 0; i < table.Limit(); ++i) + { + CHIP_ERROR err = table.Get(i, e); + if (CHIP_ERROR_NOT_FOUND == err) + { + // No more entries in the table + break; + } + ReturnErrorOnFailure(err); + ReturnErrorOnFailure(subEncoder.Encode(e)); + } + } + return CHIP_NO_ERROR; + }); +} + +CHIP_ERROR CheckAdmin(chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, bool & is_admin) +{ + RequestPath requestPath{ .cluster = commandPath.mClusterId, .endpoint = commandPath.mEndpointId }; + CHIP_ERROR err = GetAccessControl().Check(commandObj->GetSubjectDescriptor(), requestPath, Privilege::kAdminister); + if (CHIP_NO_ERROR == err) + { + is_admin = true; + } + else if (CHIP_ERROR_ACCESS_DENIED == err) + { + is_admin = false; + err = CHIP_NO_ERROR; + } + return err; } +class IcdManagementFabricDelegate : public chip::FabricTable::Delegate +{ + void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override + { + uint16_t supported_clients = 0; + if (EMBER_ZCL_STATUS_SUCCESS != Attributes::ClientsSupportedPerFabric::Get(kRootEndpointId, &supported_clients)) + { + // Fallback to maximum, the remove function will loop until no more entries are found. + supported_clients = UINT16_MAX; + } + IcdMonitoringTable table(chip::Server::GetInstance().GetPersistentStorage(), fabricIndex, supported_clients); + table.RemoveAll(); + } +}; + +IcdManagementFabricDelegate gFabricDelegate; IcdManagementAttributeAccess gAttribute; } // namespace -//============================================================================== - -InteractionModel::Status -IcdManagementServer::RegisterClient(chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::IcdManagement::Commands::RegisterClient::DecodableType & commandData) +InteractionModel::Status IcdManagementServer::RegisterClient(chip::app::CommandHandler * commandObj, + const chip::app::ConcreteCommandPath & commandPath, + const Commands::RegisterClient::DecodableType & commandData) { - // TODO: Implementent logic for end device - return InteractionModel::Status::UnsupportedCommand; + uint16_t supported_clients = 0; + VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == + Attributes::ClientsSupportedPerFabric::Get(commandPath.mEndpointId, &supported_clients), + InteractionModel::Status::Failure); + IcdMonitoringTable table(chip::Server::GetInstance().GetPersistentStorage(), commandObj->GetAccessingFabricIndex(), + supported_clients); + + // Get current entry, if exists + IcdMonitoringEntry entry; + CHIP_ERROR err = table.Find(commandData.checkInNodeID, entry); + if (CHIP_NO_ERROR == err) + { + // Existing entry: Validate Key if, and only if, the ISD has NOT administrator permissions + bool is_admin = false; + err = CheckAdmin(commandObj, commandPath, is_admin); + VerifyOrReturnError(CHIP_NO_ERROR == err, InteractionModel::Status::Failure); + if (!is_admin) + { + VerifyOrReturnError(commandData.verificationKey.HasValue(), InteractionModel::Status::Failure); + VerifyOrReturnError(commandData.verificationKey.Value().data_equal(entry.key), InteractionModel::Status::Failure); + } + } + else if (CHIP_ERROR_NOT_FOUND == err) + { + // New entry + VerifyOrReturnError(entry.index < table.Limit(), InteractionModel::Status::ResourceExhausted); + } + else + { + // Error + return InteractionModel::Status::Failure; + } + + // Save + entry.checkInNodeID = commandData.checkInNodeID; + entry.monitoredSubject = commandData.monitoredSubject; + entry.key = commandData.key; + err = table.Set(entry.index, entry); + VerifyOrReturnError(CHIP_ERROR_INVALID_ARGUMENT != err, InteractionModel::Status::ConstraintError); + VerifyOrReturnError(CHIP_NO_ERROR == err, InteractionModel::Status::Failure); + + return InteractionModel::Status::Success; } -InteractionModel::Status IcdManagementServer::UnregisterClient( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::IcdManagement::Commands::UnregisterClient::DecodableType & commandData) +InteractionModel::Status IcdManagementServer::UnregisterClient(chip::app::CommandHandler * commandObj, + const chip::app::ConcreteCommandPath & commandPath, + const Commands::UnregisterClient::DecodableType & commandData) { - // TODO: Implementent logic for end device - return InteractionModel::Status::UnsupportedCommand; + uint16_t supported_clients = 0; + VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == + Attributes::ClientsSupportedPerFabric::Get(commandPath.mEndpointId, &supported_clients), + InteractionModel::Status::Failure); + IcdMonitoringTable table(chip::Server::GetInstance().GetPersistentStorage(), commandObj->GetAccessingFabricIndex(), + supported_clients); + + // Get current entry, if exists + IcdMonitoringEntry entry; + CHIP_ERROR err = table.Find(commandData.checkInNodeID, entry); + VerifyOrReturnError(CHIP_ERROR_NOT_FOUND != err, InteractionModel::Status::NotFound); + VerifyOrReturnError(CHIP_NO_ERROR == err, InteractionModel::Status::Failure); + + // Existing entry: Validate Key if, and only if, the ISD has NOT administrator permissions + bool is_admin = false; + err = CheckAdmin(commandObj, commandPath, is_admin); + VerifyOrReturnError(CHIP_NO_ERROR == err, InteractionModel::Status::Failure); + + if (!is_admin) + { + VerifyOrReturnError(commandData.key.HasValue(), InteractionModel::Status::Failure); + VerifyOrReturnError(commandData.key.Value().data_equal(entry.key), InteractionModel::Status::Failure); + } + + err = table.Remove(entry.index); + VerifyOrReturnError(CHIP_NO_ERROR == err, InteractionModel::Status::Failure); + + return InteractionModel::Status::Success; } InteractionModel::Status IcdManagementServer::StayActiveRequest(const chip::app::ConcreteCommandPath & commandPath) { - // TODO: Implementent logic for end device + + // TODO: Implementent stay awake logic for end device return InteractionModel::Status::UnsupportedCommand; } -//============================================================================== +void emberAfIcdManagementClusterInitCallback() +{ + Server::GetInstance().GetFabricTable().AddFabricDelegate(&gFabricDelegate); +} /** * @brief ICD Management Cluster RegisterClient Command callback (from client) * */ -bool emberAfIcdManagementClusterRegisterClientCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::IcdManagement::Commands::RegisterClient::DecodableType & commandData) +bool emberAfIcdManagementClusterRegisterClientCallback(chip::app::CommandHandler * commandObj, + const chip::app::ConcreteCommandPath & commandPath, + const Commands::RegisterClient::DecodableType & commandData) { IcdManagementServer server; + InteractionModel::Status status = server.RegisterClient(commandObj, commandPath, commandData); + if (InteractionModel::Status::Success == status) + { + // Response + IcdManagement::Commands::RegisterClientResponse::Type response; + if (EMBER_ZCL_STATUS_SUCCESS == Attributes::ICDCounter::Get(commandPath.mEndpointId, &response.ICDCounter)) + { + commandObj->AddResponse(commandPath, response); + return true; + } + status = InteractionModel::Status::Failure; + } + // Error commandObj->AddStatus(commandPath, status); return true; } /** - * @brief ICD Management Cluster UnregisterClient Command callback (from client) + * @brief ICD Management Cluster UregisterClient Command callback (from client) * */ -bool emberAfIcdManagementClusterUnregisterClientCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::IcdManagement::Commands::UnregisterClient::DecodableType & commandData) +bool emberAfIcdManagementClusterUnregisterClientCallback(chip::app::CommandHandler * commandObj, + const chip::app::ConcreteCommandPath & commandPath, + const Commands::UnregisterClient::DecodableType & commandData) { IcdManagementServer server; InteractionModel::Status status = server.UnregisterClient(commandObj, commandPath, commandData); @@ -135,9 +275,9 @@ bool emberAfIcdManagementClusterUnregisterClientCallback( /** * @brief ICD Management Cluster StayActiveRequest Command callback (from client) */ -bool emberAfIcdManagementClusterStayActiveRequestCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::IcdManagement::Commands::StayActiveRequest::DecodableType & commandData) +bool emberAfIcdManagementClusterStayActiveRequestCallback(chip::app::CommandHandler * commandObj, + const chip::app::ConcreteCommandPath & commandPath, + const Commands::StayActiveRequest::DecodableType & commandData) { IcdManagementServer server; InteractionModel::Status status = server.StayActiveRequest(commandPath); diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index d18188b333325d..0faf6d0b06a05c 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -54,17 +54,17 @@ source_set("binding-test-srcs") { ] } -# source_set("client-monitoring-test-srcs") { -# sources = [ -# "${chip_root}/src/app/util/ClientMonitoringRegistrationTable.cpp", -# "${chip_root}/src/app/util/ClientMonitoringRegistrationTable.h", -# ] - -# public_deps = [ -# "${chip_root}/src/app/common:cluster-objects", -# "${chip_root}/src/lib/core", -# ] -# } +source_set("icd-management-test-srcs") { + sources = [ + "${chip_root}/src/app/util/IcdMonitoringTable.cpp", + "${chip_root}/src/app/util/IcdMonitoringTable.h", + ] + + public_deps = [ + "${chip_root}/src/app/common:cluster-objects", + "${chip_root}/src/lib/core", + ] +} source_set("ota-requestor-test-srcs") { sources = [ @@ -107,10 +107,6 @@ chip_test_suite("tests") { "TestAttributeValueEncoder.cpp", "TestBindingTable.cpp", "TestBuilderParser.cpp", - - # Disable CM cluster table tests until update is done - # https://github.com/project-chip/connectedhomeip/issues/24425 - # "TestClientMonitoringRegistrationTable.cpp", "TestClusterInfo.cpp", "TestCommandInteraction.cpp", "TestCommandPathParams.cpp", @@ -122,6 +118,7 @@ chip_test_suite("tests") { "TestExtensionFieldSets.cpp", "TestFabricScopedEventLogging.cpp", "TestICDManager.cpp", + "TestIcdMonitoringTable.cpp", "TestInteractionModelEngine.cpp", "TestMessageDef.cpp", "TestNumericAttributeTraits.cpp", @@ -163,10 +160,7 @@ chip_test_suite("tests") { public_deps = [ ":binding-test-srcs", - - # Disable CM cluster table tests until update is done - #https://github.com/project-chip/connectedhomeip/issues/24425 - # ":client-monitoring-test-srcs", + ":icd-management-test-srcs", ":ota-requestor-test-srcs", ":scenes-table-test-srcs", "${chip_root}/src/app", diff --git a/src/app/tests/TestClientMonitoringRegistrationTable.cpp b/src/app/tests/TestClientMonitoringRegistrationTable.cpp deleted file mode 100644 index e6802151b7d4d6..00000000000000 --- a/src/app/tests/TestClientMonitoringRegistrationTable.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * - * Copyright (c) 2022 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. - */ - -#include -#include -#include -#include -#include -#include - -using chip::ClientMonitoringRegistrationTable; -using chip::NullOptional; - -namespace { - -constexpr chip::FabricIndex kTestFabricIndex = 1; -constexpr uint64_t kTestICid = 10; -constexpr chip::NodeId kTestClientNodeId = 11; - -constexpr chip::FabricIndex kTestFabricIndex_2 = 2; -constexpr uint64_t kTestICid_2 = 20; -constexpr chip::NodeId kTestClientNodeId_2 = 21; - -void TestDefaultClientValues(nlTestSuite * aSuite, void * aContext) -{ - chip::TestPersistentStorageDelegate testStorage; - ClientMonitoringRegistrationTable registration(testStorage); - - NL_TEST_ASSERT(aSuite, !registration.GetClientRegistrationEntry().IsValid()); - NL_TEST_ASSERT(aSuite, registration.GetClientRegistrationEntry().clientNodeId == chip::kUndefinedFabricIndex); - NL_TEST_ASSERT(aSuite, registration.GetClientRegistrationEntry().ICid == chip::kUndefinedNodeId); - NL_TEST_ASSERT(aSuite, registration.GetClientRegistrationEntry().fabricIndex == chip::kInvalidIcId); -} - -void TestLoadFromStorageEmptyValue(nlTestSuite * aSuite, void * aContext) -{ - chip::TestPersistentStorageDelegate testStorage; - ClientMonitoringRegistrationTable registration(testStorage); - - CHIP_ERROR err = registration.LoadFromStorage(kTestFabricIndex); - NL_TEST_ASSERT(aSuite, err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND); -} - -void TestSaveAndLoadRegistrationValue(nlTestSuite * aSuite, void * aContext) -{ - chip::TestPersistentStorageDelegate testStorage; - ClientMonitoringRegistrationTable savedRegistration(testStorage); - ClientMonitoringRegistrationTable loadedRegistration(testStorage); - - savedRegistration.GetClientRegistrationEntry().clientNodeId = kTestClientNodeId; - savedRegistration.GetClientRegistrationEntry().ICid = kTestICid; - savedRegistration.GetClientRegistrationEntry().fabricIndex = kTestFabricIndex; - - CHIP_ERROR err = savedRegistration.SaveToStorage(); - NL_TEST_ASSERT(aSuite, err == CHIP_NO_ERROR); - - err = loadedRegistration.LoadFromStorage(kTestFabricIndex); - NL_TEST_ASSERT(aSuite, err == CHIP_NO_ERROR); - - NL_TEST_ASSERT(aSuite, loadedRegistration.GetClientRegistrationEntry().clientNodeId == kTestClientNodeId); - NL_TEST_ASSERT(aSuite, loadedRegistration.GetClientRegistrationEntry().ICid == kTestICid); - NL_TEST_ASSERT(aSuite, loadedRegistration.GetClientRegistrationEntry().fabricIndex == kTestFabricIndex); -} - -void TestSaveLoadRegistrationValueForMultipleFabrics(nlTestSuite * aSuite, void * aContexT) -{ - chip::TestPersistentStorageDelegate testStorage; - ClientMonitoringRegistrationTable savedRegistration(testStorage); - ClientMonitoringRegistrationTable loadedRegistration(testStorage); - - savedRegistration.GetClientRegistrationEntry().clientNodeId = kTestClientNodeId; - savedRegistration.GetClientRegistrationEntry().ICid = kTestICid; - savedRegistration.GetClientRegistrationEntry().fabricIndex = kTestFabricIndex; - - CHIP_ERROR err = savedRegistration.SaveToStorage(); - NL_TEST_ASSERT(aSuite, err == CHIP_NO_ERROR); - - savedRegistration.GetClientRegistrationEntry().clientNodeId = kTestClientNodeId_2; - savedRegistration.GetClientRegistrationEntry().ICid = kTestICid_2; - savedRegistration.GetClientRegistrationEntry().fabricIndex = kTestFabricIndex_2; - - err = savedRegistration.SaveToStorage(); - NL_TEST_ASSERT(aSuite, err == CHIP_NO_ERROR); - - err = loadedRegistration.LoadFromStorage(kTestFabricIndex); - NL_TEST_ASSERT(aSuite, err == CHIP_NO_ERROR); - - NL_TEST_ASSERT(aSuite, loadedRegistration.GetClientRegistrationEntry().clientNodeId == kTestClientNodeId); - NL_TEST_ASSERT(aSuite, loadedRegistration.GetClientRegistrationEntry().ICid == kTestICid); - NL_TEST_ASSERT(aSuite, loadedRegistration.GetClientRegistrationEntry().fabricIndex == kTestFabricIndex); - - err = loadedRegistration.LoadFromStorage(kTestFabricIndex_2); - NL_TEST_ASSERT(aSuite, err == CHIP_NO_ERROR); - - NL_TEST_ASSERT(aSuite, loadedRegistration.GetClientRegistrationEntry().clientNodeId == kTestClientNodeId_2); - NL_TEST_ASSERT(aSuite, loadedRegistration.GetClientRegistrationEntry().ICid == kTestICid_2); - NL_TEST_ASSERT(aSuite, loadedRegistration.GetClientRegistrationEntry().fabricIndex == kTestFabricIndex_2); -} - -void TestSaveAllInvalidRegistrationValues(nlTestSuite * aSuite, void * context) -{ - chip::TestPersistentStorageDelegate testStorage; - ClientMonitoringRegistrationTable registration(testStorage); - - CHIP_ERROR err = registration.SaveToStorage(); - NL_TEST_ASSERT(aSuite, err == CHIP_ERROR_INCORRECT_STATE); -} - -void TestDeleteValidEntryFromStorage(nlTestSuite * aSuite, void * context) -{ - chip::TestPersistentStorageDelegate testStorage; - ClientMonitoringRegistrationTable table(testStorage); - - table.GetClientRegistrationEntry().clientNodeId = kTestClientNodeId; - table.GetClientRegistrationEntry().ICid = kTestICid; - table.GetClientRegistrationEntry().fabricIndex = kTestFabricIndex; - - CHIP_ERROR err = table.SaveToStorage(); - NL_TEST_ASSERT(aSuite, err == CHIP_NO_ERROR); - - NL_TEST_ASSERT(aSuite, table.HasValueForFabric(kTestFabricIndex)); - - err = table.DeleteFromStorage(kTestFabricIndex); - NL_TEST_ASSERT(aSuite, err == CHIP_NO_ERROR); - - NL_TEST_ASSERT(aSuite, !table.HasValueForFabric(kTestFabricIndex)); -} - -} // namespace - -int TestClientMonitoringRegistrationTable() -{ - static nlTest sTests[] = { NL_TEST_DEF("TestDefaultClientValues", TestDefaultClientValues), - NL_TEST_DEF("TestLoadFromStorageEmptyValue", TestLoadFromStorageEmptyValue), - NL_TEST_DEF("TestSaveAndLoadRegistrationValue", TestSaveAndLoadRegistrationValue), - NL_TEST_DEF("TestSaveAllInvalidRegistrationValues", TestSaveAllInvalidRegistrationValues), - NL_TEST_DEF("TestSaveLoadRegistrationValueForMultipleFabrics", - TestSaveLoadRegistrationValueForMultipleFabrics), - NL_TEST_DEF("TestDeleteValidEntryFromStorage", TestDeleteValidEntryFromStorage), - NL_TEST_SENTINEL() }; - - nlTestSuite cmSuite = { "TestClientMonitoringRegistrationTable", &sTests[0], nullptr, nullptr }; - - nlTestRunner(&cmSuite, nullptr); - return (nlTestRunnerStats(&cmSuite)); -} - -CHIP_REGISTER_TEST_SUITE(TestClientMonitoringRegistrationTable) diff --git a/src/app/tests/TestIcdMonitoringTable.cpp b/src/app/tests/TestIcdMonitoringTable.cpp new file mode 100644 index 00000000000000..e87adfa3ed84d3 --- /dev/null +++ b/src/app/tests/TestIcdMonitoringTable.cpp @@ -0,0 +1,372 @@ +/* + * + * Copyright (c) 2022 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. + */ + +#include +#include +#include +#include +#include +#include + +using namespace chip; + +namespace { + +constexpr uint16_t kMaxTestClients1 = 2; +constexpr uint16_t kMaxTestClients2 = 1; +constexpr FabricIndex kTestFabricIndex1 = 1; +constexpr FabricIndex kTestFabricIndex2 = kMaxValidFabricIndex; +constexpr uint64_t kClientNodeId11 = 0x100001; +constexpr uint64_t kClientNodeId12 = 0x100002; +constexpr uint64_t kClientNodeId13 = 0x100003; +constexpr uint64_t kClientNodeId21 = 0x200001; +constexpr uint64_t kClientNodeId22 = 0x200002; + +constexpr uint8_t kKeyBuffer0a[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +constexpr uint8_t kKeyBuffer0b[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +constexpr uint8_t kKeyBuffer1a[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f +}; +constexpr uint8_t kKeyBuffer1b[] = { + 0xf1, 0xe1, 0xd1, 0xc1, 0xb1, 0xa1, 0x91, 0x81, 0x71, 0x61, 0x51, 0x14, 0x31, 0x21, 0x11, 0x01 +}; +constexpr uint8_t kKeyBuffer2a[] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f +}; +constexpr uint8_t kKeyBuffer2b[] = { + 0xf2, 0xe2, 0xd2, 0xc2, 0xb2, 0xa2, 0x92, 0x82, 0x72, 0x62, 0x52, 0x42, 0x32, 0x22, 0x12, 0x02 +}; +constexpr uint8_t kKeyBuffer3a[] = { + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f +}; +// constexpr uint8_t kKeyBuffer3b[] = { 0xf3, 0xe3, 0xd3, 0xc3, 0xb3, 0xa3, 0x93, 0x83, 0x73, 0x63, 0x53, 0x14, 0x33, 0x23, 0x13, +// 0x03 }; + +void TestSaveAndLoadRegistrationValue(nlTestSuite * aSuite, void * aContext) +{ + TestPersistentStorageDelegate storage; + IcdMonitoringTable saving(storage, kTestFabricIndex1, kMaxTestClients1); + IcdMonitoringTable loading(storage, kTestFabricIndex1, kMaxTestClients1); + IcdMonitoringEntry entry; + CHIP_ERROR err; + + // Insert first entry + entry.checkInNodeID = kClientNodeId11; + entry.monitoredSubject = kClientNodeId12; + entry.key = ByteSpan(kKeyBuffer1a); + err = saving.Set(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + + // Insert second entry + entry.checkInNodeID = kClientNodeId12; + entry.monitoredSubject = kClientNodeId11; + entry.key = ByteSpan(kKeyBuffer2a); + err = saving.Set(1, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + + // Insert one too many + entry.checkInNodeID = kClientNodeId13; + entry.monitoredSubject = kClientNodeId13; + entry.key = ByteSpan(kKeyBuffer3a); + err = saving.Set(2, entry); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_ARGUMENT == err); + + // Retrieve first entry + err = loading.Get(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + NL_TEST_ASSERT(aSuite, kTestFabricIndex1 == entry.fabricIndex); + NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.checkInNodeID); + NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.monitoredSubject); + NL_TEST_ASSERT(aSuite, sizeof(kKeyBuffer1a) == entry.key.size()); + NL_TEST_ASSERT(aSuite, 0 == memcmp(entry.key.data(), kKeyBuffer1a, entry.key.size())); + + // Retrieve second entry + err = loading.Get(1, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + NL_TEST_ASSERT(aSuite, kTestFabricIndex1 == entry.fabricIndex); + NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.checkInNodeID); + NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.monitoredSubject); + NL_TEST_ASSERT(aSuite, sizeof(kKeyBuffer2a) == entry.key.size()); + NL_TEST_ASSERT(aSuite, 0 == memcmp(entry.key.data(), kKeyBuffer2a, entry.key.size())); + + // No more entries + err = loading.Get(2, entry); + NL_TEST_ASSERT(aSuite, 2 == loading.Limit()); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == err); + + // Overwrite first entry + entry.checkInNodeID = kClientNodeId13; + entry.monitoredSubject = kClientNodeId11; + entry.key = ByteSpan(kKeyBuffer1b); + err = saving.Set(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + + // Retrieve first entry (modified) + err = loading.Get(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + NL_TEST_ASSERT(aSuite, kTestFabricIndex1 == entry.fabricIndex); + NL_TEST_ASSERT(aSuite, kClientNodeId13 == entry.checkInNodeID); + NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.monitoredSubject); + NL_TEST_ASSERT(aSuite, sizeof(kKeyBuffer1b) == entry.key.size()); + NL_TEST_ASSERT(aSuite, 0 == memcmp(entry.key.data(), kKeyBuffer1b, entry.key.size())); + + // Retrieve second entry (not modified) + err = loading.Get(1, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + NL_TEST_ASSERT(aSuite, kTestFabricIndex1 == entry.fabricIndex); + NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.checkInNodeID); + NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.monitoredSubject); + NL_TEST_ASSERT(aSuite, sizeof(kKeyBuffer2a) == entry.key.size()); + NL_TEST_ASSERT(aSuite, 0 == memcmp(entry.key.data(), kKeyBuffer2a, entry.key.size())); +} + +void TestSaveAllInvalidRegistrationValues(nlTestSuite * aSuite, void * aContext) +{ + TestPersistentStorageDelegate storage; + IcdMonitoringTable table(storage, kTestFabricIndex1, kMaxTestClients1); + IcdMonitoringEntry entry; + CHIP_ERROR err; + + // Invalid checkInNodeID + entry.checkInNodeID = kUndefinedNodeId; + entry.monitoredSubject = kClientNodeId12; + entry.key = ByteSpan(kKeyBuffer1a); + err = table.Set(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_ARGUMENT == err); + + // Invalid monitoredSubject + entry.checkInNodeID = kClientNodeId11; + entry.monitoredSubject = kUndefinedNodeId; + entry.key = ByteSpan(kKeyBuffer1a); + err = table.Set(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_ARGUMENT == err); + + // Invalid key (empty) + entry.checkInNodeID = kClientNodeId11; + entry.monitoredSubject = kClientNodeId12; + entry.key = ByteSpan(); + err = table.Set(0, entry); + + NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_ARGUMENT == err); + // Invalid key (too short) + entry.checkInNodeID = kClientNodeId11; + entry.monitoredSubject = kClientNodeId12; + entry.key = ByteSpan(kKeyBuffer0a); + err = table.Set(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_ARGUMENT == err); + + // Invalid key (too long) + entry.checkInNodeID = kClientNodeId11; + entry.monitoredSubject = kClientNodeId12; + entry.key = ByteSpan(kKeyBuffer0b); + err = table.Set(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_ARGUMENT == err); +} + +void TestSaveLoadRegistrationValueForMultipleFabrics(nlTestSuite * aSuite, void * aContext) +{ + TestPersistentStorageDelegate storage; + IcdMonitoringTable table1(storage, kTestFabricIndex1, kMaxTestClients1); + IcdMonitoringTable table2(storage, kTestFabricIndex2, kMaxTestClients2); + IcdMonitoringEntry entry; + CHIP_ERROR err; + + // Insert in first fabric + entry.checkInNodeID = kClientNodeId11; + entry.monitoredSubject = kClientNodeId12; + entry.key = ByteSpan(kKeyBuffer1a); + err = table1.Set(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + + // Insert in first fabric + entry.checkInNodeID = kClientNodeId12; + entry.monitoredSubject = kClientNodeId11; + entry.key = ByteSpan(kKeyBuffer1b); + err = table1.Set(1, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + + // Insert in second fabric + entry.checkInNodeID = kClientNodeId21; + entry.monitoredSubject = kClientNodeId22; + entry.key = ByteSpan(kKeyBuffer2a); + err = table2.Set(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + + // Insert in second fabric (one too many) + entry.checkInNodeID = kClientNodeId22; + entry.monitoredSubject = kClientNodeId21; + entry.key = ByteSpan(kKeyBuffer2b); + err = table2.Set(1, entry); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_ARGUMENT == err); + + // Retrieve fabric1, first entry + err = table1.Get(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + NL_TEST_ASSERT(aSuite, kTestFabricIndex1 == entry.fabricIndex); + NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.checkInNodeID); + NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.monitoredSubject); + NL_TEST_ASSERT(aSuite, sizeof(kKeyBuffer1a) == entry.key.size()); + NL_TEST_ASSERT(aSuite, 0 == memcmp(entry.key.data(), kKeyBuffer1a, entry.key.size())); + + // Retrieve fabric2, second entry + err = table1.Get(1, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + NL_TEST_ASSERT(aSuite, kTestFabricIndex1 == entry.fabricIndex); + NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.checkInNodeID); + NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.monitoredSubject); + NL_TEST_ASSERT(aSuite, sizeof(kKeyBuffer1b) == entry.key.size()); + NL_TEST_ASSERT(aSuite, 0 == memcmp(entry.key.data(), kKeyBuffer1b, entry.key.size())); + + // Retrieve fabric2, first entry + err = table2.Get(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + NL_TEST_ASSERT(aSuite, kTestFabricIndex2 == entry.fabricIndex); + NL_TEST_ASSERT(aSuite, kClientNodeId21 == entry.checkInNodeID); + NL_TEST_ASSERT(aSuite, kClientNodeId22 == entry.monitoredSubject); + NL_TEST_ASSERT(aSuite, sizeof(kKeyBuffer2a) == entry.key.size()); + NL_TEST_ASSERT(aSuite, 0 == memcmp(entry.key.data(), kKeyBuffer2a, entry.key.size())); +} + +void TestDeleteValidEntryFromStorage(nlTestSuite * aSuite, void * context) +{ + TestPersistentStorageDelegate storage; + IcdMonitoringTable table1(storage, kTestFabricIndex1, kMaxTestClients1); + IcdMonitoringTable table2(storage, kTestFabricIndex2, kMaxTestClients2); + IcdMonitoringEntry entry; + CHIP_ERROR err; + + // Insert first entry + entry.checkInNodeID = kClientNodeId11; + entry.monitoredSubject = kClientNodeId12; + entry.key = ByteSpan(kKeyBuffer1a); + err = table1.Set(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + + // Insert second entry + entry.checkInNodeID = kClientNodeId12; + entry.monitoredSubject = kClientNodeId11; + entry.key = ByteSpan(kKeyBuffer2a); + err = table1.Set(1, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + + // Insert in second fabric + entry.checkInNodeID = kClientNodeId21; + entry.monitoredSubject = kClientNodeId22; + entry.key = ByteSpan(kKeyBuffer2a); + err = table2.Set(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + + // Remove (invalid) + err = table1.Remove(2); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR != err); + + // Retrieve fabric1 + err = table1.Get(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + NL_TEST_ASSERT(aSuite, kTestFabricIndex1 == entry.fabricIndex); + NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.checkInNodeID); + NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.monitoredSubject); + NL_TEST_ASSERT(aSuite, sizeof(kKeyBuffer1a) == entry.key.size()); + NL_TEST_ASSERT(aSuite, 0 == memcmp(entry.key.data(), kKeyBuffer1a, entry.key.size())); + + // Retrieve second entry (not modified) + err = table1.Get(1, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + NL_TEST_ASSERT(aSuite, kTestFabricIndex1 == entry.fabricIndex); + NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.checkInNodeID); + NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.monitoredSubject); + NL_TEST_ASSERT(aSuite, sizeof(kKeyBuffer2a) == entry.key.size()); + NL_TEST_ASSERT(aSuite, 0 == memcmp(entry.key.data(), kKeyBuffer2a, entry.key.size())); + + // Remove (existing) + err = table1.Remove(0); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + + // Retrieve second entry (shifted down) + err = table1.Get(1, entry); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == err); + + err = table1.Get(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + NL_TEST_ASSERT(aSuite, kTestFabricIndex1 == entry.fabricIndex); + NL_TEST_ASSERT(aSuite, kClientNodeId12 == entry.checkInNodeID); + NL_TEST_ASSERT(aSuite, kClientNodeId11 == entry.monitoredSubject); + NL_TEST_ASSERT(aSuite, sizeof(kKeyBuffer2a) == entry.key.size()); + NL_TEST_ASSERT(aSuite, 0 == memcmp(entry.key.data(), kKeyBuffer2a, entry.key.size())); + + // Retrieve fabric2, first entry + err = table2.Get(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + NL_TEST_ASSERT(aSuite, kTestFabricIndex2 == entry.fabricIndex); + NL_TEST_ASSERT(aSuite, kClientNodeId21 == entry.checkInNodeID); + NL_TEST_ASSERT(aSuite, kClientNodeId22 == entry.monitoredSubject); + NL_TEST_ASSERT(aSuite, sizeof(kKeyBuffer2a) == entry.key.size()); + NL_TEST_ASSERT(aSuite, 0 == memcmp(entry.key.data(), kKeyBuffer2a, entry.key.size())); + + // Remove all (fabric 1) + err = table1.RemoveAll(); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + + err = table1.Get(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == err); + + // Check fabric 2 + err = table2.Get(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + NL_TEST_ASSERT(aSuite, kTestFabricIndex2 == entry.fabricIndex); + NL_TEST_ASSERT(aSuite, kClientNodeId21 == entry.checkInNodeID); + NL_TEST_ASSERT(aSuite, kClientNodeId22 == entry.monitoredSubject); + NL_TEST_ASSERT(aSuite, sizeof(kKeyBuffer2a) == entry.key.size()); + NL_TEST_ASSERT(aSuite, 0 == memcmp(entry.key.data(), kKeyBuffer2a, entry.key.size())); + + // Remove all (fabric 2) + err = table2.RemoveAll(); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == err); + + err = table2.Get(0, entry); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == err); +} + +} // namespace + +/** + * Set up the test suite. + */ +int Test_Setup(void * inContext) +{ + + return SUCCESS; +} + +int TestClientMonitoringRegistrationTable() +{ + static nlTest sTests[] = { + NL_TEST_DEF("TestSaveAndLoadRegistrationValue", TestSaveAndLoadRegistrationValue), + NL_TEST_DEF("TestSaveAllInvalidRegistrationValues", TestSaveAllInvalidRegistrationValues), + NL_TEST_DEF("TestSaveLoadRegistrationValueForMultipleFabrics", TestSaveLoadRegistrationValueForMultipleFabrics), + NL_TEST_DEF("TestDeleteValidEntryFromStorage", TestDeleteValidEntryFromStorage), NL_TEST_SENTINEL() + }; + + nlTestSuite cmSuite = { "TestClientMonitoringRegistrationTable", &sTests[0], &Test_Setup, nullptr }; + + nlTestRunner(&cmSuite, nullptr); + return (nlTestRunnerStats(&cmSuite)); +} + +CHIP_REGISTER_TEST_SUITE(TestClientMonitoringRegistrationTable) diff --git a/src/app/tests/suites/TestClientMonitoringCluster.yaml b/src/app/tests/suites/TestClientMonitoringCluster.yaml deleted file mode 100644 index 8376543a7893a5..00000000000000 --- a/src/app/tests/suites/TestClientMonitoringCluster.yaml +++ /dev/null @@ -1,102 +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: Client Monitoring Tests - -config: - nodeId: 0x12344321 - cluster: "Client Monitoring" - endpoint: 0 - -tests: - - label: "Wait for the commissioned device to be retrieved" - cluster: "DelayCommands" - command: "WaitForCommissionee" - arguments: - values: - - name: "nodeId" - value: nodeId - - - label: "Register Client 2 - Invalid" - command: "RegisterClientMonitoring" - arguments: - values: - - name: "ClientNodeId" - value: 0 - - name: "ICid" - value: 0 - response: - error: FAILURE - - - label: "Register Client 1" - command: "RegisterClientMonitoring" - arguments: - values: - - name: "ClientNodeId" - value: 10 - - name: "ICid" - value: 20 - - - label: "Register Client 2 - Invalid" - command: "RegisterClientMonitoring" - arguments: - values: - - name: "ClientNodeId" - value: 11 - - name: "ICid" - value: 21 - response: - error: RESOURCE_EXHAUSTED - - - label: "Verify Register Client" - command: "readAttribute" - attribute: "ExpectedClients" - response: - value: [{ ClientNodeId: 10, ICid: 20 }] - - - label: "Unregister Client - Invalid Client NodeId" - command: "UnregisterClientMonitoring" - arguments: - values: - - name: "ClientNodeId" - value: 30 - - name: "ICid" - value: 20 - response: - error: FAILURE - - - label: "Unregister Client - Invalid ICid" - command: "UnregisterClientMonitoring" - arguments: - values: - - name: "ClientNodeId" - value: 10 - - name: "ICid" - value: 30 - response: - error: FAILURE - - - label: "Unregister Client - Valid" - command: "UnregisterClientMonitoring" - arguments: - values: - - name: "ClientNodeId" - value: 10 - - name: "ICid" - value: 20 - - - label: "Verify Register Client - Empty" - command: "readAttribute" - attribute: "ExpectedClients" - response: - value: [] diff --git a/src/app/tests/suites/TestIcdManagementCluster.yaml b/src/app/tests/suites/TestIcdManagementCluster.yaml new file mode 100644 index 00000000000000..985e943df5057f --- /dev/null +++ b/src/app/tests/suites/TestIcdManagementCluster.yaml @@ -0,0 +1,304 @@ +# Copyright (c) 2021 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: ICD Management Cluster Tests + +config: + nodeId: 0x12344321 + cluster: "ICD Management" + endpoint: 0 + +tests: + - label: "Wait for the commissioned device to be retrieved" + cluster: "DelayCommands" + command: "WaitForCommissionee" + arguments: + values: + - name: "nodeId" + value: nodeId + + - label: "Read IdleModeInterval" + command: "readAttribute" + attribute: "IdleModeInterval" + response: + value: 500 + + - label: "Read ActiveModeInterval" + command: "readAttribute" + attribute: "ActiveModeInterval" + response: + value: 300 + + - label: "Read ActiveModeThreshold" + command: "readAttribute" + attribute: "ActiveModeThreshold" + response: + value: 300 + + - label: "Read ICDCounter" + command: "readAttribute" + attribute: "ICDCounter" + response: + value: 0 + + - label: "Read ClientsSupportedPerFabric" + command: "readAttribute" + attribute: "ClientsSupportedPerFabric" + response: + value: 2 + + - label: "Unregister 1.0" + command: "UnregisterClient" + arguments: + values: + - name: "CheckInNodeID" + value: 101 + response: + error: NOT_FOUND + + - label: "Unregister 2.0" + command: "UnregisterClient" + arguments: + values: + - name: "CheckInNodeID" + value: 102 + response: + error: NOT_FOUND + + - label: "Unregister 3.0" + command: "UnregisterClient" + arguments: + values: + - name: "CheckInNodeID" + value: 102 + response: + error: NOT_FOUND + + - label: "Read RegisteredClients" + command: "readAttribute" + attribute: "RegisteredClients" + response: + value: [] + + - label: "Register 1.0 (key too short)" + command: "RegisterClient" + arguments: + values: + - name: "CheckInNodeID" + value: 101 + - name: "MonitoredSubject" + value: 1001 + - name: "Key" + value: "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e" + response: + error: CONSTRAINT_ERROR + + - label: "Register 1.0 (key too long)" + command: "RegisterClient" + arguments: + values: + - name: "CheckInNodeID" + value: 101 + - name: "MonitoredSubject" + value: 1001 + - name: "Key" + value: "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\xff" + response: + error: CONSTRAINT_ERROR + + - label: "Register 1.1" + command: "RegisterClient" + arguments: + values: + - name: "CheckInNodeID" + value: 101 + - name: "MonitoredSubject" + value: 1001 + - name: "Key" + value: "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + response: + values: + - name: "ICDCounter" + value: 0 + + - label: "Register 2.1" + command: "RegisterClient" + arguments: + values: + - name: "CheckInNodeID" + value: 201 + - name: "MonitoredSubject" + value: 2001 + - name: "Key" + value: + "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + response: + values: + - name: "ICDCounter" + value: 0 + + - label: "Register 3.1" + command: "RegisterClient" + arguments: + values: + - name: "CheckInNodeID" + value: 301 + - name: "MonitoredSubject" + value: 3001 + - name: "Key" + value: "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" + response: + error: RESOURCE_EXHAUSTED + + - label: "Read RegisteredClients" + command: "readAttribute" + attribute: "RegisteredClients" + response: + value: + [ + { + CheckInNodeID: 101, + MonitoredSubject: 1001, + Key: "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + }, + { + CheckInNodeID: 201, + MonitoredSubject: 2001, + Key: + "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f", + }, + ] + + - label: "Register 1.1" + command: "RegisterClient" + arguments: + values: + - name: "CheckInNodeID" + value: 101 + - name: "MonitoredSubject" + value: 1002 + - name: "Key" + value: "\x01\x11\x21\x31\x41\x51\x61\x71\x81\x91\xa1\xb1\xc1\xd1\xe1\xf1" + response: + values: + - name: "ICDCounter" + value: 0 + + - label: "Read RegisteredClients" + command: "readAttribute" + attribute: "RegisteredClients" + response: + value: + [ + { + CheckInNodeID: 101, + MonitoredSubject: 1002, + Key: "\x01\x11\x21\x31\x41\x51\x61\x71\x81\x91\xa1\xb1\xc1\xd1\xe1\xf1", + }, + { + CheckInNodeID: 201, + MonitoredSubject: 2001, + Key: + "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f", + }, + ] + + - label: "Register 2.2 (wrong verification key)" + command: "RegisterClient" + arguments: + values: + - name: "CheckInNodeID" + value: 201 + - name: "MonitoredSubject" + value: 2002 + - name: "Key" + value: "\x02\x12\x22\x32\x42\x52\x62\x72\x82\x92\xa2\xb2\xc2\xd2\xe2\x2f" + - name: "VerificationKey" + value: + "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2f\x2f" + response: + values: + - name: "ICDCounter" + value: 0 + + - label: "Read RegisteredClients" + command: "readAttribute" + attribute: "RegisteredClients" + response: + value: + [ + { + CheckInNodeID: 101, + MonitoredSubject: 1002, + Key: "\x01\x11\x21\x31\x41\x51\x61\x71\x81\x91\xa1\xb1\xc1\xd1\xe1\xf1", + }, + { + CheckInNodeID: 201, + MonitoredSubject: 2002, + Key: "\x02\x12\x22\x32\x42\x52\x62\x72\x82\x92\xa2\xb2\xc2\xd2\xe2\x2f", + }, + ] + + - label: "Unregister 1.1 (wrong key)" + command: "UnregisterClient" + arguments: + values: + - name: "CheckInNodeID" + value: 101 + - name: "Key" + value: "\x01\x21\x21\x31\x41\x51\x61\x71\x81\x91\xa1\xb1\xc1\xd1\xe1\xf1" + + - label: "Read RegisteredClients" + command: "readAttribute" + attribute: "RegisteredClients" + response: + value: + [ + { + CheckInNodeID: 201, + MonitoredSubject: 2002, + Key: "\x02\x12\x22\x32\x42\x52\x62\x72\x82\x92\xa2\xb2\xc2\xd2\xe2\x2f", + }, + ] + + - label: "Unregister 2.1" + command: "UnregisterClient" + arguments: + values: + - name: "CheckInNodeID" + value: 201 + + - label: "Read RegisteredClients" + command: "readAttribute" + attribute: "RegisteredClients" + response: + value: [] + + - label: "Unregister 1.3" + command: "UnregisterClient" + arguments: + values: + - name: "CheckInNodeID" + value: 101 + response: + error: NOT_FOUND + + - label: "Unregister 2.2" + command: "UnregisterClient" + arguments: + values: + - name: "CheckInNodeID" + value: 102 + response: + error: NOT_FOUND diff --git a/src/app/tests/suites/ciTests.json b/src/app/tests/suites/ciTests.json index 1d588af8f3050c..0cab61e18fdc61 100644 --- a/src/app/tests/suites/ciTests.json +++ b/src/app/tests/suites/ciTests.json @@ -42,6 +42,7 @@ "TestColorControl_9_2" ], "DeviceManagement": [ + "TestIcdManagementCluster", "Test_TC_OPCREDS_1_2", "Test_TC_BINFO_1_1", "Test_TC_BINFO_2_1", diff --git a/src/app/util/ClientMonitoringRegistrationTable.cpp b/src/app/util/ClientMonitoringRegistrationTable.cpp deleted file mode 100644 index bfe81a7ddf77d9..00000000000000 --- a/src/app/util/ClientMonitoringRegistrationTable.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/** - * - * Copyright (c) 2022 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. - */ - -// Disable CM cluster table until update is done -// https://github.com/project-chip/connectedhomeip/issues/24425 -#if 0 -#include "ClientMonitoringRegistrationTable.h" - -#include - -namespace chip { - -/********************************************************** - * ClientMonitoringRegistrationTable Implementation - *********************************************************/ - -ClientMonitoringRegistrationTable::ClientMonitoringRegistrationTable(PersistentStorageDelegate & storage) : mStorage(storage) {} - -CHIP_ERROR ClientMonitoringRegistrationTable::LoadFromStorage(FabricIndex fabricIndex) -{ - uint8_t buffer[kRegStorageSize] = { 0 }; - uint16_t size = sizeof(buffer); - - ReturnErrorOnFailure( - mStorage.SyncGetKeyValue(DefaultStorageKeyAllocator::ClientMonitoringTableEntry(fabricIndex).KeyName(), buffer, size)); - - TLV::TLVReader reader; - reader.Init(buffer, size); - ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())); - - ReturnErrorOnFailure(mRegisteredClient.Decode(reader)); - - mRegisteredClient.fabricIndex = fabricIndex; - - return CHIP_NO_ERROR; -} - -CHIP_ERROR ClientMonitoringRegistrationTable::SaveToStorage() -{ - VerifyOrReturnError(mRegisteredClient.IsValid(), CHIP_ERROR_INCORRECT_STATE); - - uint8_t buffer[kRegStorageSize] = { 0 }; - TLV::TLVWriter writer; - - writer.Init(buffer); - ReturnErrorOnFailure(mRegisteredClient.EncodeForWrite(writer, TLV::AnonymousTag())); - ReturnErrorOnFailure(writer.Finalize()); - - return mStorage.SyncSetKeyValue(DefaultStorageKeyAllocator::ClientMonitoringTableEntry(mRegisteredClient.fabricIndex).KeyName(), - buffer, static_cast(writer.GetLengthWritten())); -} - -ClientMonitoringRegistrationTable::ClientRegistrationEntry & ClientMonitoringRegistrationTable::GetClientRegistrationEntry() -{ - return mRegisteredClient; -} - -CHIP_ERROR ClientMonitoringRegistrationTable::DeleteFromStorage(FabricIndex fabric) -{ - return mStorage.SyncDeleteKeyValue(DefaultStorageKeyAllocator::ClientMonitoringTableEntry(fabric).KeyName()); -} - -bool ClientMonitoringRegistrationTable::HasValueForFabric(FabricIndex fabric) -{ - return mStorage.SyncDoesKeyExist(DefaultStorageKeyAllocator::ClientMonitoringTableEntry(fabric).KeyName()); -} - -} // namespace chip -#endif diff --git a/src/app/util/ClientMonitoringRegistrationTable.h b/src/app/util/ClientMonitoringRegistrationTable.h deleted file mode 100644 index 51886e8527e34a..00000000000000 --- a/src/app/util/ClientMonitoringRegistrationTable.h +++ /dev/null @@ -1,102 +0,0 @@ -/** - * - * Copyright (c) 2022 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. - */ - -// Disable CM cluster table until update is done -// https://github.com/project-chip/connectedhomeip/issues/24425 -#if 0 -#pragma once - -#include -#include -#include -#include -#include - -namespace chip { - -/** - * @brief ClientMonitoringRegistrationTable exists to manage the persistence of entries in the ClientMonitoring Cluster. - * To access persisted data with the ClientMonitoringRegistrationTable class, instantiate an instance of this class - * and call the LoadFromStorage function. - * - * This class can only manage one fabric at a time. The flow is load a fabric, execute necessary operations, - * save it if there are any changes and load another fabric. - * - * Issue to refactor the class to use one entry for the entire table - * https://github.com/project-chip/connectedhomeip/issues/24288 - */ -class ClientMonitoringRegistrationTable -{ -public: - using MonitoringRegistrationStruct = chip::app::Clusters::ClientMonitoring::Structs::MonitoringRegistration::Type; - - struct ClientRegistrationEntry : MonitoringRegistrationStruct - { - bool IsValid() { return clientNodeId != kUndefinedNodeId && ICid != kInvalidIcId && fabricIndex != kUndefinedFabricIndex; } - }; - - ClientMonitoringRegistrationTable(PersistentStorageDelegate & storage); - - /** - * @brief Function saves the mRegisteredClient attribute to persitant storage - * To correctly persit an entry, the values must be stored in the structures attributes - * - * @return CHIP_ERROR - */ - CHIP_ERROR SaveToStorage(); - - /** - * @brief Function loads a client registration entry from persistent storage for a single fabric - * - * @param[in] fabricIndex fabric index to load from storage - * @return CHIP_ERROR - */ - CHIP_ERROR LoadFromStorage(FabricIndex fabricIndex); - - /** - * @brief Function deletes a client registration entry from persistent storage for a single fabric - * - * @param[in] fabricIndex fabric index to delete from storage - * @return CHIP_ERROR - */ - CHIP_ERROR DeleteFromStorage(FabricIndex fabricIndex); - - /** - * @brief Function check if a client registration entry is stored for a single fabric - * - * @param[in] fabricIndex fabric index to check - * @return CHIP_ERROR - */ - bool HasValueForFabric(FabricIndex fabric); - - /** - * @brief Accessor function that returns the client registration entry that was loaded for a fabric from persistant storage. - * @see LoadFromStorage - * - * @return ClientMonitoringRegistrationTable::ClientRegistrationEntry& - */ - ClientRegistrationEntry & GetClientRegistrationEntry(); - -private: - static constexpr uint8_t kRegStorageSize = TLV::EstimateStructOverhead(sizeof(NodeId), sizeof(uint64_t)); - - ClientRegistrationEntry mRegisteredClient; - PersistentStorageDelegate & mStorage; -}; - -} // namespace chip -#endif diff --git a/src/app/util/IcdMonitoringTable.cpp b/src/app/util/IcdMonitoringTable.cpp new file mode 100644 index 00000000000000..2f208812db74f2 --- /dev/null +++ b/src/app/util/IcdMonitoringTable.cpp @@ -0,0 +1,111 @@ +#include "IcdMonitoringTable.h" +#include + +namespace chip { + +CHIP_ERROR IcdMonitoringEntry::UpdateKey(StorageKeyName & skey) +{ + VerifyOrReturnError(kUndefinedFabricIndex != this->fabricIndex, CHIP_ERROR_INVALID_FABRIC_INDEX); + skey = DefaultStorageKeyAllocator::IcdManagementTableEntry(this->fabricIndex, index); + return CHIP_NO_ERROR; +} + +CHIP_ERROR IcdMonitoringEntry::Serialize(TLV::TLVWriter & writer) const +{ + return this->EncodeForWrite(writer, TLV::AnonymousTag()); +} + +CHIP_ERROR IcdMonitoringEntry::Deserialize(TLV::TLVReader & reader) +{ + ReturnErrorOnFailure(reader.Next(TLV::AnonymousTag())); + return this->Decode(reader); +} + +void IcdMonitoringEntry::Clear() +{ + this->checkInNodeID = kUndefinedNodeId; + this->monitoredSubject = kUndefinedNodeId; + this->key = ByteSpan(); +} + +CHIP_ERROR IcdMonitoringTable::Get(uint16_t index, IcdMonitoringEntry & entry) const +{ + entry.fabricIndex = this->mFabric; + entry.index = index; + ReturnErrorOnFailure(entry.Load(this->mStorage)); + entry.fabricIndex = this->mFabric; + return CHIP_NO_ERROR; +} + +CHIP_ERROR IcdMonitoringTable::Find(NodeId id, IcdMonitoringEntry & entry) +{ + uint16_t index = 0; + while (index < this->Limit()) + { + ReturnErrorOnFailure(this->Get(index++, entry)); + if (id == entry.checkInNodeID) + { + return CHIP_NO_ERROR; + } + } + entry.index = index; + return CHIP_ERROR_NOT_FOUND; +} + +CHIP_ERROR IcdMonitoringTable::Set(uint16_t index, const IcdMonitoringEntry & entry) +{ + VerifyOrReturnError(index < this->Limit(), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(kUndefinedNodeId != entry.checkInNodeID, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(kUndefinedNodeId != entry.monitoredSubject, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(entry.key.size() == IcdMonitoringEntry::kKeyMaxSize, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(nullptr != entry.key.data(), CHIP_ERROR_INVALID_ARGUMENT); + IcdMonitoringEntry e(this->mFabric, index); + e.checkInNodeID = entry.checkInNodeID; + e.monitoredSubject = entry.monitoredSubject; + e.key = entry.key; + e.index = index; + return e.Save(this->mStorage); +} + +CHIP_ERROR IcdMonitoringTable::Remove(uint16_t index) +{ + IcdMonitoringEntry entry(this->mFabric, index); + + // Shift remaining entries down one position + while (CHIP_NO_ERROR == this->Get(static_cast(index + 1), entry)) + { + // WARNING: The key data is held in the entry's serializing buffer + IcdMonitoringEntry copy = entry; + this->Set(index++, copy); + } + + // Remove last entry + entry.fabricIndex = this->mFabric; + entry.index = index; + return entry.Delete(this->mStorage); +} + +CHIP_ERROR IcdMonitoringTable::RemoveAll() +{ + IcdMonitoringEntry entry(this->mFabric); + uint16_t index = 0; + while (index < this->Limit()) + { + CHIP_ERROR err = this->Get(index++, entry); + if (CHIP_ERROR_NOT_FOUND == err) + { + break; + } + ReturnErrorOnFailure(err); + entry.fabricIndex = this->mFabric; + entry.Delete(this->mStorage); + } + return CHIP_NO_ERROR; +} + +uint16_t IcdMonitoringTable::Limit() const +{ + return mLimit; +} + +} // namespace chip diff --git a/src/app/util/IcdMonitoringTable.h b/src/app/util/IcdMonitoringTable.h new file mode 100644 index 00000000000000..b488d515759df8 --- /dev/null +++ b/src/app/util/IcdMonitoringTable.h @@ -0,0 +1,124 @@ +/** + * + * 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. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { + +constexpr size_t kIcdMonitoringBufferSize = 40; + +struct IcdMonitoringEntry : public PersistentData, + chip::app::Clusters::IcdManagement::Structs::MonitoringRegistrationStruct::Type +{ + static constexpr size_t kKeyMaxSize = 16; + + IcdMonitoringEntry(FabricIndex fabric = kUndefinedFabricIndex, NodeId nodeId = kUndefinedNodeId) + { + this->fabricIndex = fabric; + this->checkInNodeID = nodeId; + this->monitoredSubject = nodeId; + } + bool IsValid() { return this->checkInNodeID != kUndefinedNodeId && this->fabricIndex != kUndefinedFabricIndex; } + + CHIP_ERROR UpdateKey(StorageKeyName & key) override; + CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override; + CHIP_ERROR Deserialize(TLV::TLVReader & reader) override; + void Clear() override; + + uint16_t index = 0; +}; + +/** + * @brief IcdMonitoringTable exists to manage the persistence of entries in the IcdManagement Cluster. + * To access persisted data with the IcdMonitoringTable class, instantiate an instance of this class + * and call the LoadFromStorage function. + * + * This class can only manage one fabric at a time. The flow is load a fabric, execute necessary operations, + * save it if there are any changes and load another fabric. + * + * Issue to refactor the class to use one entry for the entire table + * https://github.com/project-chip/connectedhomeip/issues/24288 + */ + +struct IcdMonitoringTable +{ + IcdMonitoringTable(PersistentStorageDelegate & storage, FabricIndex fabric, uint16_t limit) : + mStorage(&storage), mFabric(fabric), mLimit(limit) + {} + + /** + * @brief Returns the MonitoringRegistrationStruct entry at the given position. + * @param index Zero-based position within the RegisteredClients table. + * @param entry On success, contains the MonitoringRegistrationStruct matching the given index. + * @return CHIP_NO_ERROR on success, + * CHIP_ERROR_NOT_FOUND if index is greater than the index of the last entry on the table. + */ + CHIP_ERROR Get(uint16_t index, IcdMonitoringEntry & entry) const; + + /** + * @brief Stores the MonitoringRegistrationStruct entry at the given position, + * overwriting any existing entry. + * @param index Zero-based position within the RegisteredClients table. + * @param entry On success, contains the MonitoringRegistrationStruct matching the given index. + * @return CHIP_NO_ERROR on success + */ + CHIP_ERROR Set(uint16_t index, const IcdMonitoringEntry & entry); + + /** + * @brief Search the registered clients for an entry on the fabric whose checkInNodeID matches the given id. + * @param id NodeId to match. + * @param entry On success, contains the MonitoringRegistrationStruct matching the given node ID. + * If found, entry.index contains the position of the entry in the table. + * If CHIP_ERROR_NOT_FOUND is returned, entry.index contains the total number of entries in the table. + * @return CHIP_NO_ERROR if found, CHIP_ERROR_NOT_FOUND if no checkInNodeID matches the provided id. + */ + CHIP_ERROR Find(NodeId id, IcdMonitoringEntry & entry); + + /** + * @brief Removes the MonitoringRegistrationStruct entry at the given position, + * shifting down the upper entries. + * @param index Zero-based position within the RegisteredClients table. + * @return CHIP_NO_ERROR on success + */ + CHIP_ERROR Remove(uint16_t index); + + /** + * @brief Removes all the entries for the current fabricIndex. + * @return CHIP_NO_ERROR on success + */ + CHIP_ERROR RemoveAll(); + + /** + * @return Maximum number of entries allowed in the RegisteredClients table. + */ + uint16_t Limit() const; + +private: + PersistentStorageDelegate * mStorage; + FabricIndex mFabric; + uint16_t mLimit = 0; +}; + +} // namespace chip diff --git a/src/controller/data_model/BUILD.gn b/src/controller/data_model/BUILD.gn index ab81054bb0f1f0..8285034a4bbb43 100644 --- a/src/controller/data_model/BUILD.gn +++ b/src/controller/data_model/BUILD.gn @@ -304,11 +304,6 @@ if (current_os == "android" || matter_enable_java_compilation) { "jni/WindowCoveringClient-ReadImpl.cpp", "jni/ZeoliteFilterMonitoringClient-InvokeSubscribeImpl.cpp", "jni/ZeoliteFilterMonitoringClient-ReadImpl.cpp", - - # Disable CM cluster table tests until update is done - # https://github.com/project-chip/connectedhomeip/issues/24425 - #"jni/ClientMonitoringClient-ReadImpl.cpp", - #"jni/ClientMonitoringClient-InvokeSubscribeImpl.cpp", ] deps = [ diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index 0a3b60f18b9c88..24a88cb64df273 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -1355,16 +1355,6 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; #define CHIP_CONFIG_NUM_CD_KEY_SLOTS 5 #endif // CHIP_CONFIG_NUM_CD_KEY_SLOTS -/** - * @def CHIP_CONFIG_MAX_CLIENT_REG_PER_FABRIC - * - * @brief Defines the number of clients that can register for monitoring with a server - * see ClientMonitoring cluster for specification - */ -#ifndef CHIP_CONFIG_MAX_CLIENT_REG_PER_FABRIC -#define CHIP_CONFIG_MAX_CLIENT_REG_PER_FABRIC 1 -#endif // CHIP_CONFIG_MAX_CLIENT_REG_PER_FABRIC - /** * @def CHIP_CONFIG_MAX_SUBSCRIPTION_RESUMPTION_STORAGE_CONCURRENT_ITERATORS * diff --git a/src/lib/support/DefaultStorageKeyAllocator.h b/src/lib/support/DefaultStorageKeyAllocator.h index 51e006cfa41b8e..b437062d049b66 100644 --- a/src/lib/support/DefaultStorageKeyAllocator.h +++ b/src/lib/support/DefaultStorageKeyAllocator.h @@ -175,11 +175,11 @@ class DefaultStorageKeyAllocator static StorageKeyName BindingTable() { return StorageKeyName::FromConst("g/bt"); } static StorageKeyName BindingTableEntry(uint8_t index) { return StorageKeyName::Formatted("g/bt/%x", index); } - // Client Monitoring + // ICD Management - static StorageKeyName ClientMonitoringTableEntry(chip::FabricIndex fabric) + static StorageKeyName IcdManagementTableEntry(chip::FabricIndex fabric, uint16_t index) { - return StorageKeyName::Formatted("f/%x/cm", fabric); + return StorageKeyName::Formatted("f/%x/icd/%x", fabric, index); } static StorageKeyName OTADefaultProviders() { return StorageKeyName::FromConst("g/o/dp"); } diff --git a/src/lib/support/PersistentData.h b/src/lib/support/PersistentData.h index 4ad74da92bf32a..20286d41e6642e 100644 --- a/src/lib/support/PersistentData.h +++ b/src/lib/support/PersistentData.h @@ -23,11 +23,12 @@ namespace chip { /// @brief Interface to Persistent Storage Delegate allowing storage of data of variable size such as TLV. -/// @tparam kMaxSerializedSize size of the buffer necessary to retrieve an entry from the storage. Varies with the type of data +/// @tparam kMaxSerializedSize size of the mBuffer necessary to retrieve an entry from the storage. Varies with the type of data /// stored. Will be allocated on the stack so the implementation needs to be aware of this when choosing this value. template struct PersistentData { + PersistentData(PersistentStorageDelegate * storage = nullptr) : mStorage(storage) {} virtual ~PersistentData() = default; virtual CHIP_ERROR UpdateKey(StorageKeyName & key) = 0; @@ -35,45 +36,48 @@ struct PersistentData virtual CHIP_ERROR Deserialize(TLV::TLVReader & reader) = 0; virtual void Clear() = 0; + virtual CHIP_ERROR Save() { return this->Save(this->mStorage); } + virtual CHIP_ERROR Save(PersistentStorageDelegate * storage) { VerifyOrReturnError(nullptr != storage, CHIP_ERROR_INVALID_ARGUMENT); - uint8_t buffer[kMaxSerializedSize] = { 0 }; - StorageKeyName key = StorageKeyName::Uninitialized(); + StorageKeyName key = StorageKeyName::Uninitialized(); ReturnErrorOnFailure(UpdateKey(key)); // Serialize the data TLV::TLVWriter writer; - writer.Init(buffer, sizeof(buffer)); + writer.Init(mBuffer, sizeof(mBuffer)); + ReturnErrorOnFailure(Serialize(writer)); // Save serialized data - return storage->SyncSetKeyValue(key.KeyName(), buffer, static_cast(writer.GetLengthWritten())); + return storage->SyncSetKeyValue(key.KeyName(), mBuffer, static_cast(writer.GetLengthWritten())); } + virtual CHIP_ERROR Load() { return this->Load(this->mStorage); } + virtual CHIP_ERROR Load(PersistentStorageDelegate * storage) { VerifyOrReturnError(nullptr != storage, CHIP_ERROR_INVALID_ARGUMENT); - uint8_t buffer[kMaxSerializedSize] = { 0 }; - StorageKeyName key = StorageKeyName::Uninitialized(); - - // Set data to defaults - Clear(); + StorageKeyName key = StorageKeyName::Uninitialized(); // Update storage key ReturnErrorOnFailure(UpdateKey(key)); + // Set data to defaults + Clear(); + // Load the serialized data - uint16_t size = static_cast(sizeof(buffer)); - CHIP_ERROR err = storage->SyncGetKeyValue(key.KeyName(), buffer, size); + uint16_t size = static_cast(sizeof(mBuffer)); + CHIP_ERROR err = storage->SyncGetKeyValue(key.KeyName(), mBuffer, size); VerifyOrReturnError(CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND != err, CHIP_ERROR_NOT_FOUND); ReturnErrorOnFailure(err); // Decode serialized data TLV::TLVReader reader; - reader.Init(buffer, size); + reader.Init(mBuffer, size); return Deserialize(reader); } @@ -86,6 +90,9 @@ struct PersistentData return storage->SyncDeleteKeyValue(key.KeyName()); } + + PersistentStorageDelegate * mStorage = nullptr; + uint8_t mBuffer[kMaxSerializedSize] = { 0 }; }; } // namespace chip diff --git a/zzz_generated/chip-tool/zap-generated/test/Commands.h b/zzz_generated/chip-tool/zap-generated/test/Commands.h index 65b78d4a2430db..eaac169bea6546 100644 --- a/zzz_generated/chip-tool/zap-generated/test/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/test/Commands.h @@ -75,6 +75,7 @@ class TestList : public Command printf("Test_TC_CC_8_1\n"); printf("TestColorControl_9_1\n"); printf("TestColorControl_9_2\n"); + printf("TestIcdManagementCluster\n"); printf("Test_TC_OPCREDS_1_2\n"); printf("Test_TC_BINFO_1_1\n"); printf("Test_TC_BINFO_2_1\n"); @@ -22981,6 +22982,530 @@ class TestColorControl_9_2Suite : public TestCommand } }; +class TestIcdManagementClusterSuite : public TestCommand +{ +public: + TestIcdManagementClusterSuite(CredentialIssuerCommands * credsIssuerConfig) : + TestCommand("TestIcdManagementCluster", 26, credsIssuerConfig) + { + AddArgument("nodeId", 0, UINT64_MAX, &mNodeId); + AddArgument("cluster", &mCluster); + AddArgument("endpoint", 0, UINT16_MAX, &mEndpoint); + AddArgument("timeout", 0, UINT16_MAX, &mTimeout); + } + + ~TestIcdManagementClusterSuite() {} + + chip::System::Clock::Timeout GetWaitDuration() const override + { + return chip::System::Clock::Seconds16(mTimeout.ValueOr(kTimeoutInSeconds)); + } + +private: + chip::Optional mNodeId; + chip::Optional mCluster; + chip::Optional mEndpoint; + chip::Optional mTimeout; + + chip::EndpointId GetEndpoint(chip::EndpointId endpoint) { return mEndpoint.HasValue() ? mEndpoint.Value() : endpoint; } + + // + // Tests methods + // + + void OnResponse(const chip::app::StatusIB & status, chip::TLV::TLVReader * data) override + { + bool shouldContinue = false; + + switch (mTestIndex - 1) + { + case 0: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + shouldContinue = true; + break; + case 1: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + { + uint32_t value; + VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); + VerifyOrReturn(CheckValue("idleModeInterval", value, 500UL)); + } + break; + case 2: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + { + uint32_t value; + VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); + VerifyOrReturn(CheckValue("activeModeInterval", value, 300UL)); + } + break; + case 3: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + { + uint16_t value; + VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); + VerifyOrReturn(CheckValue("activeModeThreshold", value, 300U)); + } + break; + case 4: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + { + uint32_t value; + VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); + VerifyOrReturn(CheckValue("ICDCounter", value, 0UL)); + } + break; + case 5: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + { + uint16_t value; + VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); + VerifyOrReturn(CheckValue("clientsSupportedPerFabric", value, 2U)); + } + break; + case 6: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), EMBER_ZCL_STATUS_NOT_FOUND)); + break; + case 7: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), EMBER_ZCL_STATUS_NOT_FOUND)); + break; + case 8: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), EMBER_ZCL_STATUS_NOT_FOUND)); + break; + case 9: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + { + chip::app::DataModel::DecodableList< + chip::app::Clusters::IcdManagement::Structs::MonitoringRegistrationStruct::DecodableType> + value; + VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); + { + auto iter_0 = value.begin(); + VerifyOrReturn(CheckNoMoreListItems("registeredClients", iter_0, 0)); + } + } + break; + case 10: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), EMBER_ZCL_STATUS_CONSTRAINT_ERROR)); + break; + case 11: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), EMBER_ZCL_STATUS_CONSTRAINT_ERROR)); + break; + case 12: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + { + chip::app::Clusters::IcdManagement::Commands::RegisterClientResponse::DecodableType value; + VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); + VerifyOrReturn(CheckValue("ICDCounter", value.ICDCounter, 0UL)); + } + break; + case 13: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + { + chip::app::Clusters::IcdManagement::Commands::RegisterClientResponse::DecodableType value; + VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); + VerifyOrReturn(CheckValue("ICDCounter", value.ICDCounter, 0UL)); + } + break; + case 14: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), EMBER_ZCL_STATUS_RESOURCE_EXHAUSTED)); + break; + case 15: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + { + chip::app::DataModel::DecodableList< + chip::app::Clusters::IcdManagement::Structs::MonitoringRegistrationStruct::DecodableType> + value; + VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); + { + auto iter_0 = value.begin(); + VerifyOrReturn(CheckNextListItemDecodes("registeredClients", iter_0, 0)); + VerifyOrReturn(CheckValue("registeredClients[0].checkInNodeID", iter_0.GetValue().checkInNodeID, 101ULL)); + VerifyOrReturn( + CheckValue("registeredClients[0].monitoredSubject", iter_0.GetValue().monitoredSubject, 1001ULL)); + VerifyOrReturn(CheckValueAsString( + "registeredClients[0].key", iter_0.GetValue().key, + chip::ByteSpan( + chip::Uint8::from_const_char("\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"), 16))); + VerifyOrReturn(CheckNextListItemDecodes("registeredClients", iter_0, 1)); + VerifyOrReturn(CheckValue("registeredClients[1].checkInNodeID", iter_0.GetValue().checkInNodeID, 201ULL)); + VerifyOrReturn( + CheckValue("registeredClients[1].monitoredSubject", iter_0.GetValue().monitoredSubject, 2001ULL)); + VerifyOrReturn(CheckValueAsString("registeredClients[1].key", iter_0.GetValue().key, + chip::ByteSpan(chip::Uint8::from_const_char(" !\042#$%&'()*+,-./"), 16))); + VerifyOrReturn(CheckNoMoreListItems("registeredClients", iter_0, 2)); + } + } + break; + case 16: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + { + chip::app::Clusters::IcdManagement::Commands::RegisterClientResponse::DecodableType value; + VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); + VerifyOrReturn(CheckValue("ICDCounter", value.ICDCounter, 0UL)); + } + break; + case 17: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + { + chip::app::DataModel::DecodableList< + chip::app::Clusters::IcdManagement::Structs::MonitoringRegistrationStruct::DecodableType> + value; + VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); + { + auto iter_0 = value.begin(); + VerifyOrReturn(CheckNextListItemDecodes("registeredClients", iter_0, 0)); + VerifyOrReturn(CheckValue("registeredClients[0].checkInNodeID", iter_0.GetValue().checkInNodeID, 101ULL)); + VerifyOrReturn( + CheckValue("registeredClients[0].monitoredSubject", iter_0.GetValue().monitoredSubject, 1002ULL)); + VerifyOrReturn(CheckValueAsString( + "registeredClients[0].key", iter_0.GetValue().key, + chip::ByteSpan(chip::Uint8::from_const_char("\001\021!1AQaq\201\221\241\261\301\321\341\361"), 16))); + VerifyOrReturn(CheckNextListItemDecodes("registeredClients", iter_0, 1)); + VerifyOrReturn(CheckValue("registeredClients[1].checkInNodeID", iter_0.GetValue().checkInNodeID, 201ULL)); + VerifyOrReturn( + CheckValue("registeredClients[1].monitoredSubject", iter_0.GetValue().monitoredSubject, 2001ULL)); + VerifyOrReturn(CheckValueAsString("registeredClients[1].key", iter_0.GetValue().key, + chip::ByteSpan(chip::Uint8::from_const_char(" !\042#$%&'()*+,-./"), 16))); + VerifyOrReturn(CheckNoMoreListItems("registeredClients", iter_0, 2)); + } + } + break; + case 18: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + { + chip::app::Clusters::IcdManagement::Commands::RegisterClientResponse::DecodableType value; + VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); + VerifyOrReturn(CheckValue("ICDCounter", value.ICDCounter, 0UL)); + } + break; + case 19: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + { + chip::app::DataModel::DecodableList< + chip::app::Clusters::IcdManagement::Structs::MonitoringRegistrationStruct::DecodableType> + value; + VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); + { + auto iter_0 = value.begin(); + VerifyOrReturn(CheckNextListItemDecodes("registeredClients", iter_0, 0)); + VerifyOrReturn(CheckValue("registeredClients[0].checkInNodeID", iter_0.GetValue().checkInNodeID, 101ULL)); + VerifyOrReturn( + CheckValue("registeredClients[0].monitoredSubject", iter_0.GetValue().monitoredSubject, 1002ULL)); + VerifyOrReturn(CheckValueAsString( + "registeredClients[0].key", iter_0.GetValue().key, + chip::ByteSpan(chip::Uint8::from_const_char("\001\021!1AQaq\201\221\241\261\301\321\341\361"), 16))); + VerifyOrReturn(CheckNextListItemDecodes("registeredClients", iter_0, 1)); + VerifyOrReturn(CheckValue("registeredClients[1].checkInNodeID", iter_0.GetValue().checkInNodeID, 201ULL)); + VerifyOrReturn( + CheckValue("registeredClients[1].monitoredSubject", iter_0.GetValue().monitoredSubject, 2002ULL)); + VerifyOrReturn(CheckValueAsString( + "registeredClients[1].key", iter_0.GetValue().key, + chip::ByteSpan(chip::Uint8::from_const_char("\002\022\0422BRbr\202\222\242\262\302\322\342/"), 16))); + VerifyOrReturn(CheckNoMoreListItems("registeredClients", iter_0, 2)); + } + } + break; + case 20: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + break; + case 21: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + { + chip::app::DataModel::DecodableList< + chip::app::Clusters::IcdManagement::Structs::MonitoringRegistrationStruct::DecodableType> + value; + VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); + { + auto iter_0 = value.begin(); + VerifyOrReturn(CheckNextListItemDecodes("registeredClients", iter_0, 0)); + VerifyOrReturn(CheckValue("registeredClients[0].checkInNodeID", iter_0.GetValue().checkInNodeID, 201ULL)); + VerifyOrReturn( + CheckValue("registeredClients[0].monitoredSubject", iter_0.GetValue().monitoredSubject, 2002ULL)); + VerifyOrReturn(CheckValueAsString( + "registeredClients[0].key", iter_0.GetValue().key, + chip::ByteSpan(chip::Uint8::from_const_char("\002\022\0422BRbr\202\222\242\262\302\322\342/"), 16))); + VerifyOrReturn(CheckNoMoreListItems("registeredClients", iter_0, 1)); + } + } + break; + case 22: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + break; + case 23: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), 0)); + { + chip::app::DataModel::DecodableList< + chip::app::Clusters::IcdManagement::Structs::MonitoringRegistrationStruct::DecodableType> + value; + VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); + { + auto iter_0 = value.begin(); + VerifyOrReturn(CheckNoMoreListItems("registeredClients", iter_0, 0)); + } + } + break; + case 24: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), EMBER_ZCL_STATUS_NOT_FOUND)); + break; + case 25: + VerifyOrReturn(CheckValue("status", chip::to_underlying(status.mStatus), EMBER_ZCL_STATUS_NOT_FOUND)); + break; + default: + LogErrorOnFailure(ContinueOnChipMainThread(CHIP_ERROR_INVALID_ARGUMENT)); + } + + if (shouldContinue) + { + ContinueOnChipMainThread(CHIP_NO_ERROR); + } + } + + CHIP_ERROR DoTestStep(uint16_t testIndex) override + { + using namespace chip::app::Clusters; + switch (testIndex) + { + case 0: { + LogStep(0, "Wait for the commissioned device to be retrieved"); + ListFreer listFreer; + chip::app::Clusters::DelayCommands::Commands::WaitForCommissionee::Type value; + value.nodeId = mNodeId.HasValue() ? mNodeId.Value() : 305414945ULL; + return WaitForCommissionee(kIdentityAlpha, value); + } + case 1: { + LogStep(1, "Read IdleModeInterval"); + return ReadAttribute(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Attributes::IdleModeInterval::Id, + true, chip::NullOptional); + } + case 2: { + LogStep(2, "Read ActiveModeInterval"); + return ReadAttribute(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, + IcdManagement::Attributes::ActiveModeInterval::Id, true, chip::NullOptional); + } + case 3: { + LogStep(3, "Read ActiveModeThreshold"); + return ReadAttribute(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, + IcdManagement::Attributes::ActiveModeThreshold::Id, true, chip::NullOptional); + } + case 4: { + LogStep(4, "Read ICDCounter"); + return ReadAttribute(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Attributes::ICDCounter::Id, true, + chip::NullOptional); + } + case 5: { + LogStep(5, "Read ClientsSupportedPerFabric"); + return ReadAttribute(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, + IcdManagement::Attributes::ClientsSupportedPerFabric::Id, true, chip::NullOptional); + } + case 6: { + LogStep(6, "Unregister 1.0"); + ListFreer listFreer; + chip::app::Clusters::IcdManagement::Commands::UnregisterClient::Type value; + value.checkInNodeID = 101ULL; + return SendCommand(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Commands::UnregisterClient::Id, + value, chip::NullOptional + + ); + } + case 7: { + LogStep(7, "Unregister 2.0"); + ListFreer listFreer; + chip::app::Clusters::IcdManagement::Commands::UnregisterClient::Type value; + value.checkInNodeID = 102ULL; + return SendCommand(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Commands::UnregisterClient::Id, + value, chip::NullOptional + + ); + } + case 8: { + LogStep(8, "Unregister 3.0"); + ListFreer listFreer; + chip::app::Clusters::IcdManagement::Commands::UnregisterClient::Type value; + value.checkInNodeID = 102ULL; + return SendCommand(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Commands::UnregisterClient::Id, + value, chip::NullOptional + + ); + } + case 9: { + LogStep(9, "Read RegisteredClients"); + return ReadAttribute(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, + IcdManagement::Attributes::RegisteredClients::Id, true, chip::NullOptional); + } + case 10: { + LogStep(10, "Register 1.0 (key too short)"); + ListFreer listFreer; + chip::app::Clusters::IcdManagement::Commands::RegisterClient::Type value; + value.checkInNodeID = 101ULL; + value.monitoredSubject = 1001ULL; + value.key = + chip::ByteSpan(chip::Uint8::from_const_char( + "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036garbage: not in length on purpose"), + 15); + return SendCommand(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Commands::RegisterClient::Id, + value, chip::NullOptional + + ); + } + case 11: { + LogStep(11, "Register 1.0 (key too long)"); + ListFreer listFreer; + chip::app::Clusters::IcdManagement::Commands::RegisterClient::Type value; + value.checkInNodeID = 101ULL; + value.monitoredSubject = 1001ULL; + value.key = chip::ByteSpan( + chip::Uint8::from_const_char( + "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\377garbage: not in length on purpose"), + 17); + return SendCommand(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Commands::RegisterClient::Id, + value, chip::NullOptional + + ); + } + case 12: { + LogStep(12, "Register 1.1"); + ListFreer listFreer; + chip::app::Clusters::IcdManagement::Commands::RegisterClient::Type value; + value.checkInNodeID = 101ULL; + value.monitoredSubject = 1001ULL; + value.key = chip::ByteSpan( + chip::Uint8::from_const_char( + "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037garbage: not in length on purpose"), + 16); + return SendCommand(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Commands::RegisterClient::Id, + value, chip::NullOptional + + ); + } + case 13: { + LogStep(13, "Register 2.1"); + ListFreer listFreer; + chip::app::Clusters::IcdManagement::Commands::RegisterClient::Type value; + value.checkInNodeID = 201ULL; + value.monitoredSubject = 2001ULL; + value.key = chip::ByteSpan(chip::Uint8::from_const_char(" !\042#$%&'()*+,-./garbage: not in length on purpose"), 16); + return SendCommand(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Commands::RegisterClient::Id, + value, chip::NullOptional + + ); + } + case 14: { + LogStep(14, "Register 3.1"); + ListFreer listFreer; + chip::app::Clusters::IcdManagement::Commands::RegisterClient::Type value; + value.checkInNodeID = 301ULL; + value.monitoredSubject = 3001ULL; + value.key = chip::ByteSpan(chip::Uint8::from_const_char("0123456789:;<=>?garbage: not in length on purpose"), 16); + return SendCommand(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Commands::RegisterClient::Id, + value, chip::NullOptional + + ); + } + case 15: { + LogStep(15, "Read RegisteredClients"); + return ReadAttribute(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, + IcdManagement::Attributes::RegisteredClients::Id, true, chip::NullOptional); + } + case 16: { + LogStep(16, "Register 1.1"); + ListFreer listFreer; + chip::app::Clusters::IcdManagement::Commands::RegisterClient::Type value; + value.checkInNodeID = 101ULL; + value.monitoredSubject = 1002ULL; + value.key = chip::ByteSpan( + chip::Uint8::from_const_char("\001\021!1AQaq\201\221\241\261\301\321\341\361garbage: not in length on purpose"), + 16); + return SendCommand(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Commands::RegisterClient::Id, + value, chip::NullOptional + + ); + } + case 17: { + LogStep(17, "Read RegisteredClients"); + return ReadAttribute(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, + IcdManagement::Attributes::RegisteredClients::Id, true, chip::NullOptional); + } + case 18: { + LogStep(18, "Register 2.2 (wrong verification key)"); + ListFreer listFreer; + chip::app::Clusters::IcdManagement::Commands::RegisterClient::Type value; + value.checkInNodeID = 201ULL; + value.monitoredSubject = 2002ULL; + value.key = chip::ByteSpan( + chip::Uint8::from_const_char("\002\022\0422BRbr\202\222\242\262\302\322\342/garbage: not in length on purpose"), + 16); + value.verificationKey.Emplace(); + value.verificationKey.Value() = + chip::ByteSpan(chip::Uint8::from_const_char(" !\042#$%&'()*+,-//garbage: not in length on purpose"), 16); + return SendCommand(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Commands::RegisterClient::Id, + value, chip::NullOptional + + ); + } + case 19: { + LogStep(19, "Read RegisteredClients"); + return ReadAttribute(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, + IcdManagement::Attributes::RegisteredClients::Id, true, chip::NullOptional); + } + case 20: { + LogStep(20, "Unregister 1.1 (wrong key)"); + ListFreer listFreer; + chip::app::Clusters::IcdManagement::Commands::UnregisterClient::Type value; + value.checkInNodeID = 101ULL; + value.key.Emplace(); + value.key.Value() = chip::ByteSpan( + chip::Uint8::from_const_char("\001!!1AQaq\201\221\241\261\301\321\341\361garbage: not in length on purpose"), 16); + return SendCommand(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Commands::UnregisterClient::Id, + value, chip::NullOptional + + ); + } + case 21: { + LogStep(21, "Read RegisteredClients"); + return ReadAttribute(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, + IcdManagement::Attributes::RegisteredClients::Id, true, chip::NullOptional); + } + case 22: { + LogStep(22, "Unregister 2.1"); + ListFreer listFreer; + chip::app::Clusters::IcdManagement::Commands::UnregisterClient::Type value; + value.checkInNodeID = 201ULL; + return SendCommand(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Commands::UnregisterClient::Id, + value, chip::NullOptional + + ); + } + case 23: { + LogStep(23, "Read RegisteredClients"); + return ReadAttribute(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, + IcdManagement::Attributes::RegisteredClients::Id, true, chip::NullOptional); + } + case 24: { + LogStep(24, "Unregister 1.3"); + ListFreer listFreer; + chip::app::Clusters::IcdManagement::Commands::UnregisterClient::Type value; + value.checkInNodeID = 101ULL; + return SendCommand(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Commands::UnregisterClient::Id, + value, chip::NullOptional + + ); + } + case 25: { + LogStep(25, "Unregister 2.2"); + ListFreer listFreer; + chip::app::Clusters::IcdManagement::Commands::UnregisterClient::Type value; + value.checkInNodeID = 102ULL; + return SendCommand(kIdentityAlpha, GetEndpoint(0), IcdManagement::Id, IcdManagement::Commands::UnregisterClient::Id, + value, chip::NullOptional + + ); + } + } + return CHIP_NO_ERROR; + } +}; + class Test_TC_OPCREDS_1_2Suite : public TestCommand { public: @@ -122112,6 +122637,7 @@ void registerCommandsTests(Commands & commands, CredentialIssuerCommands * creds make_unique(credsIssuerConfig), make_unique(credsIssuerConfig), make_unique(credsIssuerConfig), + make_unique(credsIssuerConfig), make_unique(credsIssuerConfig), make_unique(credsIssuerConfig), make_unique(credsIssuerConfig),