| /* |
| * |
| * 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 |
| * <tt>chip::System::PacketBuffer</tt>, a class that provides |
| * structure for network packet buffer management. |
| */ |
| |
| #include <errno.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <utility> |
| #include <vector> |
| |
| #include <gtest/gtest.h> |
| |
| #include <lib/support/CHIPMem.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/SafeInt.h> |
| #include <platform/CHIPDeviceLayer.h> |
| #include <system/SystemPacketBuffer.h> |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| #include <lwip/init.h> |
| #include <lwip/tcpip.h> |
| #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<struct pbuf *>(reinterpret_cast<void *>(x))) |
| #define OF_LWIP_PBUF(x) (reinterpret_cast<PacketBuffer *>(reinterpret_cast<void *>(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<BufferConfiguration>(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<BufferConfiguration> configurations; |
| std::vector<PacketBufferHandle> 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(); |
| }; |
| |
| /* |
| * Run fixture's class function as a test. |
| */ |
| #define TEST_F_FROM_FIXTURE(test_fixture, test_name) \ |
| TEST_F(test_fixture, test_name) \ |
| { \ |
| test_name(); \ |
| } \ |
| void test_fixture::test_name() |
| |
| /** |
| * 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<unsigned int>(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<uint8_t *>(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<uint16_t>(config->init_len), chipSystemLayer, "Max Length exceeded for LwIP based systems"); |
| |
| config->handle->len = static_cast<uint16_t>(config->init_len); |
| config->handle->tot_len = static_cast<uint16_t>(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<unsigned int>(i)); |
| handles_ok = false; |
| } |
| else if (handle->ref != 1) |
| { |
| printf("TestTerminate: handle %u buffer=%p ref=%u\n", static_cast<unsigned int>(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::kMaxSizeWithoutReserve) |
| { |
| 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<size_t>(0)); |
| EXPECT_EQ(pb->tot_len, static_cast<size_t>(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<PacketBufferHandle> 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<ptrdiff_t>(config.init_len)) |
| { |
| // Set start to the beginning of payload, right after handle's header. |
| EXPECT_EQ(config.handle->len, static_cast<size_t>(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<size_t>((static_cast<int32_t>(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<size_t>(config_2.end_buffer - config_2.payload_ptr)); |
| EXPECT_EQ(config_2.handle->tot_len, static_cast<size_t>(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<size_t>(config_2.end_buffer - config_2.payload_ptr)); |
| EXPECT_EQ(config_2.handle->tot_len, static_cast<size_t>(config_2.end_buffer - config_2.payload_ptr)); |
| EXPECT_EQ(config_2.handle.GetNext(), nullptr); |
| |
| EXPECT_EQ(config_1.handle->tot_len, |
| static_cast<size_t>(static_cast<int32_t>(config_1.init_len) + |
| static_cast<int32_t>(config_2.end_buffer - config_2.payload_ptr) - |
| static_cast<int32_t>(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<size_t>(static_cast<int32_t>(config_1.init_len) + static_cast<int32_t>(length) - |
| static_cast<int32_t>(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<size_t>(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<size_t>(static_cast<int32_t>(config.end_buffer - config.payload_ptr) - |
| static_cast<int32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<size_t>(0)); |
| EXPECT_EQ(config.handle->tot_len, static_cast<size_t>(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<uint32_t>(config_1.handle->len); |
| const uint32_t buf_2_len = static_cast<uint32_t>(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<uint32_t>(config.handle->AllocSize()); |
| uint16_t reserved_size = config.reserved_size; |
| |
| if (kStructureSize + config.reserved_size > kAllocSize) |
| { |
| reserved_size = static_cast<uint16_t>(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<uint32_t>(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<uint32_t>(reinterpret_cast<uintptr_t>(config.handle->Start()) % length); |
| uint32_t payload_shift = 0; |
| if (payload_offset > 0) |
| payload_shift = static_cast<uint32_t>(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<uint8_t>(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<uint32_t>(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<uint16_t>(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<uint16_t>(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<uint16_t>(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<uint16_t>(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::kMaxSizeWithoutReserve]; |
| for (uint8_t & payload : lPayload) |
| { |
| payload = static_cast<uint8_t>(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 uint16_t kOversizeDataSize = PacketBuffer::kMaxSizeWithoutReserve + 99; |
| PacketBuffer * p = reinterpret_cast<PacketBuffer *>(chip::Platform::MemoryAlloc(kStructureSize + kOversizeDataSize)); |
| ASSERT_NE(p, nullptr); |
| |
| p->next = nullptr; |
| p->payload = reinterpret_cast<uint8_t *>(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. |
| |
| memset(handle->Start(), 1, PacketBuffer::kMaxSizeWithoutReserve); |
| handle->SetDataLength(PacketBuffer::kMaxSizeWithoutReserve); |
| EXPECT_EQ(handle->DataLength(), PacketBuffer::kMaxSizeWithoutReserve); |
| |
| PacketBufferHandle clone = handle.CloneData(); |
| ASSERT_FALSE(clone.IsNull()); |
| EXPECT_EQ(clone->DataLength(), PacketBuffer::kMaxSizeWithoutReserve); |
| EXPECT_EQ(memcmp(handle->Start(), clone->Start(), PacketBuffer::kMaxSizeWithoutReserve), 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 |