Skip to content

Commit

Permalink
[IC-Device] Initial implementation of Client Monitoring Registration …
Browse files Browse the repository at this point in the history
…table (#24096)

* Client Monitoring table implementation

* CM table tests

* Add sub-struct to add a validation function
Add unit test for invalid values

* Address PR comments

* Apply suggestions from code review

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

* Increase max unit test limit

* Apply suggestions from code review

Co-authored-by: Damian Królik <66667989+Damian-Nordic@users.noreply.github.com>

* Move comments to headeR

* update test assert

* Add provisional comment

* restyle

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
Co-authored-by: Damian Królik <66667989+Damian-Nordic@users.noreply.github.com>
  • Loading branch information
3 people authored and pull[bot] committed Jan 19, 2024
1 parent 53a75f4 commit 1183371
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 48 deletions.
14 changes: 14 additions & 0 deletions src/app/tests/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ 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("ota-requestor-test-srcs") {
sources = [
"${chip_root}/src/app/clusters/ota-requestor/DefaultOTARequestorStorage.cpp",
Expand All @@ -77,6 +89,7 @@ chip_test_suite("tests") {
"TestAttributeValueEncoder.cpp",
"TestBindingTable.cpp",
"TestBuilderParser.cpp",
"TestClientMonitoringRegistrationTable.cpp",
"TestClusterInfo.cpp",
"TestCommandInteraction.cpp",
"TestCommandPathParams.cpp",
Expand Down Expand Up @@ -117,6 +130,7 @@ chip_test_suite("tests") {

public_deps = [
":binding-test-srcs",
":client-monitoring-test-srcs",
":ota-requestor-test-srcs",
"${chip_root}/src/app",
"${chip_root}/src/app/common:cluster-objects",
Expand Down
141 changes: 141 additions & 0 deletions src/app/tests/TestClientMonitoringRegistrationTable.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
*
* 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 <app/util/ClientMonitoringRegistrationTable.h>
#include <lib/core/CHIPError.h>
#include <lib/support/DefaultStorageKeyAllocator.h>
#include <lib/support/TestPersistentStorageDelegate.h>
#include <lib/support/UnitTestRegistration.h>
#include <nlunit-test.h>

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);
}

} // 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_SENTINEL() };

nlTestSuite cmSuite = { "TestClientMonitoringRegistrationTable", &sTests[0], nullptr, nullptr };

nlTestRunner(&cmSuite, nullptr);
return (nlTestRunnerStats(&cmSuite));
}

CHIP_REGISTER_TEST_SUITE(TestClientMonitoringRegistrationTable)
62 changes: 28 additions & 34 deletions src/app/util/ClientMonitoringRegistrationTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,59 +17,53 @@

#include "ClientMonitoringRegistrationTable.h"

namespace chip {
#include <lib/support/DefaultStorageKeyAllocator.h>

/**********************************************************
* Attributes Definition
*********************************************************/
namespace chip {

/**********************************************************
* ClientMonitoringRegistrationTable Implementation
*********************************************************/

ClientMonitoringRegistrationTable::ClientMonitoringRegistrationTable(FabricIndex fabricIndex)
{
this->LoadFromStorage(fabricIndex);
}
ClientMonitoringRegistrationTable::ClientMonitoringRegistrationTable(PersistentStorageDelegate & storage) : mStorage(storage) {}

void ClientMonitoringRegistrationTable::LoadFromStorage(FabricIndex fabricIndex)
CHIP_ERROR ClientMonitoringRegistrationTable::LoadFromStorage(FabricIndex fabricIndex)
{
// TODO: Implement load from NVM logic
}
uint8_t buffer[kRegStorageSize] = { 0 };
uint16_t size = sizeof(buffer);

void ClientMonitoringRegistrationTable::SaveToStorage()
{
// Store to NVM based of class attributes
}
ReturnErrorOnFailure(
mStorage.SyncGetKeyValue(DefaultStorageKeyAllocator::ClientMonitoringTableEntry(fabricIndex).KeyName(), buffer, size));

NodeId ClientMonitoringRegistrationTable::getClientNodeId()
{
return mRegisteredClient.clientNodeId;
}
TLV::TLVReader reader;
reader.Init(buffer, size);
ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));

uint64_t ClientMonitoringRegistrationTable::getICid()
{
return mRegisteredClient.ICid;
}
ReturnErrorOnFailure(mRegisteredClient.Decode(reader));

FabricIndex ClientMonitoringRegistrationTable::getFaricIndex()
{
return mRegisteredClient.fabricIndex;
}
mRegisteredClient.fabricIndex = fabricIndex;

void ClientMonitoringRegistrationTable::setClientNodeId(NodeId clientNodeId)
{
mRegisteredClient.clientNodeId = clientNodeId;
return CHIP_NO_ERROR;
}

void ClientMonitoringRegistrationTable::setICid(uint64_t ICid)
CHIP_ERROR ClientMonitoringRegistrationTable::SaveToStorage()
{
mRegisteredClient.ICid = ICid;
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<uint16_t>(writer.GetLengthWritten()));
}

void ClientMonitoringRegistrationTable::setFabricIndex(FabricIndex fabric)
ClientMonitoringRegistrationTable::ClientRegistrationEntry & ClientMonitoringRegistrationTable::GetClientRegistrationEntry()
{
mRegisteredClient.fabricIndex = fabric;
return mRegisteredClient;
}

} // namespace chip
55 changes: 42 additions & 13 deletions src/app/util/ClientMonitoringRegistrationTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,62 @@

#include <app-common/zap-generated/cluster-objects.h>
#include <lib/core/CHIPConfig.h>
#include <lib/core/CHIPPersistentStorageDelegate.h>
#include <lib/core/DataModelTypes.h>
#include <lib/support/CodeUtils.h>
#include <platform/CHIPDeviceConfig.h>

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.
*/
class ClientMonitoringRegistrationTable
{
public:
using MonitoringRegistrationStruct = chip::app::Clusters::ClientMonitoring::Structs::MonitoringRegistration::Type;

ClientMonitoringRegistrationTable(FabricIndex fabricIndex);
struct ClientRegistrationEntry : MonitoringRegistrationStruct
{
bool IsValid() { return clientNodeId != kUndefinedNodeId && ICid != kInvalidIcId && fabricIndex != kUndefinedFabricIndex; }
};

ClientMonitoringRegistrationTable(PersistentStorageDelegate & storage);
~ClientMonitoringRegistrationTable(){};

void SaveToStorage();
/**
* @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();

// Getter
NodeId getClientNodeId();
uint64_t getICid();
FabricIndex getFaricIndex();
/**
* @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);

// Setter
void setClientNodeId(NodeId clientNodeId);
void setICid(uint64_t ICid);
void setFabricIndex(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:
void LoadFromStorage(FabricIndex fabricIndex);
MonitoringRegistrationStruct mRegisteredClient;
static constexpr uint8_t kRegStorageSize = TLV::EstimateStructOverhead(sizeof(NodeId), sizeof(uint64_t));

ClientRegistrationEntry mRegisteredClient;
PersistentStorageDelegate & mStorage;
};

} // namespace chip
4 changes: 4 additions & 0 deletions src/lib/core/DataModelTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ constexpr EndpointId kRootEndpointId = 0;
constexpr ListIndex kInvalidListIndex = 0xFFFF; // List index is a uint16 thus 0xFFFF is a invalid list index.
constexpr KeysetId kInvalidKeysetId = 0xFFFF;

// Invalid IC identifier is provisional. Value will most likely change when identifying token is defined
// https://github.com/project-chip/connectedhomeip/issues/24251
constexpr uint64_t kInvalidIcId = 0;

// These are MEIs, 0xFFFF is not a valid manufacturer code,
// thus 0xFFFF'FFFF is not a valid MEI.
static constexpr ClusterId kInvalidClusterId = 0xFFFF'FFFF;
Expand Down
7 changes: 7 additions & 0 deletions src/lib/support/DefaultStorageKeyAllocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ 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

static StorageKeyName ClientMonitoringTableEntry(chip::FabricIndex fabric)
{
return StorageKeyName::Formatted("f/%x/cm", fabric);
}

static StorageKeyName OTADefaultProviders() { return StorageKeyName::FromConst("g/o/dp"); }
static StorageKeyName OTACurrentProvider() { return StorageKeyName::FromConst("g/o/cp"); }
static StorageKeyName OTAUpdateToken() { return StorageKeyName::FromConst("g/o/ut"); }
Expand Down
2 changes: 1 addition & 1 deletion src/lib/support/UnitTestRegistration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

namespace chip {

const size_t kTestSuitesMax = 128;
const size_t kTestSuitesMax = 256;

typedef struct
{
Expand Down

0 comments on commit 1183371

Please sign in to comment.