Add unit test for EcosystemInformation cluster server implementation (#35953)
diff --git a/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp
index 4880c10..5793ebf 100644
--- a/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp
+++ b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp
@@ -215,9 +215,11 @@
std::unique_ptr<EcosystemLocationStruct> EcosystemLocationStruct::Builder::Build()
{
VerifyOrReturnValue(!mIsAlreadyBuilt, nullptr, ChipLogError(Zcl, "Build() already called"));
- VerifyOrReturnValue(!mLocationDescriptor.mLocationName.empty(), nullptr, ChipLogError(Zcl, "Must Provided Location Name"));
- VerifyOrReturnValue(mLocationDescriptor.mLocationName.size() <= kLocationDescriptorNameMaxSize, nullptr,
- ChipLogError(Zcl, "Must Location Name must be less than 64 bytes"));
+ VerifyOrReturnValue(!mLocationDescriptor.mLocationName.empty(), nullptr, ChipLogError(Zcl, "Must Provide Location Name"));
+ static_assert(kLocationDescriptorNameMaxSize <= std::numeric_limits<uint16_t>::max());
+ VerifyOrReturnValue(
+ mLocationDescriptor.mLocationName.size() <= kLocationDescriptorNameMaxSize, nullptr,
+ ChipLogError(Zcl, "Location Name must be less than %u bytes", static_cast<uint16_t>(kLocationDescriptorNameMaxSize)));
// std::make_unique does not have access to private constructor we workaround with using new
std::unique_ptr<EcosystemLocationStruct> ret{ new EcosystemLocationStruct(std::move(mLocationDescriptor),
@@ -271,6 +273,7 @@
VerifyOrReturnError(aLocation, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError((aEndpoint != kRootEndpointId && aEndpoint != kInvalidEndpointId), CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(!aLocationId.empty(), CHIP_ERROR_INVALID_ARGUMENT);
+ VerifyOrReturnError(aLocationId.size() <= kUniqueLocationIdMaxSize, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(aFabricIndex >= kMinValidFabricIndex, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(aFabricIndex <= kMaxValidFabricIndex, CHIP_ERROR_INVALID_ARGUMENT);
diff --git a/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h
index 3fe1621..13d2bc8 100644
--- a/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h
+++ b/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h
@@ -26,6 +26,7 @@
#include <vector>
#include <app-common/zap-generated/cluster-objects.h>
+#include <app-common/zap-generated/ids/Clusters.h>
#include <app/AttributeAccessInterface.h>
diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn
index 104a57a..e3ad975 100644
--- a/src/app/tests/BUILD.gn
+++ b/src/app/tests/BUILD.gn
@@ -154,6 +154,18 @@
]
}
+source_set("ecosystem-information-test-srcs") {
+ sources = [
+ "${chip_root}/src/app/clusters/ecosystem-information-server/ecosystem-information-server.cpp",
+ "${chip_root}/src/app/clusters/ecosystem-information-server/ecosystem-information-server.h",
+ ]
+ public_deps = [
+ "${chip_root}/src/app:interaction-model",
+ "${chip_root}/src/app/common:cluster-objects",
+ "${chip_root}/src/lib/core",
+ ]
+}
+
source_set("app-test-stubs") {
sources = [
"test-ember-api.cpp",
@@ -201,6 +213,7 @@
"TestDataModelSerialization.cpp",
"TestDefaultOTARequestorStorage.cpp",
"TestDefaultThreadNetworkDirectoryStorage.cpp",
+ "TestEcosystemInformationCluster.cpp",
"TestEventLoggingNoUTCTime.cpp",
"TestEventOverflow.cpp",
"TestEventPathParams.cpp",
@@ -228,6 +241,7 @@
public_deps = [
":app-test-stubs",
":binding-test-srcs",
+ ":ecosystem-information-test-srcs",
":operational-state-test-srcs",
":ota-requestor-test-srcs",
":power-cluster-test-srcs",
@@ -236,6 +250,7 @@
"${chip_root}/src/app",
"${chip_root}/src/app/codegen-data-model-provider:instance-header",
"${chip_root}/src/app/common:cluster-objects",
+ "${chip_root}/src/app/data-model-provider/tests:encode-decode",
"${chip_root}/src/app/icd/client:manager",
"${chip_root}/src/app/tests:helpers",
"${chip_root}/src/app/util/mock:mock_codegen_data_model",
diff --git a/src/app/tests/TestEcosystemInformationCluster.cpp b/src/app/tests/TestEcosystemInformationCluster.cpp
new file mode 100644
index 0000000..f8d9f34
--- /dev/null
+++ b/src/app/tests/TestEcosystemInformationCluster.cpp
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "lib/support/CHIPMem.h"
+
+#include <app/clusters/ecosystem-information-server/ecosystem-information-server.h>
+#include <app/data-model-provider/tests/ReadTesting.h>
+#include <pw_unit_test/framework.h>
+
+namespace chip {
+namespace app {
+namespace {
+
+using namespace Clusters;
+using namespace Clusters::EcosystemInformation;
+
+const EndpointId kValidEndpointId = 1;
+const Structs::DeviceTypeStruct::Type kValidDeviceType = { .deviceType = 0, .revision = 1 };
+constexpr Access::SubjectDescriptor kSubjectDescriptor = Testing::kAdminSubjectDescriptor;
+const FabricIndex kValidFabricIndex = kSubjectDescriptor.fabricIndex;
+
+struct RequiredEcosystemDeviceParams
+{
+ EndpointId originalEndpointId = kValidEndpointId;
+ Structs::DeviceTypeStruct::Type deviceType = kValidDeviceType;
+ FabricIndex fabicIndex = kValidFabricIndex;
+};
+
+const RequiredEcosystemDeviceParams kDefaultRequiredDeviceParams;
+
+const EndpointId kAnotherValidEndpointId = 2;
+static_assert(kValidEndpointId != kAnotherValidEndpointId);
+const char * kValidLocationName = "AValidLocationName";
+const ClusterId kEcosystemInfoClusterId = EcosystemInformation::Id;
+const AttributeId kDeviceDirectoryAttributeId = EcosystemInformation::Attributes::DeviceDirectory::Id;
+const AttributeId kLocationDirectoryAttributeId = EcosystemInformation::Attributes::LocationDirectory::Id;
+
+} // namespace
+
+class TestEcosystemInformationCluster : public ::testing::Test
+{
+public:
+ static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); }
+ static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); }
+
+ Clusters::EcosystemInformation::EcosystemInformationServer & EcoInfoCluster() { return mClusterServer; }
+
+ std::unique_ptr<EcosystemDeviceStruct>
+ CreateSimplestValidDeviceStruct(const RequiredEcosystemDeviceParams & requiredParams = kDefaultRequiredDeviceParams)
+ {
+ std::unique_ptr<EcosystemDeviceStruct> deviceInfo = EcosystemDeviceStruct::Builder()
+ .SetOriginalEndpoint(requiredParams.originalEndpointId)
+ .AddDeviceType(requiredParams.deviceType)
+ .SetFabricIndex(requiredParams.fabicIndex)
+ .Build();
+ VerifyOrDie(deviceInfo);
+ return deviceInfo;
+ }
+
+ std::unique_ptr<EcosystemLocationStruct> CreateValidLocationStruct(const char * requiredLocationName = kValidLocationName)
+ {
+ std::string locationName(requiredLocationName);
+ std::unique_ptr<EcosystemLocationStruct> locationInfo =
+ EcosystemLocationStruct::Builder().SetLocationName(locationName).Build();
+ VerifyOrDie(locationInfo);
+ return locationInfo;
+ }
+
+private:
+ Clusters::EcosystemInformation::EcosystemInformationServer mClusterServer;
+};
+
+TEST_F(TestEcosystemInformationCluster, UnsupportedClusterWhenReadingDeviceDirectoryOnNewClusterServer)
+{
+ ConcreteAttributePath path(kValidEndpointId, kEcosystemInfoClusterId, kDeviceDirectoryAttributeId);
+
+ Testing::ReadOperation testRequest(path);
+ std::unique_ptr<AttributeValueEncoder> encoder = testRequest.StartEncoding();
+
+ ASSERT_EQ(EcoInfoCluster().ReadAttribute(path, *encoder), CHIP_IM_GLOBAL_STATUS(UnsupportedCluster));
+}
+
+TEST_F(TestEcosystemInformationCluster, UnsupportedClusterWhenReadingLocationDirectoryOnNewClusterServer)
+{
+ ConcreteAttributePath path(kValidEndpointId, kEcosystemInfoClusterId, kLocationDirectoryAttributeId);
+
+ Testing::ReadOperation testRequest(path);
+ std::unique_ptr<AttributeValueEncoder> encoder = testRequest.StartEncoding();
+
+ ASSERT_EQ(EcoInfoCluster().ReadAttribute(path, *encoder), CHIP_IM_GLOBAL_STATUS(UnsupportedCluster));
+}
+
+TEST_F(TestEcosystemInformationCluster, EmptyReadAfterAddEcosystemInformationClusterToEndpoint)
+{
+ ConcreteAttributePath deviceDirectoryPath(kValidEndpointId, kEcosystemInfoClusterId, kDeviceDirectoryAttributeId);
+ ConcreteAttributePath locationDirectoryPath(kValidEndpointId, kEcosystemInfoClusterId, kLocationDirectoryAttributeId);
+
+ ASSERT_EQ(EcoInfoCluster().AddEcosystemInformationClusterToEndpoint(kValidEndpointId), CHIP_NO_ERROR);
+
+ Testing::ReadOperation testDeviceDirectoryRequest(deviceDirectoryPath);
+ std::unique_ptr<AttributeValueEncoder> deviceDirectoryEncoder = testDeviceDirectoryRequest.StartEncoding();
+ ASSERT_EQ(EcoInfoCluster().ReadAttribute(deviceDirectoryPath, *deviceDirectoryEncoder), CHIP_NO_ERROR);
+ ASSERT_EQ(testDeviceDirectoryRequest.FinishEncoding(), CHIP_NO_ERROR);
+ std::vector<Testing::DecodedAttributeData> deviceDirectoryAttributeData;
+ ASSERT_EQ(testDeviceDirectoryRequest.GetEncodedIBs().Decode(deviceDirectoryAttributeData), CHIP_NO_ERROR);
+ ASSERT_EQ(deviceDirectoryAttributeData.size(), 1u);
+ Testing::DecodedAttributeData & deviceDirectoryEncodedData = deviceDirectoryAttributeData[0];
+ ASSERT_EQ(deviceDirectoryEncodedData.attributePath, testDeviceDirectoryRequest.GetRequest().path);
+ EcosystemInformation::Attributes::DeviceDirectory::TypeInfo::DecodableType decodableDeviceDirectory;
+ ASSERT_EQ(decodableDeviceDirectory.Decode(deviceDirectoryEncodedData.dataReader), CHIP_NO_ERROR);
+ size_t deviceDirectorySize = 0;
+ ASSERT_EQ(decodableDeviceDirectory.ComputeSize(&deviceDirectorySize), CHIP_NO_ERROR);
+ ASSERT_EQ(deviceDirectorySize, 0u);
+
+ Testing::ReadOperation testLocationDirectoryRequest(locationDirectoryPath);
+ std::unique_ptr<AttributeValueEncoder> locationDirectoryEncoder = testLocationDirectoryRequest.StartEncoding();
+ ASSERT_EQ(EcoInfoCluster().ReadAttribute(locationDirectoryPath, *locationDirectoryEncoder), CHIP_NO_ERROR);
+ ASSERT_EQ(testLocationDirectoryRequest.FinishEncoding(), CHIP_NO_ERROR);
+ std::vector<Testing::DecodedAttributeData> locationDirectoryAttributeData;
+ ASSERT_EQ(testLocationDirectoryRequest.GetEncodedIBs().Decode(locationDirectoryAttributeData), CHIP_NO_ERROR);
+ ASSERT_EQ(locationDirectoryAttributeData.size(), 1u);
+ Testing::DecodedAttributeData & locationDirectoryEncodedData = locationDirectoryAttributeData[0];
+ ASSERT_EQ(locationDirectoryEncodedData.attributePath, testLocationDirectoryRequest.GetRequest().path);
+ EcosystemInformation::Attributes::LocationDirectory::TypeInfo::DecodableType decodableLocationDirectory;
+ ASSERT_EQ(decodableLocationDirectory.Decode(locationDirectoryEncodedData.dataReader), CHIP_NO_ERROR);
+ size_t locationDirectorySize = 0;
+ ASSERT_EQ(decodableLocationDirectory.ComputeSize(&locationDirectorySize), CHIP_NO_ERROR);
+ ASSERT_EQ(locationDirectorySize, 0u);
+}
+
+TEST_F(TestEcosystemInformationCluster, BuildingEcosystemDeviceStruct)
+{
+ EcosystemDeviceStruct::Builder deviceInfoBuilder;
+ std::unique_ptr<EcosystemDeviceStruct> deviceInfo = deviceInfoBuilder.Build();
+ ASSERT_FALSE(deviceInfo);
+
+ deviceInfoBuilder.SetOriginalEndpoint(1);
+ deviceInfo = deviceInfoBuilder.Build();
+ ASSERT_FALSE(deviceInfo);
+
+ auto deviceType = Structs::DeviceTypeStruct::Type();
+ deviceType.revision = 1;
+ deviceInfoBuilder.AddDeviceType(deviceType);
+ deviceInfo = deviceInfoBuilder.Build();
+ ASSERT_FALSE(deviceInfo);
+
+ deviceInfoBuilder.SetFabricIndex(1);
+ deviceInfo = deviceInfoBuilder.Build();
+ ASSERT_TRUE(deviceInfo);
+
+ // Building a second device info with previously successfully built deviceInfoBuilder
+ // is expected to fail.
+ std::unique_ptr<EcosystemDeviceStruct> secondDeviceInfo = deviceInfoBuilder.Build();
+ ASSERT_FALSE(secondDeviceInfo);
+}
+
+TEST_F(TestEcosystemInformationCluster, BuildingInvalidEcosystemDeviceStruct)
+{
+ auto deviceType = Structs::DeviceTypeStruct::Type();
+ deviceType.revision = 1;
+ const FabricIndex kFabricIndexTooLow = 0;
+ const FabricIndex kFabricIndexTooHigh = kMaxValidFabricIndex + 1;
+
+ EcosystemDeviceStruct::Builder deviceInfoBuilder;
+ deviceInfoBuilder.SetOriginalEndpoint(1);
+ deviceInfoBuilder.AddDeviceType(deviceType);
+ deviceInfoBuilder.SetFabricIndex(kFabricIndexTooLow);
+ std::unique_ptr<EcosystemDeviceStruct> deviceInfo = deviceInfoBuilder.Build();
+ ASSERT_FALSE(deviceInfo);
+
+ deviceInfoBuilder.SetFabricIndex(kFabricIndexTooHigh);
+ deviceInfo = deviceInfoBuilder.Build();
+ ASSERT_FALSE(deviceInfo);
+
+ deviceInfoBuilder.SetFabricIndex(1);
+ // At this point deviceInfoBuilder would be able to be built successfully.
+
+ std::string nameThatsTooLong(65, 'x');
+ uint64_t nameEpochValueUs = 0; // This values doesn't matter.
+ deviceInfoBuilder.SetDeviceName(std::move(nameThatsTooLong), nameEpochValueUs);
+ deviceInfo = deviceInfoBuilder.Build();
+ ASSERT_FALSE(deviceInfo);
+
+ // Ending unit test by building something that should work just to make sure
+ // Builder isn't silently failing on building for some other reason.
+ std::string nameThatsMaxLength(64, 'x');
+ deviceInfoBuilder.SetDeviceName(std::move(nameThatsMaxLength), nameEpochValueUs);
+ deviceInfo = deviceInfoBuilder.Build();
+ ASSERT_TRUE(deviceInfo);
+}
+
+TEST_F(TestEcosystemInformationCluster, AddDeviceInfoInvalidArguments)
+{
+ ASSERT_EQ(EcoInfoCluster().AddDeviceInfo(kValidEndpointId, nullptr), CHIP_ERROR_INVALID_ARGUMENT);
+
+ std::unique_ptr<EcosystemDeviceStruct> deviceInfo = CreateSimplestValidDeviceStruct();
+ ASSERT_TRUE(deviceInfo);
+ ASSERT_EQ(EcoInfoCluster().AddDeviceInfo(kRootEndpointId, std::move(deviceInfo)), CHIP_ERROR_INVALID_ARGUMENT);
+
+ deviceInfo = CreateSimplestValidDeviceStruct();
+ ASSERT_TRUE(deviceInfo);
+ ASSERT_EQ(EcoInfoCluster().AddDeviceInfo(kInvalidEndpointId, std::move(deviceInfo)), CHIP_ERROR_INVALID_ARGUMENT);
+}
+
+TEST_F(TestEcosystemInformationCluster, AddDeviceInfo)
+{
+ std::unique_ptr<EcosystemDeviceStruct> deviceInfo = CreateSimplestValidDeviceStruct();
+ // originalEndpoint and path endpoint do not need to be the same, for that reason we use a different value for
+ // path endpoint
+ static_assert(kAnotherValidEndpointId != kValidEndpointId);
+ ASSERT_EQ(EcoInfoCluster().AddDeviceInfo(kAnotherValidEndpointId, std::move(deviceInfo)), CHIP_NO_ERROR);
+ ConcreteAttributePath deviceDirectoryPath(kAnotherValidEndpointId, kEcosystemInfoClusterId, kDeviceDirectoryAttributeId);
+ Testing::ReadOperation testDeviceDirectoryRequest(deviceDirectoryPath);
+ testDeviceDirectoryRequest.SetSubjectDescriptor(kSubjectDescriptor);
+ std::unique_ptr<AttributeValueEncoder> deviceDirectoryEncoder = testDeviceDirectoryRequest.StartEncoding();
+
+ ASSERT_EQ(EcoInfoCluster().ReadAttribute(deviceDirectoryPath, *deviceDirectoryEncoder), CHIP_NO_ERROR);
+ ASSERT_EQ(testDeviceDirectoryRequest.FinishEncoding(), CHIP_NO_ERROR);
+
+ std::vector<Testing::DecodedAttributeData> attributeData;
+ ASSERT_EQ(testDeviceDirectoryRequest.GetEncodedIBs().Decode(attributeData), CHIP_NO_ERROR);
+ ASSERT_EQ(attributeData.size(), 1u);
+ Testing::DecodedAttributeData & encodedData = attributeData[0];
+ ASSERT_EQ(encodedData.attributePath, testDeviceDirectoryRequest.GetRequest().path);
+ EcosystemInformation::Attributes::DeviceDirectory::TypeInfo::DecodableType decodableDeviceDirectory;
+ ASSERT_EQ(decodableDeviceDirectory.Decode(encodedData.dataReader), CHIP_NO_ERROR);
+ size_t size = 0;
+ ASSERT_EQ(decodableDeviceDirectory.ComputeSize(&size), CHIP_NO_ERROR);
+ ASSERT_EQ(size, 1u);
+ auto iterator = decodableDeviceDirectory.begin();
+ ASSERT_TRUE(iterator.Next());
+ auto deviceDirectoryEntry = iterator.GetValue();
+ ASSERT_FALSE(deviceDirectoryEntry.deviceName.HasValue());
+ ASSERT_FALSE(deviceDirectoryEntry.deviceNameLastEdit.HasValue());
+ ASSERT_EQ(deviceDirectoryEntry.bridgedEndpoint, kInvalidEndpointId);
+ ASSERT_EQ(deviceDirectoryEntry.originalEndpoint, kValidEndpointId);
+ size_t deviceTypeListSize = 0;
+ ASSERT_EQ(deviceDirectoryEntry.deviceTypes.ComputeSize(&deviceTypeListSize), CHIP_NO_ERROR);
+ ASSERT_EQ(deviceTypeListSize, 1u);
+ auto deviceTypeIterator = deviceDirectoryEntry.deviceTypes.begin();
+ ASSERT_TRUE(deviceTypeIterator.Next());
+ auto deviceTypeEntry = deviceTypeIterator.GetValue();
+ ASSERT_EQ(deviceTypeEntry.deviceType, 0u);
+ ASSERT_EQ(deviceTypeEntry.revision, 1);
+ ASSERT_FALSE(deviceTypeIterator.Next());
+ size_t uniqueLocationIdListSize = 0;
+ ASSERT_EQ(deviceDirectoryEntry.uniqueLocationIDs.ComputeSize(&uniqueLocationIdListSize), CHIP_NO_ERROR);
+ ASSERT_EQ(uniqueLocationIdListSize, 0u);
+ ASSERT_EQ(deviceDirectoryEntry.uniqueLocationIDsLastEdit, 0u);
+ ASSERT_EQ(deviceDirectoryEntry.fabricIndex, kSubjectDescriptor.fabricIndex);
+ ASSERT_FALSE(iterator.Next());
+}
+
+TEST_F(TestEcosystemInformationCluster, BuildingEcosystemLocationStruct)
+{
+ EcosystemLocationStruct::Builder locationInfoBuilder;
+
+ std::string validLocationName = "validName";
+ locationInfoBuilder.SetLocationName(validLocationName);
+
+ std::unique_ptr<EcosystemLocationStruct> locationInfo = locationInfoBuilder.Build();
+ ASSERT_TRUE(locationInfo);
+
+ // Building a second device info with previously successfully built deviceInfoBuilder
+ // is expected to fail.
+ locationInfo = locationInfoBuilder.Build();
+ ASSERT_FALSE(locationInfo);
+}
+
+TEST_F(TestEcosystemInformationCluster, BuildingInvalidEcosystemLocationStruct)
+{
+ EcosystemLocationStruct::Builder locationInfoBuilder;
+
+ std::string nameThatsTooLong(129, 'x');
+ locationInfoBuilder.SetLocationName(nameThatsTooLong);
+
+ std::unique_ptr<EcosystemLocationStruct> locationInfo = locationInfoBuilder.Build();
+ ASSERT_FALSE(locationInfo);
+
+ // Ending unit test by building something that should work just to make sure
+ // Builder isn't silently failing on building for some other reason.
+ std::string nameThatsMaxLength(128, 'x');
+ locationInfoBuilder.SetLocationName(nameThatsMaxLength);
+ locationInfo = locationInfoBuilder.Build();
+ ASSERT_TRUE(locationInfo);
+}
+
+TEST_F(TestEcosystemInformationCluster, AddLocationInfoInvalidArguments)
+{
+ const FabricIndex kFabricIndexTooLow = 0;
+ const FabricIndex kFabricIndexTooHigh = kMaxValidFabricIndex + 1;
+ const std::string kEmptyLocationIdStr;
+ const std::string kValidLocationIdStr = "SomeLocationString";
+ const std::string kInvalidLocationIdTooLongStr(65, 'x');
+
+ std::unique_ptr<EcosystemLocationStruct> locationInfo = CreateValidLocationStruct();
+ ASSERT_TRUE(locationInfo);
+ ASSERT_EQ(EcoInfoCluster().AddLocationInfo(kInvalidEndpointId, kValidLocationIdStr, kValidFabricIndex, std::move(locationInfo)),
+ CHIP_ERROR_INVALID_ARGUMENT);
+
+ locationInfo = CreateValidLocationStruct();
+ ASSERT_TRUE(locationInfo);
+ ASSERT_EQ(EcoInfoCluster().AddLocationInfo(kRootEndpointId, kValidLocationIdStr, kValidFabricIndex, std::move(locationInfo)),
+ CHIP_ERROR_INVALID_ARGUMENT);
+
+ locationInfo = CreateValidLocationStruct();
+ ASSERT_TRUE(locationInfo);
+ ASSERT_EQ(EcoInfoCluster().AddLocationInfo(kValidEndpointId, kEmptyLocationIdStr, kValidFabricIndex, std::move(locationInfo)),
+ CHIP_ERROR_INVALID_ARGUMENT);
+
+ locationInfo = CreateValidLocationStruct();
+ ASSERT_TRUE(locationInfo);
+ ASSERT_EQ(EcoInfoCluster().AddLocationInfo(kValidEndpointId, kInvalidLocationIdTooLongStr, kValidFabricIndex,
+ std::move(locationInfo)),
+ CHIP_ERROR_INVALID_ARGUMENT);
+
+ locationInfo = CreateValidLocationStruct();
+ ASSERT_TRUE(locationInfo);
+ ASSERT_EQ(EcoInfoCluster().AddLocationInfo(kValidEndpointId, kValidLocationIdStr, kFabricIndexTooLow, std::move(locationInfo)),
+ CHIP_ERROR_INVALID_ARGUMENT);
+
+ locationInfo = CreateValidLocationStruct();
+ ASSERT_TRUE(locationInfo);
+ ASSERT_EQ(EcoInfoCluster().AddLocationInfo(kValidEndpointId, kValidLocationIdStr, kFabricIndexTooHigh, std::move(locationInfo)),
+ CHIP_ERROR_INVALID_ARGUMENT);
+
+ // Sanity check that we can successfully add something after all the previously failed attempts
+ locationInfo = CreateValidLocationStruct();
+ ASSERT_TRUE(locationInfo);
+ ASSERT_EQ(EcoInfoCluster().AddLocationInfo(kValidEndpointId, kValidLocationIdStr, kValidFabricIndex, std::move(locationInfo)),
+ CHIP_NO_ERROR);
+
+ // Adding a second identical entry is expected to fail
+ locationInfo = CreateValidLocationStruct();
+ ASSERT_TRUE(locationInfo);
+ ASSERT_EQ(EcoInfoCluster().AddLocationInfo(kValidEndpointId, kValidLocationIdStr, kValidFabricIndex, std::move(locationInfo)),
+ CHIP_ERROR_INVALID_ARGUMENT);
+}
+
+TEST_F(TestEcosystemInformationCluster, AddLocationInfo)
+{
+ std::unique_ptr<EcosystemLocationStruct> locationInfo = CreateValidLocationStruct();
+ const char * kValidLocationIdStr = "SomeLocationIdString";
+ ASSERT_EQ(EcoInfoCluster().AddLocationInfo(kValidEndpointId, kValidLocationIdStr, Testing::kAdminSubjectDescriptor.fabricIndex,
+ std::move(locationInfo)),
+ CHIP_NO_ERROR);
+
+ ConcreteAttributePath locationDirectoryPath(kValidEndpointId, kEcosystemInfoClusterId, kLocationDirectoryAttributeId);
+ Testing::ReadOperation testLocationDirectoryRequest(locationDirectoryPath);
+ testLocationDirectoryRequest.SetSubjectDescriptor(Testing::kAdminSubjectDescriptor);
+ std::unique_ptr<AttributeValueEncoder> locationDirectoryEncoder = testLocationDirectoryRequest.StartEncoding();
+ ASSERT_EQ(EcoInfoCluster().ReadAttribute(locationDirectoryPath, *locationDirectoryEncoder), CHIP_NO_ERROR);
+ ASSERT_EQ(testLocationDirectoryRequest.FinishEncoding(), CHIP_NO_ERROR);
+
+ std::vector<Testing::DecodedAttributeData> locationDirectoryAttributeData;
+ ASSERT_EQ(testLocationDirectoryRequest.GetEncodedIBs().Decode(locationDirectoryAttributeData), CHIP_NO_ERROR);
+ ASSERT_EQ(locationDirectoryAttributeData.size(), 1u);
+ Testing::DecodedAttributeData & locationDirectoryEncodedData = locationDirectoryAttributeData[0];
+ ASSERT_EQ(locationDirectoryEncodedData.attributePath, testLocationDirectoryRequest.GetRequest().path);
+ EcosystemInformation::Attributes::LocationDirectory::TypeInfo::DecodableType decodableLocationDirectory;
+ ASSERT_EQ(decodableLocationDirectory.Decode(locationDirectoryEncodedData.dataReader), CHIP_NO_ERROR);
+ size_t locationDirectorySize = 0;
+ ASSERT_EQ(decodableLocationDirectory.ComputeSize(&locationDirectorySize), CHIP_NO_ERROR);
+ ASSERT_EQ(locationDirectorySize, 1u);
+ auto iterator = decodableLocationDirectory.begin();
+ ASSERT_TRUE(iterator.Next());
+ auto locationDirectoryEntry = iterator.GetValue();
+ ASSERT_TRUE(locationDirectoryEntry.uniqueLocationID.data_equal(CharSpan::fromCharString(kValidLocationIdStr)));
+ ASSERT_TRUE(locationDirectoryEntry.locationDescriptor.locationName.data_equal(CharSpan::fromCharString(kValidLocationName)));
+ ASSERT_TRUE(locationDirectoryEntry.locationDescriptor.floorNumber.IsNull());
+ ASSERT_TRUE(locationDirectoryEntry.locationDescriptor.areaType.IsNull());
+ ASSERT_EQ(locationDirectoryEntry.locationDescriptorLastEdit, 0u);
+ ASSERT_EQ(locationDirectoryEntry.fabricIndex, Testing::kAdminSubjectDescriptor.fabricIndex);
+ ASSERT_FALSE(iterator.Next());
+}
+
+} // namespace app
+} // namespace chip