/* * * Copyright (c) 2020-2022 Project CHIP Authors * Copyright (c) 2016-2017 Nest Labs, Inc. * * 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. */ /** * @file * This file implements a unit test suite for * chip::System::PacketBuffer, a class that provides * structure for network packet buffer management. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if CHIP_SYSTEM_CONFIG_USE_LWIP #include #include #endif // CHIP_SYSTEM_CONFIG_USE_LWIP #if CHIP_SYSTEM_CONFIG_USE_LWIP #if (LWIP_VERSION_MAJOR == 2) && (LWIP_VERSION_MINOR == 0) #define PBUF_TYPE(pbuf) (pbuf)->type #else // (LWIP_VERSION_MAJOR == 2) && (LWIP_VERSION_MINOR == 0) #define PBUF_TYPE(pbuf) (pbuf)->type_internal #endif // (LWIP_VERSION_MAJOR == 2) && (LWIP_VERSION_MINOR == 0) #endif // CHIP_SYSTEM_CONFIG_USE_LWIP using ::chip::Encoding::PacketBufferWriter; using ::chip::System::PacketBuffer; using ::chip::System::PacketBufferHandle; #if !CHIP_SYSTEM_CONFIG_USE_LWIP using ::chip::System::pbuf; #endif // Utility functions. #define TO_LWIP_PBUF(x) (reinterpret_cast(reinterpret_cast(x))) #define OF_LWIP_PBUF(x) (reinterpret_cast(reinterpret_cast(x))) namespace { void ScrambleData(uint8_t * start, size_t length) { for (size_t i = 0; i < length; ++i) ++start[i]; } } // namespace namespace chip { namespace System { /* * This class is a friend class of `PacketBuffer` and `PacketBufferHandle` because some tests * use or check private methods or properties. */ class TestSystemPacketBuffer : public ::testing::Test { public: static constexpr auto kBlockSize = PacketBuffer::kBlockSize; static constexpr auto kStructureSize = PacketBuffer::kStructureSize; static constexpr uint16_t kReservedSizes[] = { 0, 10, 128, 1536, PacketBuffer::kMaxSizeWithoutReserve, kBlockSize }; static constexpr uint16_t kLengths[] = { 0, 1, 10, 128, kBlockSize, UINT16_MAX }; static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); ASSERT_EQ(chip::DeviceLayer::PlatformMgr().InitChipStack(), CHIP_NO_ERROR); } static void TearDownTestSuite() { chip::DeviceLayer::PlatformMgr().Shutdown(); chip::Platform::MemoryShutdown(); } void SetUp() { configurations.resize(0); // Set up the buffer configuration vector for this suite. for (auto size : kReservedSizes) { configurations.emplace_back(size); } } void TearDown() { ASSERT_TRUE(ResetConfigurations()); ASSERT_TRUE(ResetHandles()); } static void PrintHandle(const char * tag, const PacketBuffer * buffer) { printf("%s %p ref=%u len=%-4zu next=%p\n", StringOrNullMarker(tag), buffer, buffer ? buffer->ref : 0, buffer ? buffer->len : 0, buffer ? buffer->next : nullptr); } static void PrintHandle(const char * tag, const PacketBufferHandle & handle) { PrintHandle(tag, handle.mBuffer); } struct BufferConfiguration { BufferConfiguration(uint16_t aReservedSize = 0) : init_len(0), reserved_size(aReservedSize), start_buffer(nullptr), end_buffer(nullptr), payload_ptr(nullptr), handle(nullptr) {} size_t init_len; uint16_t reserved_size; uint8_t * start_buffer; uint8_t * end_buffer; uint8_t * payload_ptr; PacketBufferHandle handle; }; static void PrintHandle(const char * tag, const BufferConfiguration & config) { PrintHandle(tag, config.handle); } static void PrintConfig(const char * tag, const BufferConfiguration & config) { printf("%s pay=%-4zu len=%-4zu res=%-4u:", StringOrNullMarker(tag), config.payload_ptr - config.start_buffer, config.init_len, config.reserved_size); PrintHandle("", config.handle); } /* * Buffers allocated through PrepareTestBuffer with kRecordHandle set will be recorded in `handles` so that their * reference counts can be verified by ResetHandles(). Initially they have two refs: the recorded one and the returned one. */ static constexpr int kRecordHandle = 0x01; static constexpr int kAllowHandleReuse = 0x02; void PrepareTestBuffer(BufferConfiguration * config, int flags = 0); /* * Checks and clears the recorded handles. Returns true if it detects no leaks or double frees. * Called from `TerminateTest()`, but tests may choose to call it more often to verify reference counts. */ bool ResetConfigurations(); bool ResetHandles(); std::vector configurations; std::vector handles; void CheckAddRef(); void CheckAddToEnd(); void CheckCompactHead(); void CheckConsume(); void CheckConsumeHead(); void CheckDataLength(); void CheckEnsureReservedSize(); void CheckFree(); void CheckFreeHead(); void CheckHandleAdopt(); void CheckHandleAdvance(); void CheckHandleCloneData(); void CheckHandleConstruct(); void CheckHandleFree(); void CheckHandleHold(); void CheckHandleMove(); void CheckHandleRelease(); void CheckHandleRetain(); void CheckHandleRightSize(); void CheckLast(); void CheckNew(); void CheckNext(); void CheckPopHead(); void CheckRead(); void CheckSetDataLength(); void CheckSetStart(); }; /** * Allocate memory for a test buffer and configure according to test buffer configuration. */ void TestSystemPacketBuffer::PrepareTestBuffer(BufferConfiguration * config, int flags) { if (config->handle.IsNull()) { config->handle = PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSizeWithoutReserve, 0); VerifyOrDieWithMsg(!config->handle.IsNull(), chipSystemLayer, "NewPacketBuffer: Failed to allocate packet buffer (%u retained): %s", static_cast(handles.size()), strerror(errno)); if (flags & kRecordHandle) { handles.push_back(config->handle.Retain()); } } else { VerifyOrDieWithMsg((flags & kAllowHandleReuse) != 0, chipSystemLayer, "Dirty test configuration"); } const size_t lInitialSize = kStructureSize + config->reserved_size; const size_t lAllocSize = kBlockSize; uint8_t * const raw = reinterpret_cast(config->handle.Get()); memset(raw + kStructureSize, 0, lAllocSize - kStructureSize); config->start_buffer = raw; config->end_buffer = raw + lAllocSize; if (lInitialSize > lAllocSize) { config->payload_ptr = config->end_buffer; } else { config->payload_ptr = config->start_buffer + lInitialSize; } if (config->handle->HasChainedBuffer()) { // This should not happen. PacketBuffer::Free(config->handle->ChainedBuffer()); config->handle->next = nullptr; } config->handle->payload = config->payload_ptr; #if CHIP_SYSTEM_CONFIG_USE_LWIP VerifyOrDieWithMsg(chip::CanCastTo(config->init_len), chipSystemLayer, "Max Length exceeded for LwIP based systems"); config->handle->len = static_cast(config->init_len); config->handle->tot_len = static_cast(config->init_len); #else config->handle->len = config->init_len; config->handle->tot_len = config->init_len; #endif } bool TestSystemPacketBuffer::ResetConfigurations() { // Clear the configurations' buffer handles. for (auto & configuration : configurations) configuration.handle = nullptr; return true; } bool TestSystemPacketBuffer::ResetHandles() { // Check against leaks or double-frees in tests: every handle obtained through // TestSystemPacketBuffer::NewPacketBuffer should have a reference count of 1. bool handles_ok = true; for (size_t i = 0; i < handles.size(); ++i) { const PacketBufferHandle & handle = handles[i]; if (handle.Get() == nullptr) { printf("TestTerminate: handle %u null\n", static_cast(i)); handles_ok = false; } else if (handle->ref != 1) { printf("TestTerminate: handle %u buffer=%p ref=%u\n", static_cast(i), handle.Get(), handle->ref); handles_ok = false; while (handle->ref > 1) { PacketBuffer::Free(handle.Get()); } } } handles.resize(0); return handles_ok; } /** * Test PacketBufferHandle::New() function. * * Description: For every buffer-configuration from inContext, create a buffer's instance * using the New() method. Then, verify that when the size of the reserved space * passed to New() is greater than PacketBuffer::kMaxSizeWithoutReserve, * the method returns nullptr. Otherwise, check for correctness of initializing * the new buffer's internal state. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckNew) { for (const auto & config : configurations) { const PacketBufferHandle buffer = PacketBufferHandle::New(0, config.reserved_size); if (config.reserved_size > PacketBuffer::kMaxAllocSize) { EXPECT_TRUE(buffer.IsNull()); continue; } EXPECT_LE(config.reserved_size, buffer->AllocSize()); ASSERT_FALSE(buffer.IsNull()); if (!buffer.IsNull()) { // TODO: the code below seems maybe questionable: OF_LWIP_PBUF is never used // NOLINTBEGIN(bugprone-casting-through-void) const pbuf * const pb = TO_LWIP_PBUF(buffer.Get()); // NOLINTEND(bugprone-casting-through-void) EXPECT_EQ(pb->len, static_cast(0)); EXPECT_EQ(pb->tot_len, static_cast(0)); EXPECT_EQ(pb->next, nullptr); EXPECT_EQ(pb->ref, 1); } } #if CHIP_SYSTEM_PACKETBUFFER_FROM_LWIP_POOL || CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_POOL // Use the rest of the buffer space std::vector allocate_all_the_things; for (;;) { PacketBufferHandle buffer = PacketBufferHandle::New(0, 0); if (buffer.IsNull()) { break; } // Hold on to the buffer, to use up all the buffer space. allocate_all_the_things.push_back(std::move(buffer)); } #endif // CHIP_SYSTEM_PACKETBUFFER_FROM_LWIP_POOL || CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_POOL } /** * Test PacketBuffer::Start() function. */ TEST_F(TestSystemPacketBuffer, CheckStart) { for (auto & config : configurations) { PrepareTestBuffer(&config, kRecordHandle); EXPECT_EQ(config.handle->Start(), config.payload_ptr); } } /** * Test PacketBuffer::SetStart() function. * * Description: For every buffer-configuration from inContext, create a * buffer's instance according to the configuration. Next, * for any offset value from start_offset[], pass it to the * buffer's instance through SetStart method. Then, verify that * the beginning of the buffer has been correctly internally * adjusted according to the offset value passed into the * SetStart() method. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckSetStart) { static constexpr ptrdiff_t sSizePacketBuffer = kBlockSize; for (auto & config : configurations) { // clang-format off static constexpr ptrdiff_t start_offset[] = { -sSizePacketBuffer, -128, -1, 0, 1, 128, sSizePacketBuffer }; // clang-format on for (ptrdiff_t offset : start_offset) { PrepareTestBuffer(&config, kRecordHandle | kAllowHandleReuse); uint8_t * const test_start = config.payload_ptr + offset; uint8_t * verify_start = test_start; config.handle->SetStart(test_start); if (verify_start < config.start_buffer + kStructureSize) { // Set start before valid payload beginning. verify_start = config.start_buffer + kStructureSize; } if (verify_start > config.end_buffer) { // Set start after valid payload beginning. verify_start = config.end_buffer; } EXPECT_EQ(config.handle->payload, verify_start); if (verify_start - config.payload_ptr > static_cast(config.init_len)) { // Set start to the beginning of payload, right after handle's header. EXPECT_EQ(config.handle->len, static_cast(0)); } else { // Set start to somewhere between the end of the handle's // header and the end of payload. EXPECT_EQ(config.handle->len, static_cast((static_cast(config.init_len) - (verify_start - config.payload_ptr)))); } } } } /** * Test PacketBuffer::DataLength() function. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckDataLength) { for (auto & config : configurations) { PrepareTestBuffer(&config, kRecordHandle); EXPECT_EQ(config.handle->DataLength(), config.handle->len); } } /** * Test PacketBuffer::SetDataLength() function. * * Description: Take two initial configurations of PacketBuffer from * inContext and create two PacketBuffer instances based on those * configurations. For any two buffers, call SetDataLength with * different value from sLength[]. If two buffers are created with * the same configuration, test SetDataLength on one buffer, * without specifying the head of the buffer chain. Otherwise, * test SetDataLength with one buffer being down the chain and the * other one being passed as the head of the chain. After calling * the method verify that data lengths were correctly adjusted. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckSetDataLength) { for (auto & config_1 : configurations) { for (auto & config_2 : configurations) { for (auto length : kLengths) { PrepareTestBuffer(&config_1, kRecordHandle | kAllowHandleReuse); PrepareTestBuffer(&config_2, kRecordHandle | kAllowHandleReuse); if (&config_1 == &config_2) { // headOfChain (the second arg) is NULL config_2.handle->SetDataLength(length, nullptr); if (length > (config_2.end_buffer - config_2.payload_ptr)) { EXPECT_EQ(config_2.handle->len, static_cast(config_2.end_buffer - config_2.payload_ptr)); EXPECT_EQ(config_2.handle->tot_len, static_cast(config_2.end_buffer - config_2.payload_ptr)); EXPECT_EQ(config_2.handle.GetNext(), nullptr); } else { EXPECT_EQ(config_2.handle->len, length); EXPECT_EQ(config_2.handle->tot_len, length); EXPECT_EQ(config_2.handle.GetNext(), nullptr); } } else { // headOfChain (the second arg) is config_1.handle config_2.handle->SetDataLength(length, config_1.handle); if (length > (config_2.end_buffer - config_2.payload_ptr)) { EXPECT_EQ(config_2.handle->len, static_cast(config_2.end_buffer - config_2.payload_ptr)); EXPECT_EQ(config_2.handle->tot_len, static_cast(config_2.end_buffer - config_2.payload_ptr)); EXPECT_EQ(config_2.handle.GetNext(), nullptr); EXPECT_EQ(config_1.handle->tot_len, static_cast(static_cast(config_1.init_len) + static_cast(config_2.end_buffer - config_2.payload_ptr) - static_cast(config_2.init_len))); } else { EXPECT_EQ(config_2.handle->len, length); EXPECT_EQ(config_2.handle->tot_len, length); EXPECT_EQ(config_2.handle.GetNext(), nullptr); EXPECT_EQ(config_1.handle->tot_len, static_cast(static_cast(config_1.init_len) + static_cast(length) - static_cast(config_2.init_len))); } } } } } } /** * Test PacketBuffer::TotalLength() function. */ TEST_F(TestSystemPacketBuffer, CheckTotalLength) { for (auto & config : configurations) { PrepareTestBuffer(&config, kRecordHandle); EXPECT_EQ(config.handle->TotalLength(), config.init_len); } } /** * Test PacketBuffer::MaxDataLength() function. */ TEST_F(TestSystemPacketBuffer, CheckMaxDataLength) { for (auto & config : configurations) { PrepareTestBuffer(&config, kRecordHandle); EXPECT_EQ(config.handle->MaxDataLength(), static_cast(config.end_buffer - config.payload_ptr)); } } /** * Test PacketBuffer::AvailableDataLength() function. */ TEST_F(TestSystemPacketBuffer, CheckAvailableDataLength) { for (auto & config : configurations) { PrepareTestBuffer(&config, kRecordHandle); EXPECT_EQ(config.handle->AvailableDataLength(), static_cast(static_cast(config.end_buffer - config.payload_ptr) - static_cast(config.init_len))); } } /** * Test PacketBuffer::ReservedSize() function. */ TEST_F(TestSystemPacketBuffer, CheckReservedSize) { for (auto & config : configurations) { PrepareTestBuffer(&config, kRecordHandle); const size_t kAllocSize = config.handle->AllocSize(); if (config.reserved_size > kAllocSize) { EXPECT_EQ(config.handle->ReservedSize(), kAllocSize); } else { EXPECT_EQ(config.handle->ReservedSize(), config.reserved_size); } } } /** * Test PacketBuffer::HasChainedBuffer() function. */ TEST_F(TestSystemPacketBuffer, CheckHasChainedBuffer) { for (auto & config_1 : configurations) { for (auto & config_2 : configurations) { if (&config_1 == &config_2) { continue; } PrepareTestBuffer(&config_1); PrepareTestBuffer(&config_2); EXPECT_FALSE(config_1.handle->HasChainedBuffer()); EXPECT_FALSE(config_2.handle->HasChainedBuffer()); config_1.handle->AddToEnd(config_2.handle.Retain()); EXPECT_TRUE(config_1.handle->HasChainedBuffer()); EXPECT_FALSE(config_2.handle->HasChainedBuffer()); config_1.handle = nullptr; config_2.handle = nullptr; } } } /** * Test PacketBuffer::AddToEnd() function. * * Description: Take three initial configurations of PacketBuffer from * inContext, create three PacketBuffers based on those * configurations and then link those buffers together with * PacketBuffer:AddToEnd(). Then, assert that after connecting * buffers together, their internal states are correctly updated. * This test function tests linking any combination of three * buffer-configurations passed within inContext. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckAddToEnd) { for (auto & config_1 : configurations) { for (auto & config_2 : configurations) { for (auto & config_3 : configurations) { if (&config_1 == &config_2 || &config_1 == &config_3 || &config_2 == &config_3) { continue; } PrepareTestBuffer(&config_1); PrepareTestBuffer(&config_2); PrepareTestBuffer(&config_3); EXPECT_EQ(config_1.handle->ref, 1); EXPECT_EQ(config_2.handle->ref, 1); EXPECT_EQ(config_3.handle->ref, 1); config_1.handle->AddToEnd(config_2.handle.Retain()); EXPECT_EQ(config_1.handle->ref, 1); // config_1.handle EXPECT_EQ(config_2.handle->ref, 2); // config_2.handle and config_1.handle->next EXPECT_EQ(config_3.handle->ref, 1); // config_3.handle EXPECT_EQ(config_1.handle->tot_len, (config_1.init_len + config_2.init_len)); EXPECT_EQ(config_1.handle.GetNext(), config_2.handle.Get()); EXPECT_EQ(config_2.handle.GetNext(), nullptr); EXPECT_EQ(config_3.handle.GetNext(), nullptr); config_1.handle->AddToEnd(config_3.handle.Retain()); EXPECT_EQ(config_1.handle->ref, 1); // config_1.handle EXPECT_EQ(config_2.handle->ref, 2); // config_2.handle and config_1.handle->next EXPECT_EQ(config_3.handle->ref, 2); // config_3.handle and config_2.handle->next EXPECT_EQ(config_1.handle->tot_len, (config_1.init_len + config_2.init_len + config_3.init_len)); EXPECT_EQ(config_1.handle.GetNext(), config_2.handle.Get()); EXPECT_EQ(config_2.handle.GetNext(), config_3.handle.Get()); EXPECT_EQ(config_3.handle.GetNext(), nullptr); config_1.handle = nullptr; config_2.handle = nullptr; config_3.handle = nullptr; } } } } /** * Test PacketBuffer::PopHead() function. * * Description: Take two initial configurations of PacketBuffer from * inContext and create two PacketBuffer instances based on those * configurations. Next, link those buffers together, with the first * buffer instance pointing to the second one. Then, call PopHead() * on the first buffer to unlink the second buffer. After the call, * verify correct internal state of the first buffer. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckPopHead) { // Single buffer test. for (auto & config_1 : configurations) { PrepareTestBuffer(&config_1, kRecordHandle | kAllowHandleReuse); EXPECT_EQ(config_1.handle->ref, 2); const PacketBuffer * const buffer_1 = config_1.handle.mBuffer; const PacketBufferHandle popped = config_1.handle.PopHead(); EXPECT_TRUE(config_1.handle.IsNull()); EXPECT_EQ(popped.mBuffer, buffer_1); EXPECT_EQ(popped->next, nullptr); EXPECT_EQ(popped->tot_len, config_1.init_len); EXPECT_EQ(popped->ref, 2); } ResetHandles(); // Chained buffers test. for (auto & config_1 : configurations) { for (auto & config_2 : configurations) { if (&config_1 == &config_2) { continue; } PrepareTestBuffer(&config_1, kRecordHandle | kAllowHandleReuse); PrepareTestBuffer(&config_2, kRecordHandle | kAllowHandleReuse); config_1.handle->AddToEnd(config_2.handle.Retain()); const PacketBufferHandle popped = config_1.handle.PopHead(); EXPECT_EQ(config_1.handle, config_2.handle); EXPECT_EQ(config_1.handle.GetNext(), nullptr); EXPECT_EQ(config_1.handle->tot_len, config_1.init_len); } } } /** * Test PacketBuffer::CompactHead() function. * * Description: Take two initial configurations of PacketBuffer from * inContext and create two PacketBuffer instances based on those * configurations. Next, set both buffers' data length to any * combination of values from sLengths[] and link those buffers * into a chain. Then, call CompactHead() on the first buffer in * the chain. After calling the method, verify correctly adjusted * state of the first buffer. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckCompactHead) { // Single buffer test. for (auto & config : configurations) { for (auto length : kLengths) { PrepareTestBuffer(&config, kRecordHandle | kAllowHandleReuse); config.handle->SetDataLength(length, config.handle); const uint32_t data_length = static_cast(config.handle->DataLength()); config.handle->CompactHead(); EXPECT_EQ(config.handle->payload, (config.start_buffer + kStructureSize)); EXPECT_EQ(config.handle->tot_len, data_length); } config.handle = nullptr; } EXPECT_TRUE(ResetHandles()); // Chained buffers test. for (auto & config_1 : configurations) { for (auto & config_2 : configurations) { if (&config_1 == &config_2) { continue; } // start with various initial length for the first buffer for (auto length_1 : kLengths) { // start with various initial length for the second buffer for (auto length_2 : kLengths) { PrepareTestBuffer(&config_1, kRecordHandle | kAllowHandleReuse); EXPECT_EQ(config_1.handle->ref, 2); // CompactHead requires that there be no other references to the chained buffer, // so we manage it manually. PrepareTestBuffer(&config_2); EXPECT_EQ(config_2.handle->ref, 1); PacketBuffer * buffer_2 = std::move(config_2.handle).UnsafeRelease(); EXPECT_TRUE(config_2.handle.IsNull()); config_1.handle->SetDataLength(length_1, config_1.handle); const uint32_t data_length_1 = static_cast(config_1.handle->DataLength()); // This chain will cause buffer_2 to be freed. config_1.handle->next = buffer_2; // Add various lengths to the second buffer buffer_2->SetDataLength(length_2, config_1.handle); const uint32_t data_length_2 = static_cast(buffer_2->DataLength()); config_1.handle->CompactHead(); EXPECT_EQ(config_1.handle->payload, (config_1.start_buffer + kStructureSize)); if (config_1.handle->tot_len > config_1.handle->MaxDataLength()) { EXPECT_EQ(config_1.handle->len, config_1.handle->MaxDataLength()); EXPECT_EQ(buffer_2->len, config_1.handle->tot_len - config_1.handle->MaxDataLength()); EXPECT_EQ(config_1.handle.GetNext(), buffer_2); EXPECT_EQ(config_1.handle->ref, 2); EXPECT_EQ(buffer_2->ref, 1); } else { EXPECT_EQ(config_1.handle->len, config_1.handle->tot_len); if (data_length_1 >= config_1.handle->MaxDataLength() && data_length_2 == 0) { /* make sure the second buffer is not freed */ EXPECT_EQ(config_1.handle.GetNext(), buffer_2); EXPECT_EQ(buffer_2->ref, 1); } else { /* make sure the second buffer is freed */ EXPECT_EQ(config_1.handle.GetNext(), nullptr); buffer_2 = nullptr; } } EXPECT_EQ(config_1.handle->ref, 2); config_1.handle = nullptr; // Verify and release handles. EXPECT_TRUE(ResetHandles()); } } } } } /** * Test PacketBuffer::ConsumeHead() function. * * Description: For every buffer-configuration from inContext, create a * buffer's instance according to the configuration. Next, * for any value from sLengths[], pass it to the buffer's * instance through ConsumeHead() method. Then, verify that * the internal state of the buffer has been correctly * adjusted according to the value passed into the method. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckConsumeHead) { for (auto & config : configurations) { for (auto length : kLengths) { PrepareTestBuffer(&config, kRecordHandle | kAllowHandleReuse); config.handle->ConsumeHead(length); if (length > config.init_len) { EXPECT_EQ(config.handle->payload, (config.payload_ptr + config.init_len)); EXPECT_EQ(config.handle->len, static_cast(0)); EXPECT_EQ(config.handle->tot_len, static_cast(0)); } else { EXPECT_EQ(config.handle->payload, (config.payload_ptr + length)); EXPECT_EQ(config.handle->len, (config.handle->len - length)); EXPECT_EQ(config.handle->tot_len, (config.handle->tot_len - length)); } } } } /** * Test PacketBuffer::Consume() function. * * Description: Take two different initial configurations of PacketBuffer from * inContext and create two PacketBuffer instances based on those * configurations. Next, set both buffers' data length to any * combination of values from sLengths[] and link those buffers * into a chain. Then, call Consume() on the first buffer in * the chain with all values from sLengths[]. After calling the * method, verify correctly adjusted the state of the first * buffer and appropriate return pointer from the method's call. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckConsume) { for (auto & config_1 : configurations) { for (auto & config_2 : configurations) { if (&config_1 == &config_2) { continue; } // consume various amounts of memory for (auto consumeLength : kLengths) { // start with various initial length for the first buffer for (auto len_1 : kLengths) { // start with various initial length for the second buffer for (auto len_2 : kLengths) { PrepareTestBuffer(&config_1); PrepareTestBuffer(&config_2); EXPECT_EQ(config_1.handle->ref, 1); EXPECT_EQ(config_2.handle->ref, 1); config_1.handle->AddToEnd(config_2.handle.Retain()); // Add various lengths to buffers config_1.handle->SetDataLength(len_1, config_1.handle); config_2.handle->SetDataLength(len_2, config_1.handle); const uint32_t buf_1_len = static_cast(config_1.handle->len); const uint32_t buf_2_len = static_cast(config_2.handle->len); PacketBufferHandle original_handle_1 = config_1.handle.Retain(); EXPECT_EQ(config_1.handle->ref, 2); // config_1.handle and original_handle_1 EXPECT_EQ(config_2.handle->ref, 2); // config_2.handle and config_1.handle->next config_1.handle.Consume(consumeLength); if (consumeLength == 0) { EXPECT_EQ(config_1.handle, original_handle_1); EXPECT_EQ(config_1.handle->len, buf_1_len); EXPECT_EQ(config_2.handle->len, buf_2_len); EXPECT_EQ(config_1.handle->ref, 2); // config_1.handle and original_handle_1 EXPECT_EQ(config_2.handle->ref, 2); // config_2.handle and config_1.handle->next } else if (consumeLength < buf_1_len) { EXPECT_EQ(config_1.handle, original_handle_1); EXPECT_EQ(config_1.handle->len, buf_1_len - consumeLength); EXPECT_EQ(config_2.handle->len, buf_2_len); EXPECT_EQ(config_1.handle->ref, 2); // config_1.handle and original_handle_1 EXPECT_EQ(config_2.handle->ref, 2); // config_2.handle and config_1.handle->next } else if ((consumeLength < buf_1_len + buf_2_len || (consumeLength == buf_1_len + buf_2_len && buf_2_len == 0))) { EXPECT_EQ(config_1.handle, config_2.handle); EXPECT_EQ(config_2.handle->len, buf_1_len + buf_2_len - consumeLength); EXPECT_EQ(original_handle_1->ref, 1); // original_handle_1 EXPECT_EQ(config_2.handle->ref, 2); // config_1.handle and config_2.handle } else { EXPECT_TRUE(config_1.handle.IsNull()); EXPECT_EQ(original_handle_1->ref, 1); // original_handle_1 EXPECT_EQ(config_2.handle->ref, 1); // config_2.handle } original_handle_1 = nullptr; config_1.handle = nullptr; config_2.handle = nullptr; } } } } } } /** * Test PacketBuffer::EnsureReservedSize() function. * * Description: For every buffer-configuration from inContext, create a * buffer's instance according to the configuration. Next, * manually specify how much space is reserved in the buffer. * Then, verify that EnsureReservedSize() method correctly * retrieves the amount of the reserved space. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckEnsureReservedSize) { for (auto & config : configurations) { for (auto length : kLengths) { PrepareTestBuffer(&config, kRecordHandle | kAllowHandleReuse); const uint32_t kAllocSize = static_cast(config.handle->AllocSize()); uint16_t reserved_size = config.reserved_size; if (kStructureSize + config.reserved_size > kAllocSize) { reserved_size = static_cast(kAllocSize - kStructureSize); } if (length <= reserved_size) { EXPECT_EQ(config.handle->EnsureReservedSize(length), true); continue; } if ((length + config.init_len) > (kAllocSize - kStructureSize)) { EXPECT_FALSE(config.handle->EnsureReservedSize(length)); continue; } EXPECT_EQ(config.handle->EnsureReservedSize(length), true); EXPECT_EQ(config.handle->payload, (config.payload_ptr + length - reserved_size)); } } } /** * Test PacketBuffer::AlignPayload() function. * * Description: For every buffer-configuration from inContext, create a * buffer's instance according to the configuration. Next, * manually specify how much space is reserved and the * required payload shift. Then, verify that AlignPayload() * method correctly aligns the payload start pointer. */ TEST_F(TestSystemPacketBuffer, CheckAlignPayload) { for (auto & config : configurations) { for (auto length : kLengths) { PrepareTestBuffer(&config, kRecordHandle | kAllowHandleReuse); const uint32_t kAllocSize = static_cast(config.handle->AllocSize()); if (length == 0) { EXPECT_FALSE(config.handle->AlignPayload(length)); continue; } uint32_t reserved_size = config.reserved_size; if (config.reserved_size > kAllocSize) { reserved_size = kAllocSize; } const uint32_t payload_offset = static_cast(reinterpret_cast(config.handle->Start()) % length); uint32_t payload_shift = 0; if (payload_offset > 0) payload_shift = static_cast(length - payload_offset); if (payload_shift <= kAllocSize - reserved_size) { EXPECT_EQ(config.handle->AlignPayload(length), true); EXPECT_EQ(((unsigned long) config.handle->Start() % length), 0UL); } else { EXPECT_FALSE(config.handle->AlignPayload(length)); } } } } /** * Test PacketBuffer::Next() function. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckNext) { for (auto & config_1 : configurations) { for (auto & config_2 : configurations) { PrepareTestBuffer(&config_1, kRecordHandle | kAllowHandleReuse); PrepareTestBuffer(&config_2, kRecordHandle | kAllowHandleReuse); if (&config_1 != &config_2) { EXPECT_TRUE(config_1.handle->Next().IsNull()); config_1.handle->AddToEnd(config_2.handle.Retain()); EXPECT_EQ(config_1.handle->Next(), config_2.handle); EXPECT_EQ(config_1.handle->ChainedBuffer(), config_2.handle.Get()); } else { EXPECT_FALSE(config_1.handle->HasChainedBuffer()); } EXPECT_FALSE(config_2.handle->HasChainedBuffer()); } } } /** * Test PacketBuffer::Last() function. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckLast) { for (auto & config_1 : configurations) { for (auto & config_2 : configurations) { for (auto & config_3 : configurations) { if (&config_1 == &config_2 || &config_1 == &config_3 || &config_2 == &config_3) { continue; } PrepareTestBuffer(&config_1); PrepareTestBuffer(&config_2); PrepareTestBuffer(&config_3); EXPECT_EQ(config_1.handle->Last(), config_1.handle); EXPECT_EQ(config_2.handle->Last(), config_2.handle); EXPECT_EQ(config_3.handle->Last(), config_3.handle); config_1.handle->AddToEnd(config_2.handle.Retain()); EXPECT_EQ(config_1.handle->Last(), config_2.handle); EXPECT_EQ(config_2.handle->Last(), config_2.handle); EXPECT_EQ(config_3.handle->Last(), config_3.handle); config_1.handle->AddToEnd(config_3.handle.Retain()); EXPECT_EQ(config_1.handle->Last(), config_3.handle); EXPECT_EQ(config_2.handle->Last(), config_3.handle); EXPECT_EQ(config_3.handle->Last(), config_3.handle); config_1.handle = nullptr; config_2.handle = nullptr; config_3.handle = nullptr; } } } } /** * Test PacketBuffer::Read() function. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckRead) { uint8_t payloads[2 * kBlockSize] = { 1 }; uint8_t result[2 * kBlockSize]; for (size_t i = 1; i < sizeof(payloads); ++i) { payloads[i] = static_cast(random()); } for (auto & config_1 : configurations) { for (auto & config_2 : configurations) { if (&config_1 == &config_2) { continue; } PrepareTestBuffer(&config_1, kAllowHandleReuse); PrepareTestBuffer(&config_2, kAllowHandleReuse); const size_t length_1 = config_1.handle->MaxDataLength(); const size_t length_2 = config_2.handle->MaxDataLength(); const size_t length_sum = length_1 + length_2; const uint32_t length_total = static_cast(length_sum); EXPECT_EQ(length_total, length_sum); memcpy(config_1.handle->Start(), payloads, length_1); memcpy(config_2.handle->Start(), payloads + length_1, length_2); config_1.handle->SetDataLength(length_1); config_2.handle->SetDataLength(length_2); config_1.handle->AddToEnd(config_2.handle.Retain()); EXPECT_EQ(config_1.handle->TotalLength(), length_total); if (length_1 >= 1) { // Check a read that does not span packet buffers. CHIP_ERROR err = config_1.handle->Read(result, 1); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(result[0], payloads[0]); } // Check a read that spans packet buffers. CHIP_ERROR err = config_1.handle->Read(result, length_total); EXPECT_EQ(err, CHIP_NO_ERROR); EXPECT_EQ(memcmp(payloads, result, length_total), 0); // Check a read that is too long fails. err = config_1.handle->Read(result, length_total + 1); EXPECT_EQ(err, CHIP_ERROR_BUFFER_TOO_SMALL); // Check that running off the end of a corrupt buffer chain is detected. if (length_total < UINT32_MAX) { // First case: TotalLength() is wrong. config_1.handle->tot_len = static_cast(config_1.handle->tot_len + 1); err = config_1.handle->Read(result, length_total + 1); EXPECT_EQ(err, CHIP_ERROR_INTERNAL); config_1.handle->tot_len = static_cast(config_1.handle->tot_len - 1); } if (length_1 >= 1) { // Second case: an individual buffer's DataLength() is wrong. config_1.handle->len = static_cast(config_1.handle->len - 1); err = config_1.handle->Read(result, length_total); EXPECT_EQ(err, CHIP_ERROR_INTERNAL); config_1.handle->len = static_cast(config_1.handle->len + 1); } config_1.handle = nullptr; config_2.handle = nullptr; } } } /** * Test PacketBuffer::AddRef() function. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckAddRef) { for (auto & config : configurations) { PrepareTestBuffer(&config, kRecordHandle); const auto refs = config.handle->ref; config.handle->AddRef(); EXPECT_EQ(config.handle->ref, refs + 1); config.handle->ref = refs; // Don't leak buffers. } } /** * Test PacketBuffer::Free() function. * * Description: Take two different initial configurations of PacketBuffer from * inContext and create two PacketBuffer instances based on those * configurations. Next, chain two buffers together and set each * buffer's reference count to one of the values from * init_ret_count[]. Then, call Free() on the first buffer in * the chain and verify correctly adjusted states of the two * buffers. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckFree) { const decltype(PacketBuffer::ref) init_ref_count[] = { 1, 2, 3 }; constexpr size_t kRefs = sizeof(init_ref_count) / sizeof(init_ref_count[0]); for (auto & config_1 : configurations) { for (auto & config_2 : configurations) { if (&config_1 == &config_2) { continue; } // start with various buffer ref counts for (size_t r = 0; r < kRefs; r++) { config_1.handle = PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSizeWithoutReserve, 0); config_2.handle = PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSizeWithoutReserve, 0); ASSERT_FALSE(config_1.handle.IsNull()); ASSERT_FALSE(config_2.handle.IsNull()); PrepareTestBuffer(&config_1, kAllowHandleReuse); PrepareTestBuffer(&config_2, kAllowHandleReuse); EXPECT_EQ(config_1.handle->ref, 1); EXPECT_EQ(config_2.handle->ref, 1); // Chain buffers. config_1.handle->next = config_2.handle.Get(); // Add various buffer ref counts. const auto initial_refs_1 = config_1.handle->ref = init_ref_count[r]; const auto initial_refs_2 = config_2.handle->ref = init_ref_count[(r + 1) % kRefs]; // Free head. PacketBuffer::Free(config_1.handle.mBuffer); if (initial_refs_1 == 1) { config_1.handle.mBuffer = nullptr; } // Verification. if (initial_refs_1 > 1) { // Verify that head ref count is decremented. EXPECT_EQ(config_1.handle->ref, initial_refs_1 - 1); // Verify that chain is maintained. EXPECT_EQ(config_1.handle.GetNext(), config_2.handle.Get()); // Verify that chained buffer ref count has not changed. EXPECT_EQ(config_2.handle->ref, initial_refs_2); } else { if (initial_refs_2 > 1) { // Verify that chained buffer ref count is decremented. EXPECT_EQ(config_2.handle->ref, initial_refs_2 - 1); } else { // Since the test used fake ref counts, config_2.handle now points // to a freed buffer; clear the handle's internal pointer. config_2.handle.mBuffer = nullptr; } } // Clean up. if (!config_1.handle.IsNull()) { config_1.handle->next = nullptr; config_1.handle->ref = 1; config_1.handle = nullptr; } if (!config_2.handle.IsNull()) { config_2.handle->ref = 1; config_2.handle = nullptr; } } } } } /** * Test PacketBuffer::FreeHead() function. * * Description: Take two different initial configurations of PacketBuffer from * inContext and create two PacketBuffer instances based on those * configurations. Next, chain two buffers together. Then, call * FreeHead() on the first buffer in the chain and verify that * the method returned pointer to the second buffer. */ TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckFreeHead) { for (auto & config_1 : configurations) { for (auto & config_2 : configurations) { if (&config_1 == &config_2) { continue; } // Test PacketBuffer::FreeHead PrepareTestBuffer(&config_1, kAllowHandleReuse); PrepareTestBuffer(&config_2, kAllowHandleReuse); EXPECT_EQ(config_1.handle->ref, 1); EXPECT_EQ(config_2.handle->ref, 1); PacketBufferHandle handle_1 = config_1.handle.Retain(); config_1.handle->AddToEnd(config_2.handle.Retain()); EXPECT_EQ(config_1.handle->ref, 2); EXPECT_EQ(config_2.handle->ref, 2); // config_2.handle and config_1.handle->next PacketBuffer * const returned = PacketBuffer::FreeHead(std::move(config_1.handle).UnsafeRelease()); EXPECT_EQ(handle_1->ref, 1); EXPECT_EQ(config_2.handle->ref, 2); // config_2.handle and returned EXPECT_EQ(returned, config_2.handle.Get()); config_1.handle = nullptr; EXPECT_EQ(config_2.handle->ref, 2); config_2.handle = nullptr; EXPECT_EQ(returned->ref, 1); PacketBuffer::Free(returned); // Test PacketBufferHandle::FreeHead PrepareTestBuffer(&config_1, kAllowHandleReuse); PrepareTestBuffer(&config_2, kAllowHandleReuse); EXPECT_EQ(config_1.handle->ref, 1); EXPECT_EQ(config_2.handle->ref, 1); handle_1 = config_1.handle.Retain(); config_1.handle->AddToEnd(config_2.handle.Retain()); EXPECT_EQ(config_1.handle->ref, 2); EXPECT_EQ(config_2.handle->ref, 2); // config_2.handle and config_1.handle->next PacketBuffer * const buffer_1 = config_1.handle.Get(); config_1.handle.FreeHead(); EXPECT_EQ(buffer_1->ref, 1); EXPECT_EQ(config_1.handle, config_2.handle); EXPECT_EQ(config_2.handle->ref, 2); // config_2.handle and config_1.handle config_1.handle = nullptr; config_2.handle = nullptr; } } } TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckHandleConstruct) { PacketBufferHandle handle_1; EXPECT_TRUE(handle_1.IsNull()); PacketBufferHandle handle_2(nullptr); EXPECT_TRUE(handle_2.IsNull()); PacketBufferHandle handle_3(PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSize)); ASSERT_FALSE(handle_3.IsNull()); // Private constructor. PacketBuffer * const buffer_3 = std::move(handle_3).UnsafeRelease(); PacketBufferHandle handle_4(buffer_3); EXPECT_EQ(handle_4.Get(), buffer_3); } TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckHandleMove) { for (auto & config_1 : configurations) { for (auto & config_2 : configurations) { if (&config_1 == &config_2) { continue; } PrepareTestBuffer(&config_1, kRecordHandle); PrepareTestBuffer(&config_2, kRecordHandle); const PacketBuffer * const buffer_1 = config_1.handle.Get(); const PacketBuffer * const buffer_2 = config_2.handle.Get(); EXPECT_NE(buffer_1, buffer_2); EXPECT_EQ(buffer_1->ref, 2); // test.handles and config_1.handle EXPECT_EQ(buffer_2->ref, 2); // test.handles and config_2.handle config_1.handle = std::move(config_2.handle); EXPECT_EQ(config_1.handle.Get(), buffer_2); EXPECT_EQ(config_2.handle.Get(), nullptr); EXPECT_EQ(buffer_1->ref, 1); // test.handles EXPECT_EQ(buffer_2->ref, 2); // test.handles and config_1.handle config_1.handle = nullptr; } // Verify and release handles. EXPECT_TRUE(ResetHandles()); } } TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckHandleRelease) { for (auto & config_1 : configurations) { PrepareTestBuffer(&config_1); PacketBuffer * const buffer_1 = config_1.handle.Get(); PacketBuffer * const taken_1 = std::move(config_1.handle).UnsafeRelease(); EXPECT_EQ(buffer_1, taken_1); EXPECT_TRUE(config_1.handle.IsNull()); EXPECT_EQ(buffer_1->ref, 1); PacketBuffer::Free(buffer_1); } } TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckHandleFree) { for (auto & config_1 : configurations) { PrepareTestBuffer(&config_1, kRecordHandle); const PacketBuffer * const buffer_1 = config_1.handle.Get(); EXPECT_EQ(buffer_1->ref, 2); // test.handles and config_1.handle config_1.handle = nullptr; EXPECT_TRUE(config_1.handle.IsNull()); EXPECT_EQ(config_1.handle.Get(), nullptr); EXPECT_EQ(buffer_1->ref, 1); // test.handles only } } TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckHandleRetain) { for (auto & config_1 : configurations) { PrepareTestBuffer(&config_1, kRecordHandle); EXPECT_EQ(config_1.handle->ref, 2); // test.handles and config_1.handle PacketBufferHandle handle_1 = config_1.handle.Retain(); EXPECT_EQ(config_1.handle, handle_1); EXPECT_EQ(config_1.handle->ref, 3); // test.handles and config_1.handle and handle_1 } } TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckHandleAdopt) { for (auto & config_1 : configurations) { PrepareTestBuffer(&config_1, kRecordHandle); PacketBuffer * buffer_1 = std::move(config_1.handle).UnsafeRelease(); EXPECT_TRUE(config_1.handle.IsNull()); EXPECT_EQ(buffer_1->ref, 2); // test.handles and buffer_1 config_1.handle = PacketBufferHandle::Adopt(buffer_1); EXPECT_EQ(config_1.handle.Get(), buffer_1); EXPECT_EQ(config_1.handle->ref, 2); // test.handles and config_1.handle config_1.handle = nullptr; EXPECT_TRUE(config_1.handle.IsNull()); EXPECT_EQ(buffer_1->ref, 1); // test.handles only } } TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckHandleHold) { for (auto & config_1 : configurations) { PrepareTestBuffer(&config_1, kRecordHandle); PacketBuffer * buffer_1 = std::move(config_1.handle).UnsafeRelease(); EXPECT_TRUE(config_1.handle.IsNull()); EXPECT_EQ(buffer_1->ref, 2); // test.handles and buffer_1 config_1.handle = PacketBufferHandle::Hold(buffer_1); EXPECT_EQ(config_1.handle.Get(), buffer_1); EXPECT_EQ(config_1.handle->ref, 3); // test.handles and config_1.handle and buffer_1 config_1.handle = nullptr; EXPECT_TRUE(config_1.handle.IsNull()); EXPECT_EQ(buffer_1->ref, 2); // test.handles only and buffer_1 PacketBuffer::Free(buffer_1); } } TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckHandleAdvance) { for (auto & config_1 : configurations) { for (auto & config_2 : configurations) { for (auto & config_3 : configurations) { if (&config_1 == &config_2 || &config_1 == &config_3 || &config_2 == &config_3) { continue; } PrepareTestBuffer(&config_1); PrepareTestBuffer(&config_2); PrepareTestBuffer(&config_3); PacketBufferHandle handle_1 = config_1.handle.Retain(); PacketBufferHandle handle_2 = config_2.handle.Retain(); PacketBufferHandle handle_3 = config_3.handle.Retain(); config_1.handle->AddToEnd(config_2.handle.Retain()); config_1.handle->AddToEnd(config_3.handle.Retain()); EXPECT_EQ(config_1.handle->ChainedBuffer(), config_2.handle.Get()); EXPECT_EQ(config_2.handle->ChainedBuffer(), config_3.handle.Get()); EXPECT_FALSE(config_3.handle->HasChainedBuffer()); EXPECT_EQ(handle_1->ref, 2); // handle_1 and config_1.handle EXPECT_EQ(handle_2->ref, 3); // handle_2 and config_2.handle and config_1.handle->next EXPECT_EQ(handle_3->ref, 3); // handle_3 and config_3.handle and config_2.handle->next config_1.handle.Advance(); EXPECT_EQ(config_1.handle, handle_2); EXPECT_EQ(handle_1->ref, 1); // handle_1 only EXPECT_EQ(handle_2->ref, 4); // handle_2, config_[12].handle, handle_1->next EXPECT_EQ(handle_3->ref, 3); // handle_3, config_3.handle, config_2.handle->next config_1.handle.Advance(); EXPECT_EQ(config_1.handle, handle_3); EXPECT_EQ(handle_1->ref, 1); // handle_1 only EXPECT_EQ(handle_2->ref, 3); // handle_2, config_2.handle, handle_1->next EXPECT_EQ(handle_3->ref, 4); // handle_3, config_[13].handle, handle_2->next config_1.handle = nullptr; config_2.handle = nullptr; config_3.handle = nullptr; } } } } TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckHandleRightSize) { static const char kPayload[] = "Joy!"; PacketBufferHandle handle = PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSizeWithoutReserve, 0); PacketBuffer * buffer = handle.mBuffer; memcpy(handle->Start(), kPayload, sizeof kPayload); buffer->SetDataLength(sizeof kPayload); EXPECT_EQ(handle->ref, 1); // RightSize should do nothing if there is another reference to the buffer. { PacketBufferHandle anotherHandle = handle.Retain(); handle.RightSize(); EXPECT_EQ(handle.mBuffer, buffer); } #if CHIP_SYSTEM_PACKETBUFFER_HAS_RIGHTSIZE handle.RightSize(); EXPECT_NE(handle.mBuffer, buffer); EXPECT_EQ(handle->DataLength(), sizeof kPayload); EXPECT_EQ(memcmp(handle->Start(), kPayload, sizeof kPayload), 0); #else // CHIP_SYSTEM_PACKETBUFFER_HAS_RIGHTSIZE // For this configuration, RightSize() does nothing. handle.RightSize(); EXPECT_EQ(handle.mBuffer, buffer); #endif // CHIP_SYSTEM_PACKETBUFFER_HAS_RIGHTSIZE } TEST_F_FROM_FIXTURE(TestSystemPacketBuffer, CheckHandleCloneData) { uint8_t lPayload[2 * PacketBuffer::kMaxAllocSize]; for (uint8_t & payload : lPayload) { payload = static_cast(random()); } for (auto & config_1 : configurations) { for (auto & config_2 : configurations) { if (&config_1 == &config_2) { continue; } PrepareTestBuffer(&config_1); PrepareTestBuffer(&config_2); const uint8_t * payload_1 = lPayload; memcpy(config_1.handle->Start(), payload_1, config_1.handle->MaxDataLength()); config_1.handle->SetDataLength(config_1.handle->MaxDataLength()); const uint8_t * payload_2 = lPayload + config_1.handle->MaxDataLength(); memcpy(config_2.handle->Start(), payload_2, config_2.handle->MaxDataLength()); config_2.handle->SetDataLength(config_2.handle->MaxDataLength()); // Clone single buffer. PacketBufferHandle clone_1 = config_1.handle.CloneData(); ASSERT_FALSE(clone_1.IsNull()); EXPECT_EQ(clone_1->DataLength(), config_1.handle->DataLength()); EXPECT_EQ(memcmp(clone_1->Start(), payload_1, clone_1->DataLength()), 0); if (clone_1->DataLength()) { // Verify that modifying the clone does not affect the original. ScrambleData(clone_1->Start(), clone_1->DataLength()); EXPECT_NE(memcmp(clone_1->Start(), payload_1, clone_1->DataLength()), 0); EXPECT_EQ(memcmp(config_1.handle->Start(), payload_1, config_1.handle->DataLength()), 0); } // Clone buffer chain. config_1.handle->AddToEnd(config_2.handle.Retain()); EXPECT_TRUE(config_1.handle->HasChainedBuffer()); clone_1 = config_1.handle.CloneData(); PacketBufferHandle clone_1_next = clone_1->Next(); ASSERT_FALSE(clone_1.IsNull()); EXPECT_TRUE(clone_1->HasChainedBuffer()); EXPECT_EQ(clone_1->DataLength(), config_1.handle->DataLength()); EXPECT_EQ(clone_1->TotalLength(), config_1.handle->TotalLength()); EXPECT_EQ(clone_1_next->DataLength(), config_2.handle->DataLength()); EXPECT_EQ(memcmp(clone_1->Start(), payload_1, clone_1->DataLength()), 0); EXPECT_EQ(memcmp(clone_1_next->Start(), payload_2, clone_1_next->DataLength()), 0); if (clone_1->DataLength()) { ScrambleData(clone_1->Start(), clone_1->DataLength()); EXPECT_NE(memcmp(clone_1->Start(), payload_1, clone_1->DataLength()), 0); EXPECT_EQ(memcmp(config_1.handle->Start(), payload_1, config_1.handle->DataLength()), 0); } if (clone_1_next->DataLength()) { ScrambleData(clone_1_next->Start(), clone_1_next->DataLength()); EXPECT_NE(memcmp(clone_1_next->Start(), payload_2, clone_1_next->DataLength()), 0); EXPECT_EQ(memcmp(config_2.handle->Start(), payload_2, config_2.handle->DataLength()), 0); } config_1.handle = nullptr; config_2.handle = nullptr; } } #if CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_HEAP // It is possible for a packet buffer allocation to return a larger block than requested (e.g. when using a shared pool) // and in particular to return a larger block than it is possible to request from PackBufferHandle::New(). // In that case, (a) it is incorrect to actually use the extra space, and (b) if it is not used, the clone will // be the maximum possible size. // // This is only testable on heap allocation configurations, where pbuf records the allocation size and we can manually // construct an oversize buffer. constexpr size_t kOversizeDataSize = PacketBuffer::kMaxAllocSize + 99; PacketBuffer * p = reinterpret_cast(chip::Platform::MemoryAlloc(kStructureSize + kOversizeDataSize)); ASSERT_NE(p, nullptr); p->next = nullptr; p->payload = reinterpret_cast(p) + kStructureSize; p->tot_len = 0; p->len = 0; p->ref = 1; p->alloc_size = kOversizeDataSize; PacketBufferHandle handle = PacketBufferHandle::Adopt(p); // Fill the buffer to maximum and verify that it can be cloned. size_t maxSize = PacketBuffer::kMaxAllocSize; memset(handle->Start(), 1, maxSize); handle->SetDataLength(maxSize); EXPECT_EQ(handle->DataLength(), maxSize); PacketBufferHandle clone = handle.CloneData(); ASSERT_FALSE(clone.IsNull()); EXPECT_EQ(clone->DataLength(), maxSize); EXPECT_EQ(memcmp(handle->Start(), clone->Start(), maxSize), 0); // Overfill the buffer and verify that it can not be cloned. memset(handle->Start(), 2, kOversizeDataSize); handle->SetDataLength(kOversizeDataSize); EXPECT_EQ(handle->DataLength(), kOversizeDataSize); clone = handle.CloneData(); EXPECT_TRUE(clone.IsNull()); // Free the packet buffer memory ourselves, since we allocated it ourselves. chip::Platform::MemoryFree(std::move(handle).UnsafeRelease()); #endif // CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_HEAP } TEST_F(TestSystemPacketBuffer, CheckPacketBufferWriter) { static const char kPayload[] = "Hello, world!"; PacketBufferWriter yay(PacketBufferHandle::New(sizeof(kPayload))); PacketBufferWriter nay(PacketBufferHandle::New(sizeof(kPayload)), sizeof(kPayload) - 2); ASSERT_FALSE(yay.IsNull()); ASSERT_FALSE(nay.IsNull()); yay.Put(kPayload); yay.Put('\0'); nay.Put(kPayload); nay.Put('\0'); EXPECT_TRUE(yay.Fit()); EXPECT_FALSE(nay.Fit()); PacketBufferHandle yayBuffer = yay.Finalize(); PacketBufferHandle nayBuffer = nay.Finalize(); EXPECT_TRUE(yay.IsNull()); EXPECT_TRUE(nay.IsNull()); ASSERT_FALSE(yayBuffer.IsNull()); EXPECT_TRUE(nayBuffer.IsNull()); EXPECT_EQ(memcmp(yayBuffer->Start(), kPayload, sizeof kPayload), 0); } } // namespace System } // namespace chip