[WriteClient] Adding UnitTests for PutPreencodedAttribute and other related methods (#38752)
* [WriteClient] Adding UnitTests for PutPreencodedAttribute and other related methods
* Integrating Comments
* Adding one more testcase in PreEncoded TLV
* deacativating TestWriteChunking for fake platform
* optimising: Putting repeeated testcases in a loop
* Integrating comments
* matching signs in assert
* adding maybe_unused to variable only used in progress logs
diff --git a/src/app/WriteClient.cpp b/src/app/WriteClient.cpp
index 74bddbf..8c57fed 100644
--- a/src/app/WriteClient.cpp
+++ b/src/app/WriteClient.cpp
@@ -247,7 +247,6 @@
return err;
}
-// TODO #38287 Add Unit Tests for PutPreencodedAttribute and for TryPutPreencodedAttributeWritePayloadIntoList.
CHIP_ERROR WriteClient::PutPreencodedAttribute(const ConcreteDataAttributePath & attributePath, const TLV::TLVReader & data)
{
ReturnErrorOnFailure(EnsureMessage());
diff --git a/src/app/tests/TestWriteInteraction.cpp b/src/app/tests/TestWriteInteraction.cpp
index 4736d02..6da2ffb 100644
--- a/src/app/tests/TestWriteInteraction.cpp
+++ b/src/app/tests/TestWriteInteraction.cpp
@@ -155,6 +155,12 @@
AppContext::TearDown();
}
+ enum class EncodingMethod
+ {
+ Standard, // Encoding using WriteClient::EncodeAttribute()
+ PreencodedTLV, // Encoding using WriteClient::PutPreencodedAttribute()
+ };
+
void TestWriteClient();
void TestWriteClientGroup();
void TestWriteHandlerReceiveInvalidMessage();
@@ -164,7 +170,7 @@
void TestWriteInvalidMessage3();
void TestWriteInvalidMessage4();
- static void AddAttributeDataIB(WriteClient & aWriteClient);
+ static void AddAttributeDataIB(WriteClient & aWriteClient, EncodingMethod encoding);
static void AddAttributeStatus(WriteHandler & aWriteHandler);
static void GenerateWriteRequest(bool aIsTimedWrite, System::PacketBufferHandle & aPayload);
static void GenerateWriteResponse(System::PacketBufferHandle & aPayload);
@@ -230,7 +236,7 @@
CHIP_ERROR mError = CHIP_NO_ERROR;
};
-void TestWriteInteraction::AddAttributeDataIB(WriteClient & aWriteClient)
+void TestWriteInteraction::AddAttributeDataIB(WriteClient & aWriteClient, EncodingMethod encoding = EncodingMethod::Standard)
{
AttributePathParams attributePathParams;
bool attributeValue = true;
@@ -238,7 +244,35 @@
attributePathParams.mClusterId = 3;
attributePathParams.mAttributeId = 4;
- EXPECT_EQ(aWriteClient.EncodeAttribute(attributePathParams, attributeValue), CHIP_NO_ERROR);
+ switch (encoding)
+ {
+ case EncodingMethod::Standard:
+
+ EXPECT_EQ(aWriteClient.EncodeAttribute(attributePathParams, attributeValue), CHIP_NO_ERROR);
+ break;
+
+ case EncodingMethod::PreencodedTLV:
+
+ // Encode AttributeData into TLV
+ uint8_t buffer[5];
+ TLV::TLVWriter writer;
+ writer.Init(buffer, sizeof(buffer));
+ TLV::TLVType outerContainer;
+
+ EXPECT_EQ(CHIP_NO_ERROR, writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainer));
+ EXPECT_EQ(CHIP_NO_ERROR, writer.PutBoolean(TLV::ContextTag(1), attributeValue));
+ EXPECT_EQ(CHIP_NO_ERROR, writer.EndContainer(outerContainer));
+
+ // Put Preencoded Data into AttributeDataIB
+ TLV::TLVReader reader;
+ reader.Init(buffer, writer.GetLengthWritten());
+ reader.Next();
+ EXPECT_EQ(aWriteClient.PutPreencodedAttribute(ConcreteDataAttributePath(attributePathParams.mEndpointId,
+ attributePathParams.mClusterId,
+ attributePathParams.mAttributeId),
+ reader),
+ CHIP_NO_ERROR);
+ }
}
void TestWriteInteraction::AddAttributeStatus(WriteHandler & aWriteHandler)
@@ -341,44 +375,49 @@
TEST_F_FROM_FIXTURE(TestWriteInteraction, TestWriteClient)
{
- TestWriteClientCallback callback;
- app::WriteClient writeClient(&GetExchangeManager(), &callback, /* aTimedWriteTimeoutMs = */ NullOptional);
+ for (EncodingMethod encoding : { EncodingMethod::Standard, EncodingMethod::PreencodedTLV })
+ {
+ TestWriteClientCallback callback;
+ app::WriteClient writeClient(&GetExchangeManager(), &callback, /* aTimedWriteTimeoutMs = */ NullOptional);
- System::PacketBufferHandle buf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
- AddAttributeDataIB(writeClient);
+ System::PacketBufferHandle buf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
+ AddAttributeDataIB(writeClient, encoding);
- EXPECT_EQ(writeClient.SendWriteRequest(GetSessionBobToAlice()), CHIP_NO_ERROR);
+ EXPECT_EQ(writeClient.SendWriteRequest(GetSessionBobToAlice()), CHIP_NO_ERROR);
- DrainAndServiceIO();
+ DrainAndServiceIO();
- GenerateWriteResponse(buf);
+ GenerateWriteResponse(buf);
- EXPECT_EQ(writeClient.ProcessWriteResponseMessage(std::move(buf)), CHIP_NO_ERROR);
+ EXPECT_EQ(writeClient.ProcessWriteResponseMessage(std::move(buf)), CHIP_NO_ERROR);
- writeClient.Close();
+ writeClient.Close();
- Messaging::ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr();
- EXPECT_EQ(rm->TestGetCountRetransTable(), 0);
+ Messaging::ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr();
+ EXPECT_EQ(rm->TestGetCountRetransTable(), 0);
+ }
}
TEST_F_FROM_FIXTURE(TestWriteInteraction, TestWriteClientGroup)
{
+ for (EncodingMethod encodingMethod : { EncodingMethod::Standard, EncodingMethod::PreencodedTLV })
+ {
+ TestWriteClientCallback callback;
+ app::WriteClient writeClient(&GetExchangeManager(), &callback, /* aTimedWriteTimeoutMs = */ NullOptional);
- TestWriteClientCallback callback;
- app::WriteClient writeClient(&GetExchangeManager(), &callback, /* aTimedWriteTimeoutMs = */ NullOptional);
+ System::PacketBufferHandle buf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
+ AddAttributeDataIB(writeClient, encodingMethod);
- System::PacketBufferHandle buf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
- AddAttributeDataIB(writeClient);
+ SessionHandle groupSession = GetSessionBobToFriends();
+ EXPECT_TRUE(groupSession->IsGroupSession());
- SessionHandle groupSession = GetSessionBobToFriends();
- EXPECT_TRUE(groupSession->IsGroupSession());
+ EXPECT_EQ(writeClient.SendWriteRequest(groupSession), CHIP_NO_ERROR);
- EXPECT_EQ(writeClient.SendWriteRequest(groupSession), CHIP_NO_ERROR);
+ DrainAndServiceIO();
- DrainAndServiceIO();
-
- // The WriteClient should be shutdown once we SendWriteRequest for group.
- EXPECT_EQ(writeClient.mState, WriteClient::State::AwaitingDestruction);
+ // The WriteClient should be shutdown once we SendWriteRequest for group.
+ EXPECT_EQ(writeClient.mState, WriteClient::State::AwaitingDestruction);
+ }
}
TEST_F(TestWriteInteraction, TestWriteHandler)
@@ -424,65 +463,95 @@
TEST_F(TestWriteInteraction, TestWriteRoundtripWithClusterObjects)
{
- Messaging::ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr();
- // Shouldn't have anything in the retransmit table when starting the test.
- EXPECT_EQ(rm->TestGetCountRetransTable(), 0);
-
- TestWriteClientCallback callback;
- auto * engine = chip::app::InteractionModelEngine::GetInstance();
- EXPECT_EQ(engine->Init(&GetExchangeManager(), &GetFabricTable(), app::reporting::GetDefaultReportScheduler()), CHIP_NO_ERROR);
-
- app::WriteClient writeClient(engine->GetExchangeManager(), &callback, Optional<uint16_t>::Missing());
-
- System::PacketBufferHandle buf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
-
- AttributePathParams attributePathParams;
- attributePathParams.mEndpointId = 2;
- attributePathParams.mClusterId = 3;
- attributePathParams.mAttributeId = 4;
-
- const uint8_t byteSpanData[] = { 0xde, 0xad, 0xbe, 0xef };
- static const char charSpanData[] = "a simple test string";
-
- app::Clusters::UnitTesting::Structs::SimpleStruct::Type dataTx;
- dataTx.a = 12;
- dataTx.b = true;
- dataTx.d = chip::ByteSpan(byteSpanData);
- // Spec A.11.2 strings SHALL NOT include a terminating null character to mark the end of a string.
- dataTx.e = chip::Span<const char>(charSpanData, strlen(charSpanData));
-
- EXPECT_EQ(writeClient.EncodeAttribute(attributePathParams, dataTx), CHIP_NO_ERROR);
-
- EXPECT_EQ(callback.mOnSuccessCalled, 0);
-
- EXPECT_EQ(writeClient.SendWriteRequest(GetSessionBobToAlice()), CHIP_NO_ERROR);
-
- DrainAndServiceIO();
-
- EXPECT_EQ(callback.mOnSuccessCalled, 1);
-
+ for (EncodingMethod encoding : { EncodingMethod::Standard, EncodingMethod::PreencodedTLV })
{
- app::Clusters::UnitTesting::Structs::SimpleStruct::Type dataRx;
- TLV::TLVReader reader;
- reader.Init(chip::Test::attributeDataTLV, chip::Test::attributeDataTLVLen);
- reader.Next();
- EXPECT_EQ(CHIP_NO_ERROR, DataModel::Decode(reader, dataRx));
- EXPECT_EQ(dataRx.a, dataTx.a);
- EXPECT_EQ(dataRx.b, dataTx.b);
- EXPECT_TRUE(dataRx.d.data_equal(dataTx.d));
- // Equals to dataRx.e.size() == dataTx.e.size() && memncmp(dataRx.e.data(), dataTx.e.data(), dataTx.e.size()) == 0
- EXPECT_TRUE(dataRx.e.data_equal(dataTx.e));
+ Messaging::ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr();
+ // Shouldn't have anything in the retransmit table when starting the test.
+ EXPECT_EQ(rm->TestGetCountRetransTable(), 0);
+
+ TestWriteClientCallback callback;
+ auto * engine = chip::app::InteractionModelEngine::GetInstance();
+ EXPECT_EQ(engine->Init(&GetExchangeManager(), &GetFabricTable(), app::reporting::GetDefaultReportScheduler()),
+ CHIP_NO_ERROR);
+
+ app::WriteClient writeClient(engine->GetExchangeManager(), &callback, Optional<uint16_t>::Missing());
+
+ System::PacketBufferHandle buf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
+
+ AttributePathParams attributePathParams;
+ attributePathParams.mEndpointId = 2;
+ attributePathParams.mClusterId = 3;
+ attributePathParams.mAttributeId = 4;
+
+ const uint8_t byteSpanData[] = { 0xde, 0xad, 0xbe, 0xef };
+ static const char charSpanData[] = "a simple test string";
+
+ app::Clusters::UnitTesting::Structs::SimpleStruct::Type dataTx;
+ dataTx.a = 12;
+ dataTx.b = true;
+ dataTx.d = chip::ByteSpan(byteSpanData);
+ // Spec A.11.2 strings SHALL NOT include a terminating null character to mark the end of a string.
+ dataTx.e = chip::Span<const char>(charSpanData, strlen(charSpanData));
+
+ if (encoding == EncodingMethod::Standard)
+ {
+ EXPECT_EQ(writeClient.EncodeAttribute(attributePathParams, dataTx), CHIP_NO_ERROR);
+ }
+ else if (encoding == EncodingMethod::PreencodedTLV)
+ {
+ // Encode AttributeData into TLV
+ uint8_t buffer[50];
+ TLV::TLVWriter writer;
+ writer.Init(buffer, sizeof(buffer));
+ TLV::TLVType outerContainer;
+
+ EXPECT_EQ(CHIP_NO_ERROR, writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerContainer));
+ EXPECT_EQ(CHIP_NO_ERROR, writer.Put(TLV::ContextTag(0), dataTx.a));
+ EXPECT_EQ(CHIP_NO_ERROR, writer.PutBoolean(TLV::ContextTag(1), dataTx.b));
+ EXPECT_EQ(CHIP_NO_ERROR, writer.Put(TLV::ContextTag(3), dataTx.d));
+ EXPECT_EQ(CHIP_NO_ERROR, writer.PutString(TLV::ContextTag(4), dataTx.e));
+ EXPECT_EQ(CHIP_NO_ERROR, writer.EndContainer(outerContainer));
+
+ // Put Preencoded Data into AttributeDataIB
+ TLV::TLVReader dataTxTLV;
+ dataTxTLV.Init(buffer, writer.GetLengthWritten());
+ dataTxTLV.Next();
+ ConcreteDataAttributePath path = ConcreteDataAttributePath(
+ attributePathParams.mEndpointId, attributePathParams.mClusterId, attributePathParams.mAttributeId);
+ EXPECT_EQ(writeClient.PutPreencodedAttribute(path, dataTxTLV), CHIP_NO_ERROR);
+ }
+
+ EXPECT_EQ(callback.mOnSuccessCalled, 0);
+
+ EXPECT_EQ(writeClient.SendWriteRequest(GetSessionBobToAlice()), CHIP_NO_ERROR);
+
+ DrainAndServiceIO();
+
+ EXPECT_EQ(callback.mOnSuccessCalled, 1);
+
+ {
+ app::Clusters::UnitTesting::Structs::SimpleStruct::Type dataRx;
+ TLV::TLVReader reader;
+ reader.Init(chip::Test::attributeDataTLV, chip::Test::attributeDataTLVLen);
+ reader.Next();
+ EXPECT_EQ(CHIP_NO_ERROR, DataModel::Decode(reader, dataRx));
+ EXPECT_EQ(dataRx.a, dataTx.a);
+ EXPECT_EQ(dataRx.b, dataTx.b);
+ EXPECT_TRUE(dataRx.d.data_equal(dataTx.d));
+ // Equals to dataRx.e.size() == dataTx.e.size() && memncmp(dataRx.e.data(), dataTx.e.data(), dataTx.e.size()) == 0
+ EXPECT_TRUE(dataRx.e.data_equal(dataTx.e));
+ }
+
+ EXPECT_EQ(callback.mOnSuccessCalled, 1);
+ EXPECT_EQ(callback.mOnErrorCalled, 0);
+ EXPECT_EQ(callback.mOnDoneCalled, 1);
+
+ // By now we should have closed all exchanges and sent all pending acks, so
+ // there should be no queued-up things in the retransmit table.
+ EXPECT_EQ(rm->TestGetCountRetransTable(), 0);
+
+ engine->Shutdown();
}
-
- EXPECT_EQ(callback.mOnSuccessCalled, 1);
- EXPECT_EQ(callback.mOnErrorCalled, 0);
- EXPECT_EQ(callback.mOnDoneCalled, 1);
-
- // By now we should have closed all exchanges and sent all pending acks, so
- // there should be no queued-up things in the retransmit table.
- EXPECT_EQ(rm->TestGetCountRetransTable(), 0);
-
- engine->Shutdown();
}
TEST_F(TestWriteInteraction, TestWriteRoundtripWithClusterObjectsVersionMatch)
@@ -578,36 +647,39 @@
TEST_F(TestWriteInteraction, TestWriteRoundtrip)
{
+ for (EncodingMethod encodingMethod : { EncodingMethod::Standard, EncodingMethod::PreencodedTLV })
+ {
+ Messaging::ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr();
+ // Shouldn't have anything in the retransmit table when starting the test.
+ EXPECT_EQ(rm->TestGetCountRetransTable(), 0);
- Messaging::ReliableMessageMgr * rm = GetExchangeManager().GetReliableMessageMgr();
- // Shouldn't have anything in the retransmit table when starting the test.
- EXPECT_EQ(rm->TestGetCountRetransTable(), 0);
+ TestWriteClientCallback callback;
+ auto * engine = chip::app::InteractionModelEngine::GetInstance();
+ EXPECT_EQ(engine->Init(&GetExchangeManager(), &GetFabricTable(), app::reporting::GetDefaultReportScheduler()),
+ CHIP_NO_ERROR);
- TestWriteClientCallback callback;
- auto * engine = chip::app::InteractionModelEngine::GetInstance();
- EXPECT_EQ(engine->Init(&GetExchangeManager(), &GetFabricTable(), app::reporting::GetDefaultReportScheduler()), CHIP_NO_ERROR);
+ app::WriteClient writeClient(engine->GetExchangeManager(), &callback, Optional<uint16_t>::Missing());
- app::WriteClient writeClient(engine->GetExchangeManager(), &callback, Optional<uint16_t>::Missing());
+ System::PacketBufferHandle buf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
+ AddAttributeDataIB(writeClient, encodingMethod);
- System::PacketBufferHandle buf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
- AddAttributeDataIB(writeClient);
+ EXPECT_EQ(callback.mOnSuccessCalled, 0);
+ EXPECT_EQ(callback.mOnErrorCalled, 0);
+ EXPECT_EQ(callback.mOnDoneCalled, 0);
- EXPECT_EQ(callback.mOnSuccessCalled, 0);
- EXPECT_EQ(callback.mOnErrorCalled, 0);
- EXPECT_EQ(callback.mOnDoneCalled, 0);
+ EXPECT_EQ(writeClient.SendWriteRequest(GetSessionBobToAlice()), CHIP_NO_ERROR);
- EXPECT_EQ(writeClient.SendWriteRequest(GetSessionBobToAlice()), CHIP_NO_ERROR);
+ DrainAndServiceIO();
- DrainAndServiceIO();
+ EXPECT_EQ(callback.mOnSuccessCalled, 1);
+ EXPECT_EQ(callback.mOnErrorCalled, 0);
+ EXPECT_EQ(callback.mOnDoneCalled, 1);
+ // By now we should have closed all exchanges and sent all pending acks, so
+ // there should be no queued-up things in the retransmit table.
+ EXPECT_EQ(rm->TestGetCountRetransTable(), 0);
- EXPECT_EQ(callback.mOnSuccessCalled, 1);
- EXPECT_EQ(callback.mOnErrorCalled, 0);
- EXPECT_EQ(callback.mOnDoneCalled, 1);
- // By now we should have closed all exchanges and sent all pending acks, so
- // there should be no queued-up things in the retransmit table.
- EXPECT_EQ(rm->TestGetCountRetransTable(), 0);
-
- engine->Shutdown();
+ engine->Shutdown();
+ }
}
// This test creates a chunked write request, we drop the second write chunk message, then write handler receives unknown
diff --git a/src/controller/tests/BUILD.gn b/src/controller/tests/BUILD.gn
index 69c9e24..2ccd960 100644
--- a/src/controller/tests/BUILD.gn
+++ b/src/controller/tests/BUILD.gn
@@ -35,9 +35,16 @@
"TestEventNumberCaching.cpp",
"TestReadChunking.cpp",
"TestServerCommandDispatch.cpp",
- "TestWriteChunking.cpp",
]
+ # At some instances, this Test end ups allocating more than 32 Timers, and since the fake platform doesn`t use the heap, the maximum number of allowed timers is 32.
+ # So we get failures because we can't allocate any more timers, in the log it looks like:
+ # SendMessage() to UDP:[::1]:5541 failed: b
+ # deactivating since the Fake platform is mostly done to test Platform APIs
+ if (chip_device_platform != "fake") {
+ test_sources += [ "TestWriteChunking.cpp" ]
+ }
+
# Not supported on efr32.
if (chip_device_platform != "efr32") {
test_sources += [
diff --git a/src/controller/tests/TestWriteChunking.cpp b/src/controller/tests/TestWriteChunking.cpp
index 105c9e8..5bb038c 100644
--- a/src/controller/tests/TestWriteChunking.cpp
+++ b/src/controller/tests/TestWriteChunking.cpp
@@ -81,6 +81,12 @@
kBadValue,
};
+ enum class EncodingMethod
+ {
+ Standard, // Encoding using WriteClient::EncodeAttribute()
+ PreencodedTLV, // Encoding using WriteClient::PutPreencodedAttribute()
+ };
+
struct Instructions
{
// The paths used in write request
@@ -93,10 +99,59 @@
std::vector<bool> expectedStatus;
};
- void RunTest(Instructions instructions);
- void RunTest_NonEmptyReplaceAll(Instructions instructions);
+ void RunTest(Instructions instructions, EncodingMethod encodingMethod);
+ void RunTest_NonEmptyReplaceAll(Instructions instructions, EncodingMethod encodingMethod);
+
+ template <class T>
+ void EncodeAttributeListIntoTLV(const DataModel::List<T> & aListAttribute, TLV::ScopedBufferTLVReader & outTlvReader);
};
+// Encodes an attribute of List Data Type into a TLV Reader object for testing WriteClient::PutPreencodedAttribute
+// Warning: This method only encodes uint8_t or ByteSpans whose length fits in one octet
+template <class T>
+void TestWriteChunking::EncodeAttributeListIntoTLV(const DataModel::List<T> & aListAttribute,
+ TLV::ScopedBufferTLVReader & outEncodedListTlvReader)
+{
+ static_assert(std::is_same<T, chip::ByteSpan>::value || std::is_same<T, uint8_t>::value,
+ "This method only encodes uint8_t or ByteSpans whose length fits in one octet");
+
+ size_t estimatedSize = 0;
+ for (size_t i = 0; i < aListAttribute.size(); i++)
+ {
+ if constexpr (std::is_same<T, uint8_t>::value)
+ {
+ // Control Octet (1) + size of uint8_t
+ estimatedSize += 1 + sizeof(uint8_t);
+ }
+ else if constexpr (std::is_same<T, chip::ByteSpan>::value)
+ {
+ ASSERT_LE(aListAttribute[i].size(), static_cast<size_t>(UINT8_MAX));
+
+ // Control Octet (1) + Length Octet (1) + size of a single ByteSpan
+ estimatedSize += 2 + aListAttribute[i].size();
+ }
+ }
+
+ // Encode AttributeData into a TLV Array
+ chip::Platform::ScopedMemoryBufferWithSize<uint8_t> buffer;
+ buffer.Alloc(TLV::EstimateStructOverhead(estimatedSize));
+
+ TLV::TLVWriter writer;
+ writer.Init(buffer.Get(), buffer.AllocatedSize());
+ TLV::TLVType outerContainer;
+
+ EXPECT_EQ(CHIP_NO_ERROR, writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Array, outerContainer));
+ for (auto & item : aListAttribute)
+ {
+ EXPECT_EQ(CHIP_NO_ERROR, writer.Put(TLV::AnonymousTag(), item));
+ }
+ EXPECT_EQ(CHIP_NO_ERROR, writer.EndContainer(outerContainer));
+
+ // Move Encoded TLV Array into TLVReader Object
+ outEncodedListTlvReader.Init(std::move(buffer), writer.GetLengthWritten());
+ outEncodedListTlvReader.Next();
+}
+
//clang-format off
DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(testClusterAttrsOnEndpoint)
@@ -300,48 +355,67 @@
// to ensure we'll sweep from fitting 2 chunks to 3-4 chunks.
//
constexpr size_t minReservationSize = kMaxSecureSduLengthBytes - 75 - 100;
- for (uint32_t i = 100; i > 0; i--)
+
+ for (EncodingMethod encodingMethod : { EncodingMethod::Standard, EncodingMethod::PreencodedTLV })
{
- CHIP_ERROR err = CHIP_NO_ERROR;
- TestWriteCallback writeCallback;
- ChipLogDetail(DataManagement, "Running iteration %d\n", static_cast<int>(i));
-
- gIterationCount = i;
-
- app::WriteClient writeClient(&GetExchangeManager(), &writeCallback, Optional<uint16_t>::Missing(),
- static_cast<uint16_t>(minReservationSize + i) /* reserved buffer size */);
-
- ByteSpan list[kTestListLength];
-
- err = writeClient.EncodeAttribute(attributePath, app::DataModel::List<ByteSpan>(list, kTestListLength));
- EXPECT_EQ(err, CHIP_NO_ERROR);
-
- err = writeClient.SendWriteRequest(sessionHandle);
- EXPECT_EQ(err, CHIP_NO_ERROR);
-
- //
- // Service the IO + Engine till we get a ReportEnd callback on the client.
- // Since bugs can happen, we don't want this test to never stop, so create a ceiling for how many
- // times this can run without seeing expected results.
- //
- for (int j = 0; j < 10 && writeCallback.mOnDoneCount == 0; j++)
+ for (uint32_t i = 100; i > 0; i--)
{
- DrainAndServiceIO();
- }
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ TestWriteCallback writeCallback;
- EXPECT_EQ(writeCallback.mSuccessCount, kTestListLength + 1 /* an extra item for the empty list at the beginning */);
- EXPECT_EQ(writeCallback.mErrorCount, 0u);
- EXPECT_EQ(writeCallback.mOnDoneCount, 1u);
+ ChipLogDetail(DataManagement, "Running iteration %d\n", static_cast<int>(i));
- EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u);
+ gIterationCount = i;
- //
- // Stop the test if we detected an error. Otherwise, it'll be difficult to read the logs.
- //
- if (HasFailure())
- {
- break;
+ app::WriteClient writeClient(&GetExchangeManager(), &writeCallback, Optional<uint16_t>::Missing(),
+ static_cast<uint16_t>(minReservationSize + i) /* reserved buffer size */);
+
+ ByteSpan list[kTestListLength];
+
+ if (encodingMethod == EncodingMethod::Standard)
+ {
+ err = writeClient.EncodeAttribute(attributePath, app::DataModel::List<ByteSpan>(list, kTestListLength));
+ EXPECT_EQ(err, CHIP_NO_ERROR);
+ }
+ else if (encodingMethod == EncodingMethod::PreencodedTLV)
+ {
+ TLV::ScopedBufferTLVReader encodedListTLV;
+ EncodeAttributeListIntoTLV(DataModel::List<ByteSpan>(list, kTestListLength), encodedListTLV);
+
+ ConcreteDataAttributePath path =
+ ConcreteDataAttributePath(attributePath.mEndpointId, attributePath.mClusterId, attributePath.mAttributeId);
+ EXPECT_EQ(writeClient.PutPreencodedAttribute(path, encodedListTLV), CHIP_NO_ERROR);
+ }
+
+ //
+
+ err = writeClient.SendWriteRequest(sessionHandle);
+ EXPECT_EQ(err, CHIP_NO_ERROR);
+
+ //
+ // Service the IO + Engine till we get a ReportEnd callback on the client.
+ // Since bugs can happen, we don't want this test to never stop, so create a ceiling for how many
+ // times this can run without seeing expected results.
+ //
+ for (int j = 0; j < 10 && writeCallback.mOnDoneCount == 0; j++)
+ {
+ DrainAndServiceIO();
+ }
+
+ EXPECT_EQ(writeCallback.mSuccessCount, kTestListLength + 1 /* an extra item for the empty list at the beginning */);
+ EXPECT_EQ(writeCallback.mErrorCount, 0u);
+ EXPECT_EQ(writeCallback.mOnDoneCount, 1u);
+
+ EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u);
+
+ //
+ // Stop the test if we detected an error. Otherwise, it'll be difficult to read the logs.
+ //
+ if (HasFailure())
+ {
+ break;
+ }
}
}
emberAfClearDynamicEndpoint(0);
@@ -377,59 +451,75 @@
// Start with a high reservation (maxReservationSize) to force chunking, then decrease the reservation in 1-byte steps.
// This increases the buffer space available for encoding, gradually reducing the need for chunking, until chunking would not
// occur anymore. This helps validate various edge cases.
- for (uint32_t reservationReduction = 0; reservationReduction < 40; reservationReduction++)
+
+ for (EncodingMethod encodingMethod : { EncodingMethod::Standard, EncodingMethod::PreencodedTLV })
{
- CHIP_ERROR err = CHIP_NO_ERROR;
- TestWriteCallback writeCallback;
-
- ChipLogDetail(DataManagement, "Running iteration %d\n", static_cast<int>(reservationReduction));
-
- app::WriteClient writeClient(&GetExchangeManager(), &writeCallback, NullOptional,
- static_cast<uint16_t>(maxReservationSize - reservationReduction) /* reserved buffer size */);
-
- ByteSpan list[kTestListLength2];
-
- err = writeClient.EncodeAttribute(attributePath, app::DataModel::List<ByteSpan>(list, kTestListLength2));
-
- EXPECT_EQ(err, CHIP_NO_ERROR);
-
- // Ensure that chunking actually occurred in the first iteration. We will iteratively chunk less and less, until chunking
- // would not occur anymore. Thus, this check is only needed at start.
- if (reservationReduction == 0)
+ for (uint32_t reservationReduction = 0; reservationReduction < 40; reservationReduction++)
{
- ASSERT_TRUE(writeClient.IsWriteRequestChunked());
- }
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ TestWriteCallback writeCallback;
- err = writeClient.SendWriteRequest(sessionHandle);
- EXPECT_EQ(err, CHIP_NO_ERROR);
+ ChipLogDetail(DataManagement, "Running iteration %d\n", static_cast<int>(reservationReduction));
- //
- // Service the IO + Engine till we get a ReportEnd callback on the client.
- // Since bugs can happen, we don't want this test to never stop, so create a ceiling for how many
- // times this can run without seeing expected results.
- //
- for (int j = 0; j < 10 && writeCallback.mOnDoneCount == 0; j++)
- {
- DrainAndServiceIO();
- }
+ app::WriteClient writeClient(
+ &GetExchangeManager(), &writeCallback, NullOptional,
+ static_cast<uint16_t>(maxReservationSize - reservationReduction) /* reserved buffer size */);
- // Due to Write Chunking being done dynamically (fitting as many items as possible into an initial ReplaceAll List, before
- // starting to chunk), it is fragile to try to predict mSuccessCount. It all depends on how much was packed into the initial
- // ReplaceAll List.
- // However, we know for sure that writeCallback should NEVER fail.
- EXPECT_EQ(writeCallback.mErrorCount, 0u);
- EXPECT_EQ(writeCallback.mOnDoneCount, 1u);
+ ByteSpan list[kTestListLength2];
- EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u);
+ if (encodingMethod == EncodingMethod::Standard)
+ {
+ err = writeClient.EncodeAttribute(attributePath, app::DataModel::List<ByteSpan>(list, kTestListLength2));
+ EXPECT_EQ(err, CHIP_NO_ERROR);
+ }
+ else if (encodingMethod == EncodingMethod::PreencodedTLV)
+ {
+ TLV::ScopedBufferTLVReader encodedListTLV;
+ EncodeAttributeListIntoTLV(DataModel::List<ByteSpan>(list, kTestListLength2), encodedListTLV);
- //
- // Stop the test if we detected an error. Otherwise, it'll be difficult to read the logs.
- //
- if (HasFailure())
- {
- break;
+ ConcreteDataAttributePath path =
+ ConcreteDataAttributePath(attributePath.mEndpointId, attributePath.mClusterId, attributePath.mAttributeId);
+ EXPECT_EQ(writeClient.PutPreencodedAttribute(path, encodedListTLV), CHIP_NO_ERROR);
+ }
+
+ // Ensure that chunking actually occurred in the first iteration. We will iteratively chunk less and less, until
+ // chunking would not occur anymore. Thus, this check is only needed at start.
+ if (reservationReduction == 0)
+ {
+ ASSERT_TRUE(writeClient.IsWriteRequestChunked());
+ }
+
+ err = writeClient.SendWriteRequest(sessionHandle);
+ EXPECT_EQ(err, CHIP_NO_ERROR);
+
+ //
+ // Service the IO + Engine till we get a ReportEnd callback on the client.
+ // Since bugs can happen, we don't want this test to never stop, so create a ceiling for how many
+ // times this can run without seeing expected results.
+ //
+ for (int j = 0; j < 10 && writeCallback.mOnDoneCount == 0; j++)
+ {
+ DrainAndServiceIO();
+ }
+
+ // Due to Write Chunking being done dynamically (fitting as many items as possible into an initial ReplaceAll List,
+ // before starting to chunk), it is fragile to try to predict mSuccessCount. It all depends on how much was packed into
+ // the initial ReplaceAll List. However, we know for sure that writeCallback should NEVER fail.
+ EXPECT_EQ(writeCallback.mErrorCount, 0u);
+ EXPECT_EQ(writeCallback.mOnDoneCount, 1u);
+
+ EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u);
+
+ //
+ // Stop the test if we detected an error. Otherwise, it'll be difficult to read the logs.
+ //
+ if (HasFailure())
+ {
+ break;
+ }
}
}
+
emberAfClearDynamicEndpoint(0);
}
@@ -898,8 +988,9 @@
emberAfClearDynamicEndpoint(0);
}
+// for (EncodingMethod encodingMethod : { EncodingMethod::Standard, EncodingMethod::PreencodedTLV })
-void TestWriteChunking::RunTest(Instructions instructions)
+void TestWriteChunking::RunTest(Instructions instructions, EncodingMethod encodingMethod = EncodingMethod::Standard)
{
CHIP_ERROR err = CHIP_NO_ERROR;
auto sessionHandle = GetSessionBobToAlice();
@@ -958,13 +1049,38 @@
break;
}
case ListData::kList: {
- err = writeClient->EncodeAttribute(AttributePathParams(p.mEndpointId, p.mClusterId, p.mAttributeId),
- DataModel::List<ByteSpan>(list, kTestListLength));
+
+ if (encodingMethod == EncodingMethod::Standard)
+ {
+ err = writeClient->EncodeAttribute(AttributePathParams(p.mEndpointId, p.mClusterId, p.mAttributeId),
+ DataModel::List<ByteSpan>(list, kTestListLength));
+ }
+ else if (encodingMethod == EncodingMethod::PreencodedTLV)
+ {
+ TLV::ScopedBufferTLVReader encodedListTLV;
+ EncodeAttributeListIntoTLV(DataModel::List<ByteSpan>(list, kTestListLength), encodedListTLV);
+
+ ConcreteDataAttributePath path = ConcreteDataAttributePath(p.mEndpointId, p.mClusterId, p.mAttributeId);
+ err = writeClient->PutPreencodedAttribute(path, encodedListTLV);
+ }
break;
}
case ListData::kBadValue: {
- err = writeClient->EncodeAttribute(AttributePathParams(p.mEndpointId, p.mClusterId, p.mAttributeId),
- DataModel::List<uint8_t>(badList, kTestListLength));
+
+ if (encodingMethod == EncodingMethod::Standard)
+ {
+ err = writeClient->EncodeAttribute(AttributePathParams(p.mEndpointId, p.mClusterId, p.mAttributeId),
+ DataModel::List<uint8_t>(badList, kTestListLength));
+ }
+ else if (encodingMethod == EncodingMethod::PreencodedTLV)
+ {
+ TLV::ScopedBufferTLVReader encodedListTLV;
+ EncodeAttributeListIntoTLV(DataModel::List<uint8_t>(badList, kTestListLength), encodedListTLV);
+
+ ConcreteDataAttributePath path = ConcreteDataAttributePath(p.mEndpointId, p.mClusterId, p.mAttributeId);
+ err = writeClient->PutPreencodedAttribute(path, encodedListTLV);
+ }
+
break;
}
}
@@ -1003,89 +1119,121 @@
// Register our fake attribute access interface.
AttributeAccessInterfaceRegistry::Instance().Register(&testServer);
- // Test 1: we should receive transaction notifications
- ChipLogProgress(Zcl, "Test 1: we should receive transaction notifications");
- RunTest(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute) },
- .expectedStatus = { true },
- });
+ for (EncodingMethod encodingMethod : { EncodingMethod::Standard, EncodingMethod::PreencodedTLV })
+ {
+ // For builds without ChipLogProgress, encodingMethodName will be ununsed and trigger build failures
+ [[maybe_unused]] const char * encodingMethodName =
+ (encodingMethod == EncodingMethod::Standard ? "StandardEncoding" : "PreencodedTLV");
- ChipLogProgress(Zcl, "Test 2: we should receive transaction notifications for incomplete list operations");
- RunTest(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute) },
- .onListWriteBeginActions = [&](const app::ConcreteAttributePath & aPath) { return Operations::kShutdownWriteClient; },
- .expectedStatus = { false },
- });
-
- ChipLogProgress(Zcl, "Test 3: we should receive transaction notifications for every list in the transaction");
- RunTest(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute),
- ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute2) },
- .expectedStatus = { true, true },
- });
-
- ChipLogProgress(Zcl, "Test 4: we should receive transaction notifications with the status of each list");
- RunTest(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute),
- ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute2) },
- .onListWriteBeginActions =
- [&](const app::ConcreteAttributePath & aPath) {
- if (aPath.mAttributeId == kTestListAttribute2)
- {
- return Operations::kShutdownWriteClient;
- }
- return Operations::kNoop;
+ // Test 1: we should receive transaction notifications
+ ChipLogProgress(Zcl, "Test 1 [%s]: we should receive transaction notifications", encodingMethodName);
+ RunTest(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute) },
+ .expectedStatus = { true },
},
- .expectedStatus = { true, false },
- });
+ encodingMethod);
- ChipLogProgress(Zcl,
- "Test 5: transactional list callbacks will be called for nullable lists, test if it is handled correctly for "
- "null value before non null values");
- RunTest(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute),
- ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute) },
- .data = { ListData::kNull, ListData::kList },
- .expectedStatus = { true },
- });
+ ChipLogProgress(Zcl, "Test 2a [%s]: we should receive transaction notifications for incomplete list operations",
+ encodingMethodName);
+ RunTest(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute) },
+ .onListWriteBeginActions =
+ [&](const app::ConcreteAttributePath & aPath) { return Operations::kShutdownWriteClient; },
+ .expectedStatus = { false },
+ },
+ encodingMethod);
- ChipLogProgress(Zcl,
- "Test 6: transactional list callbacks will be called for nullable lists, test if it is handled correctly for "
- "null value after non null values");
- RunTest(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute),
- ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute) },
- .data = { ListData::kList, ListData::kNull },
- .expectedStatus = { true },
- });
+ ChipLogProgress(Zcl, "Test 3 [%s]: we should receive transaction notifications for every list in the transaction",
+ encodingMethodName);
+ RunTest(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute),
+ ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute2) },
+ .expectedStatus = { true, true },
+ },
+ encodingMethod);
- ChipLogProgress(Zcl,
- "Test 7: transactional list callbacks will be called for nullable lists, test if it is handled correctly for "
- "null value between non null values");
- RunTest(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute),
- ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute),
- ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute) },
- .data = { ListData::kList, ListData::kNull, ListData::kList },
- .expectedStatus = { true },
- });
+ ChipLogProgress(Zcl, "Test 4 [%s]: we should receive transaction notifications with the status of each list",
+ encodingMethodName);
+ RunTest(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute),
+ ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute2) },
+ .onListWriteBeginActions =
+ [&](const app::ConcreteAttributePath & aPath) {
+ if (aPath.mAttributeId == kTestListAttribute2)
+ {
+ return Operations::kShutdownWriteClient;
+ }
+ return Operations::kNoop;
+ },
+ .expectedStatus = { true, false },
+ },
+ encodingMethod);
- ChipLogProgress(Zcl, "Test 8: transactional list callbacks will be called for nullable lists");
- RunTest(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute) },
- .data = { ListData::kNull },
- .expectedStatus = { true },
- });
+ ChipLogProgress(Zcl,
+ "Test 5 [%s]: transactional list callbacks will be called for nullable lists, test if it is handled "
+ "correctly for null value before non null values",
+ encodingMethodName);
+ RunTest(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute),
+ ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute) },
+ .data = { ListData::kNull, ListData::kList },
+ .expectedStatus = { true },
+ },
+ encodingMethod);
- ChipLogProgress(Zcl,
- "Test 9: for nullable lists, we should receive notifications for unsuccessful writes when non-fatal occurred "
- "during processing the requests");
- RunTest(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute) },
- .data = { ListData::kBadValue },
- .expectedStatus = { false },
- });
+ ChipLogProgress(Zcl,
+ "Test 6 [%s]: transactional list callbacks will be called for nullable lists, test if it is handled "
+ "correctly for null value after non null values",
+ encodingMethodName);
+ RunTest(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute),
+ ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute) },
+ .data = { ListData::kList, ListData::kNull },
+ .expectedStatus = { true },
+ },
+ encodingMethod);
+ ChipLogProgress(Zcl,
+ "Test 7 [%s]: transactional list callbacks will be called for nullable lists, test if it is handled "
+ "correctly for null value between non null values",
+ encodingMethodName);
+ RunTest(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute),
+ ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute),
+ ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute) },
+ .data = { ListData::kList, ListData::kNull, ListData::kList },
+ .expectedStatus = { true },
+ },
+ encodingMethod);
+
+ ChipLogProgress(Zcl, "Test 8 [%s]: transactional list callbacks will be called for nullable lists", encodingMethodName);
+ RunTest(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute) },
+ .data = { ListData::kNull },
+ .expectedStatus = { true },
+ },
+ encodingMethod);
+
+ ChipLogProgress(Zcl,
+ "Test 9 [%s]: for nullable lists, we should receive notifications for unsuccessful writes when non-fatal "
+ "occurred during processing the requests",
+ encodingMethodName);
+ RunTest(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, Clusters::UnitTesting::Id, kTestListAttribute) },
+ .data = { ListData::kBadValue },
+ .expectedStatus = { false },
+ },
+ encodingMethod);
+ }
EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u);
emberAfClearDynamicEndpoint(0);
@@ -1095,7 +1243,8 @@
* A Variant of RunTest above, that tests the Code Path where we encode a Non-Replace All List in WriteRequests, this
* happens with the ACL Cluster.
*/
-void TestWriteChunking::RunTest_NonEmptyReplaceAll(Instructions instructions)
+void TestWriteChunking::RunTest_NonEmptyReplaceAll(Instructions instructions,
+ EncodingMethod encodingMethod = EncodingMethod::Standard)
{
CHIP_ERROR err = CHIP_NO_ERROR;
auto sessionHandle = GetSessionBobToAlice();
@@ -1156,13 +1305,38 @@
break;
}
case ListData::kList: {
- err = writeClient->EncodeAttribute(AttributePathParams(p.mEndpointId, p.mClusterId, p.mAttributeId),
- DataModel::List<ByteSpan>(list, kTestListLength2));
+
+ if (encodingMethod == EncodingMethod::Standard)
+ {
+ err = writeClient->EncodeAttribute(AttributePathParams(p.mEndpointId, p.mClusterId, p.mAttributeId),
+ DataModel::List<ByteSpan>(list, kTestListLength2));
+ }
+ else if (encodingMethod == EncodingMethod::PreencodedTLV)
+ {
+ TLV::ScopedBufferTLVReader encodedListTLV;
+ EncodeAttributeListIntoTLV(DataModel::List<ByteSpan>(list, kTestListLength2), encodedListTLV);
+
+ ConcreteDataAttributePath path = ConcreteDataAttributePath(p.mEndpointId, p.mClusterId, p.mAttributeId);
+ err = writeClient->PutPreencodedAttribute(path, encodedListTLV);
+ }
break;
}
case ListData::kBadValue: {
- err = writeClient->EncodeAttribute(AttributePathParams(p.mEndpointId, p.mClusterId, p.mAttributeId),
- DataModel::List<uint8_t>(badList, kTestListLength2));
+
+ if (encodingMethod == EncodingMethod::Standard)
+ {
+ err = writeClient->EncodeAttribute(AttributePathParams(p.mEndpointId, p.mClusterId, p.mAttributeId),
+ DataModel::List<uint8_t>(badList, kTestListLength2));
+ }
+ else if (encodingMethod == EncodingMethod::PreencodedTLV)
+ {
+ TLV::ScopedBufferTLVReader encodedListTLV;
+ EncodeAttributeListIntoTLV(DataModel::List<uint8_t>(badList, kTestListLength2), encodedListTLV);
+
+ ConcreteDataAttributePath path = ConcreteDataAttributePath(p.mEndpointId, p.mClusterId, p.mAttributeId);
+ err = writeClient->PutPreencodedAttribute(path, encodedListTLV);
+ }
+
break;
}
}
@@ -1201,113 +1375,146 @@
// Register our fake attribute access interface.
AttributeAccessInterfaceRegistry::Instance().Register(&testServerAcl);
- // Test 1: we should receive transaction notifications
- ChipLogProgress(Zcl, "Test 1: we should receive transaction notifications");
- RunTest_NonEmptyReplaceAll(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id) },
- .expectedStatus = { true },
- });
-
- ChipLogProgress(Zcl, "Test 2: we should receive transaction notifications for incomplete list operations");
- RunTest_NonEmptyReplaceAll(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id) },
- .onListWriteBeginActions = [&](const app::ConcreteAttributePath & aPath) { return Operations::kShutdownWriteClient; },
- .expectedStatus = { false },
- });
-
- ChipLogProgress(Zcl, "Test 3: we should receive transaction notifications for every list in the transaction");
- RunTest_NonEmptyReplaceAll(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id),
- ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Extension::Id) },
- .expectedStatus = { true, true },
- });
-
- ChipLogProgress(Zcl, "Test 4: we should receive transaction notifications with the status of each list");
- RunTest_NonEmptyReplaceAll(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id),
- ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Extension::Id) },
- .onListWriteBeginActions =
- [&](const app::ConcreteAttributePath & aPath) {
- if (aPath.mAttributeId == AccessControl::Attributes::Extension::Id)
- {
- return Operations::kShutdownWriteClient;
- }
- return Operations::kNoop;
- },
- .expectedStatus = { true, false },
- });
-
- ChipLogProgress(Zcl,
- "Test 5: transactional list callbacks will be called for nullable lists, test if it is handled correctly for "
- "null value before non null values");
- RunTest_NonEmptyReplaceAll(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id),
- ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id) },
- .data = { ListData::kNull, ListData::kList },
- .expectedStatus = { true },
- });
-
- ChipLogProgress(Zcl,
- "Test 6: transactional list callbacks will be called for nullable lists, test if it is handled correctly for "
- "null value after non null values");
- RunTest_NonEmptyReplaceAll(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id),
- ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id) },
- .data = { ListData::kList, ListData::kNull },
- .expectedStatus = { true },
- });
-
- ChipLogProgress(Zcl,
- "Test 7: transactional list callbacks will be called for nullable lists, test if it is handled correctly for "
- "null value between non null values");
- RunTest_NonEmptyReplaceAll(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id),
- ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id),
- ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id) },
- .data = { ListData::kList, ListData::kNull, ListData::kList },
- .expectedStatus = { true },
- });
-
- ChipLogProgress(Zcl, "Test 8: transactional list callbacks will be called for nullable lists");
- RunTest_NonEmptyReplaceAll(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id) },
- .data = { ListData::kNull },
- .expectedStatus = { true },
- });
-
- ChipLogProgress(Zcl,
- "Test 9: for nullable lists, we should receive notifications for unsuccessful writes when non-fatal occurred "
- "during processing the requests");
- RunTest_NonEmptyReplaceAll(Instructions{
- .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id) },
- .data = { ListData::kBadValue },
- .expectedStatus = { false },
- });
-
- // This TestCase tests corner cases when we Encode many attributes into the same WriteRequest, up to 10 Attributes will be
- // Encoded.
- for (int nullableListCount = 1; nullableListCount <= 10; nullableListCount++)
+ for (EncodingMethod encodingMethod : { EncodingMethod::Standard, EncodingMethod::PreencodedTLV })
{
- ChipLogProgress(Zcl, "Test 10.%d: Encoding %d nullable lists following a single non-nullable list", nullableListCount,
- nullableListCount);
+ // For builds without ChipLogProgress, encodingMethodName will be ununsed and trigger build failures
+ [[maybe_unused]] const char * encodingMethodName =
+ (encodingMethod == EncodingMethod::Standard ? "StandardEncoding" : "PreencodedTLV");
- Instructions test;
+ // Test 1: we should receive transaction notifications
+ ChipLogProgress(Zcl, "Test 1 [%s]: we should receive transaction notifications", encodingMethodName);
+ RunTest_NonEmptyReplaceAll(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id) },
+ .expectedStatus = { true },
+ },
+ encodingMethod);
- // Add the single non-nullable list
- test.paths.push_back(ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id));
- test.data.push_back(ListData::kList);
+ ChipLogProgress(Zcl, "Test 2 [%s]: we should receive transaction notifications for incomplete list operations",
+ encodingMethodName);
+ RunTest_NonEmptyReplaceAll(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id) },
+ .onListWriteBeginActions =
+ [&](const app::ConcreteAttributePath & aPath) { return Operations::kShutdownWriteClient; },
+ .expectedStatus = { false },
+ },
+ encodingMethod);
- // Add the nullable lists
- for (int i = 0; i < nullableListCount; i++)
+ ChipLogProgress(Zcl, "Test 3 [%s]: we should receive transaction notifications for every list in the transaction",
+ encodingMethodName);
+ RunTest_NonEmptyReplaceAll(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id),
+ ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Extension::Id) },
+ .expectedStatus = { true, true },
+ },
+ encodingMethod);
+
+ ChipLogProgress(Zcl, "Test 4 [%s]: we should receive transaction notifications with the status of each list",
+ encodingMethodName);
+ RunTest_NonEmptyReplaceAll(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id),
+ ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Extension::Id) },
+ .onListWriteBeginActions =
+ [&](const app::ConcreteAttributePath & aPath) {
+ if (aPath.mAttributeId == AccessControl::Attributes::Extension::Id)
+ {
+ return Operations::kShutdownWriteClient;
+ }
+ return Operations::kNoop;
+ },
+ .expectedStatus = { true, false },
+ },
+ encodingMethod);
+
+ ChipLogProgress(Zcl,
+ "Test 5 [%s]: transactional list callbacks will be called for nullable lists, test if it is handled "
+ "correctly for null value before non null values",
+ encodingMethodName);
+ RunTest_NonEmptyReplaceAll(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id),
+ ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id) },
+ .data = { ListData::kNull, ListData::kList },
+ .expectedStatus = { true },
+ },
+ encodingMethod);
+
+ ChipLogProgress(Zcl,
+ "Test 6 [%s]: transactional list callbacks will be called for nullable lists, test if it is handled "
+ "correctly for null value after non null values",
+ encodingMethodName);
+ RunTest_NonEmptyReplaceAll(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id),
+ ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id) },
+ .data = { ListData::kList, ListData::kNull },
+ .expectedStatus = { true },
+ },
+ encodingMethod);
+
+ ChipLogProgress(Zcl,
+ "Test 7 [%s]: transactional list callbacks will be called for nullable lists, test if it is handled "
+ "correctly for null value between non null values",
+ encodingMethodName);
+ RunTest_NonEmptyReplaceAll(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id),
+ ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id),
+ ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id) },
+ .data = { ListData::kList, ListData::kNull, ListData::kList },
+ .expectedStatus = { true },
+ },
+ encodingMethod);
+
+ ChipLogProgress(Zcl, "Test 8 [%s]: transactional list callbacks will be called for nullable lists", encodingMethodName);
+ RunTest_NonEmptyReplaceAll(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id) },
+ .data = { ListData::kNull },
+ .expectedStatus = { true },
+ },
+ encodingMethod);
+
+ ChipLogProgress(Zcl,
+ "Test 9 [%s]: for nullable lists, we should receive notifications for unsuccessful writes when non-fatal "
+ "occurred during processing the requests",
+ encodingMethodName);
+ RunTest_NonEmptyReplaceAll(
+ Instructions{
+ .paths = { ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id) },
+ .data = { ListData::kBadValue },
+ .expectedStatus = { false },
+ },
+ encodingMethod);
+
+ // This TestCase tests corner cases when we Encode many attributes into the same WriteRequest, up to 10 Attributes will be
+ // Encoded.
+
+ for (int nullableListCount = 1; nullableListCount <= 10; nullableListCount++)
{
+ ChipLogProgress(Zcl, "Test 10.%d [%s]: Encoding %d nullable list(s) following a single non-nullable list",
+ nullableListCount, encodingMethodName, nullableListCount);
+
+ Instructions test;
+
+ // Add the single non-nullable list
test.paths.push_back(ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id));
- test.data.push_back(ListData::kNull);
+ test.data.push_back(ListData::kList);
+
+ // Add the nullable lists
+ for (int i = 0; i < nullableListCount; i++)
+ {
+ test.paths.push_back(ConcreteAttributePath(kTestEndpointId, AccessControl::Id, AccessControl::Attributes::Acl::Id));
+ test.data.push_back(ListData::kNull);
+ }
+
+ test.expectedStatus = { true };
+ RunTest_NonEmptyReplaceAll(test, encodingMethod);
}
-
- test.expectedStatus = { true };
- RunTest_NonEmptyReplaceAll(test);
}
-
EXPECT_EQ(GetExchangeManager().GetNumActiveExchanges(), 0u);
emberAfClearDynamicEndpoint(0);