Skip to content

Commit

Permalink
Add a "List clusters on endpoint" support into the server cluster reg…
Browse files Browse the repository at this point in the history
…istry as this seems very useful
  • Loading branch information
andreilitvin committed Feb 25, 2025
1 parent b7f8e79 commit 75c7879
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 1 deletion.
15 changes: 14 additions & 1 deletion src/app/server-cluster/ServerClusterInterfaceRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@ void ClearSingleLinkedList(ServerClusterInterface * clusters)
}
}

ServerClusterInterfaceRegistry sRegistry;

} // namespace

ServerClusterInterfaceRegistry & ServerClusterInterfaceRegistry::Instance()
{
static ServerClusterInterfaceRegistry sRegistry;
return sRegistry;
}

Expand Down Expand Up @@ -155,6 +156,18 @@ ServerClusterInterface * ServerClusterInterfaceRegistry::Unregister(const Concre
return nullptr;
}

ServerClusterInterfaceRegistry::ClustersList ServerClusterInterfaceRegistry::ClustersOnEndpoint(EndpointId endpointId)
{
EndpointClusters * clusters = FindClusters(endpointId);

if (clusters == nullptr)
{
return nullptr;
}

return clusters->firstCluster;
}

void ServerClusterInterfaceRegistry::UnregisterAllFromEndpoint(EndpointId endpointId)
{
if ((mEndpointClustersCache != nullptr) && (mEndpointClustersCache->endpointId == endpointId))
Expand Down
36 changes: 36 additions & 0 deletions src/app/server-cluster/ServerClusterInterfaceRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,39 @@ namespace app {
class ServerClusterInterfaceRegistry
{
public:
/// represents an iterable list of clusters
class ClustersList
{
public:
class Iterator
{
public:
Iterator(ServerClusterInterface * interface) : mInterface(interface) {}

Iterator & operator++()
{
if (mInterface != nullptr)
{
mInterface = mInterface->GetNextListItem();
}
return *this;
}
bool operator==(const Iterator & other) const { return mInterface == other.mInterface; }
bool operator!=(const Iterator & other) const { return mInterface != other.mInterface; }
ServerClusterInterface * operator*() { return mInterface; }

private:
ServerClusterInterface * mInterface;
};

ClustersList(ServerClusterInterface * start) : mStart(start) {}
Iterator begin() { return mStart; }
Iterator end() { return nullptr; }

private:
ServerClusterInterface * mStart;
};

~ServerClusterInterfaceRegistry();

/// Associate a specific interface with the given endpoint.
Expand All @@ -51,6 +84,9 @@ class ServerClusterInterfaceRegistry
/// Return the interface registered for the given cluster path or nullptr if one does not exist
ServerClusterInterface * Get(const ConcreteClusterPath & path);

/// ClustersList is valid as a snapshot only and should not be saved.
ClustersList ClustersOnEndpoint(EndpointId endpointId);

/// Unregister all registrations for the given endpoint.
void UnregisterAllFromEndpoint(EndpointId endpointId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,59 @@ TEST_F(TestServerClusterInterfaceRegistry, TestDestructionOrder)
}
}

TEST_F(TestServerClusterInterfaceRegistry, ClustersOnEndpoint)
{
std::vector<FakeServerClusterInterface> items;

static constexpr ClusterId kClusterTestCount = 200;
static constexpr EndpointId kEndpointTestCount = 10;

static_assert(kInvalidClusterId > kClusterTestCount, "Tests assume all clusters IDs [0...] are valid");

items.reserve(kClusterTestCount);
for (ClusterId i = 0; i < kClusterTestCount; i++)
{
items.emplace_back(i);
ASSERT_FALSE(items[i].IsInList());
}

ServerClusterInterfaceRegistry registry;

// place the clusters on the respecitve endpoints
for (ClusterId i = 0; i < kClusterTestCount; i++)
{
ASSERT_EQ(registry.Register(i % kEndpointTestCount, &items[i]), CHIP_NO_ERROR);
ASSERT_TRUE(items[i].IsInList());
}

// this IS implementation defined: we always register at "HEAD" so the listing is in
// INVERSE order of registering.
for (EndpointId ep = 0; ep < kEndpointTestCount; ep++)
{
// Move to the end since we iterate in reverse order
ClusterId expectedClusterId = ep + kEndpointTestCount * (kClusterTestCount / kEndpointTestCount);
if (expectedClusterId >= kClusterTestCount)
{
expectedClusterId -= kEndpointTestCount;
}

// ensure that iteration happens exactly as we expect: reverse order and complete
for (auto cluster : registry.ClustersOnEndpoint(ep))
{
ASSERT_LT(expectedClusterId, kClusterTestCount);
ASSERT_EQ(cluster->GetClusterId(), expectedClusterId);
expectedClusterId -= kEndpointTestCount; // next expected/registered cluster
}

// Iterated through all : we overflowed and got a large number
ASSERT_GE(expectedClusterId, kClusterTestCount);
}

// invalid index works and iteration on empty lists is ok
auto clusters = registry.ClustersOnEndpoint(kEndpointTestCount + 1);
ASSERT_EQ(clusters.begin(), clusters.end());
}

TEST_F(TestServerClusterInterfaceRegistry, InstanceUsage)
{
// This tests uses the instance member, to get coverage
Expand Down

0 comments on commit 75c7879

Please sign in to comment.