blob: 9bf2597d7a92b5ccf2b254e243849015af763054 [file] [log] [blame]
/*
*
* 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.
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <utility>
#include <vector>
#include <lib/support/CHIPMem.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/UnitTestContext.h>
#include <lib/support/UnitTestRegistration.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
#include <nlunit-test.h>
#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, uint16_t length)
{
for (uint16_t i = 0; i < length; ++i)
++start[i];
}
} // namespace
/*
* An instance of this class created for the test suite.
* It is a friend class of `PacketBuffer` and `PacketBufferHandle` because some tests
* use or check private methods or properties.
*/
class PacketBufferTest
{
public:
struct TestContext
{
const uint16_t * const reserved_sizes;
size_t reserved_size_count;
const uint16_t * const lengths;
size_t length_count;
PacketBufferTest * test;
};
static int TestSetup(void * inContext);
static int TestTeardown(void * inContext);
static int TestInitialize(void * inContext);
static int TestTerminate(void * inContext);
static void CheckNew(nlTestSuite * inSuite, void * inContext);
static void CheckStart(nlTestSuite * inSuite, void * inContext);
static void CheckSetStart(nlTestSuite * inSuite, void * inContext);
static void CheckDataLength(nlTestSuite * inSuite, void * inContext);
static void CheckSetDataLength(nlTestSuite * inSuite, void * inContext);
static void CheckTotalLength(nlTestSuite * inSuite, void * inContext);
static void CheckMaxDataLength(nlTestSuite * inSuite, void * inContext);
static void CheckAvailableDataLength(nlTestSuite * inSuite, void * inContext);
static void CheckReservedSize(nlTestSuite * inSuite, void * inContext);
static void CheckHasChainedBuffer(nlTestSuite * inSuite, void * inContext);
static void CheckAddToEnd(nlTestSuite * inSuite, void * inContext);
static void CheckPopHead(nlTestSuite * inSuite, void * inContext);
static void CheckCompactHead(nlTestSuite * inSuite, void * inContext);
static void CheckConsumeHead(nlTestSuite * inSuite, void * inContext);
static void CheckConsume(nlTestSuite * inSuite, void * inContext);
static void CheckEnsureReservedSize(nlTestSuite * inSuite, void * inContext);
static void CheckAlignPayload(nlTestSuite * inSuite, void * inContext);
static void CheckNext(nlTestSuite * inSuite, void * inContext);
static void CheckLast(nlTestSuite * inSuite, void * inContext);
static void CheckRead(nlTestSuite * inSuite, void * inContext);
static void CheckAddRef(nlTestSuite * inSuite, void * inContext);
static void CheckFree(nlTestSuite * inSuite, void * inContext);
static void CheckFreeHead(nlTestSuite * inSuite, void * inContext);
static void CheckHandleConstruct(nlTestSuite * inSuite, void * inContext);
static void CheckHandleMove(nlTestSuite * inSuite, void * inContext);
static void CheckHandleRelease(nlTestSuite * inSuite, void * inContext);
static void CheckHandleFree(nlTestSuite * inSuite, void * inContext);
static void CheckHandleRetain(nlTestSuite * inSuite, void * inContext);
static void CheckHandleAdopt(nlTestSuite * inSuite, void * inContext);
static void CheckHandleHold(nlTestSuite * inSuite, void * inContext);
static void CheckHandleAdvance(nlTestSuite * inSuite, void * inContext);
static void CheckHandleRightSize(nlTestSuite * inSuite, void * inContext);
static void CheckHandleCloneData(nlTestSuite * inSuite, void * inContext);
static void CheckPacketBufferWriter(nlTestSuite * inSuite, void * inContext);
static void CheckBuildFreeList(nlTestSuite * inSuite, void * inContext);
static void PrintHandle(const char * tag, const PacketBuffer * buffer)
{
printf("%s %p ref=%u len=%-4u 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); }
static constexpr uint16_t kBlockSize = PacketBuffer::kBlockSize;
private:
struct BufferConfiguration
{
BufferConfiguration(uint16_t aReservedSize = 0) :
init_len(0), reserved_size(aReservedSize), start_buffer(nullptr), end_buffer(nullptr), payload_ptr(nullptr),
handle(nullptr)
{}
uint16_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=%-4u res=%-4u:", StringOrNullMarker(tag), config.payload_ptr - config.start_buffer,
config.init_len, config.reserved_size);
PrintHandle("", config.handle);
}
PacketBufferTest(TestContext * context);
~PacketBufferTest();
int InitializeTest(TestContext * context);
int TerminateTest(TestContext * context);
/*
* 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 ResetHandles();
TestContext * mContext;
std::vector<BufferConfiguration> configurations;
std::vector<PacketBufferHandle> handles;
};
const uint16_t sTestReservedSizes[] = { 0, 10, 128, 1536, PacketBuffer::kMaxSizeWithoutReserve, PacketBufferTest::kBlockSize };
const uint16_t sTestLengths[] = { 0, 1, 10, 128, PacketBufferTest::kBlockSize, UINT16_MAX };
PacketBufferTest::TestContext sContext = {
sTestReservedSizes,
sizeof(sTestReservedSizes) / sizeof(uint16_t),
sTestLengths,
sizeof(sTestLengths) / sizeof(uint16_t),
};
PacketBufferTest::PacketBufferTest(TestContext * context) : mContext(context)
{
// Set up the buffer configuration vector for this suite.
configurations.resize(0);
for (size_t i = 0; i < mContext->reserved_size_count; ++i)
{
configurations.emplace_back<BufferConfiguration>(mContext->reserved_sizes[i]);
}
}
int PacketBufferTest::TestSetup(void * inContext)
{
chip::Platform::MemoryInit();
if (chip::DeviceLayer::PlatformMgr().InitChipStack() != CHIP_NO_ERROR)
return FAILURE;
TestContext * const theContext = reinterpret_cast<TestContext *>(inContext);
theContext->test = new (std::nothrow) PacketBufferTest(theContext);
if (theContext->test == nullptr)
{
return FAILURE;
}
return SUCCESS;
}
int PacketBufferTest::TestTeardown(void * inContext)
{
chip::DeviceLayer::PlatformMgr().Shutdown();
chip::Platform::MemoryShutdown();
return SUCCESS;
}
int PacketBufferTest::TestInitialize(void * inContext)
{
TestContext * const theContext = reinterpret_cast<TestContext *>(inContext);
if (theContext->test == nullptr)
{
return FAILURE;
}
return theContext->test->InitializeTest(theContext);
}
int PacketBufferTest::InitializeTest(TestContext * context)
{
if (context != mContext)
{
return FAILURE;
}
return SUCCESS;
}
int PacketBufferTest::TestTerminate(void * inContext)
{
TestContext * const theContext = reinterpret_cast<TestContext *>(inContext);
if (theContext->test == nullptr)
{
return FAILURE;
}
return theContext->test->TerminateTest(theContext);
}
int PacketBufferTest::TerminateTest(TestContext * context)
{
const bool context_ok = (context == mContext);
// Clear the configurations' bufffer handles.
for (auto & configuration : configurations)
{
configuration.handle = nullptr;
}
const bool handles_ok = ResetHandles();
return (context_ok && handles_ok) ? SUCCESS : FAILURE;
}
/**
* Allocate memory for a test buffer and configure according to test buffer configuration.
*/
void PacketBufferTest::PrepareTestBuffer(BufferConfiguration * config, int flags)
{
if (config->handle.IsNull())
{
config->handle = PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSizeWithoutReserve, 0);
if (config->handle.IsNull())
{
printf("NewPacketBuffer: Failed to allocate packet buffer (%u retained): %s\n",
static_cast<unsigned int>(handles.size()), strerror(errno));
exit(EXIT_FAILURE);
}
if (flags & kRecordHandle)
{
handles.push_back(config->handle.Retain());
}
}
else if ((flags & kAllowHandleReuse) == 0)
{
printf("Dirty test configuration\n");
exit(EXIT_FAILURE);
}
const size_t lInitialSize = PacketBuffer::kStructureSize + config->reserved_size;
const size_t lAllocSize = kBlockSize;
uint8_t * const raw = reinterpret_cast<uint8_t *>(config->handle.Get());
memset(raw + PacketBuffer::kStructureSize, 0, lAllocSize - PacketBuffer::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;
config->handle->len = config->init_len;
config->handle->tot_len = config->init_len;
}
bool PacketBufferTest::ResetHandles()
{
// Check against leaks or double-frees in tests: every handle obtained through
// PacketBufferTest::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 functions invoked from the suite.
/**
* 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.
*/
void PacketBufferTest::CheckNew(nlTestSuite * inSuite, void * inContext)
{
TestContext * const theContext = static_cast<TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (const auto & config : test->configurations)
{
const PacketBufferHandle buffer = PacketBufferHandle::New(0, config.reserved_size);
if (config.reserved_size > PacketBuffer::kMaxSizeWithoutReserve)
{
NL_TEST_ASSERT(inSuite, buffer.IsNull());
continue;
}
NL_TEST_ASSERT(inSuite, config.reserved_size <= buffer->AllocSize());
NL_TEST_ASSERT(inSuite, !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)
NL_TEST_ASSERT(inSuite, pb->len == 0);
NL_TEST_ASSERT(inSuite, pb->tot_len == 0);
NL_TEST_ASSERT(inSuite, pb->next == nullptr);
NL_TEST_ASSERT(inSuite, 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.
*/
void PacketBufferTest::CheckStart(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config : test->configurations)
{
test->PrepareTestBuffer(&config, kRecordHandle);
NL_TEST_ASSERT(inSuite, 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.
*/
void PacketBufferTest::CheckSetStart(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
static constexpr ptrdiff_t sSizePacketBuffer = kBlockSize;
for (auto & config : test->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)
{
test->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 + PacketBuffer::kStructureSize)
{
// Set start before valid payload beginning.
verify_start = config.start_buffer + PacketBuffer::kStructureSize;
}
if (verify_start > config.end_buffer)
{
// Set start after valid payload beginning.
verify_start = config.end_buffer;
}
NL_TEST_ASSERT(inSuite, config.handle->payload == verify_start);
if ((verify_start - config.payload_ptr) > config.init_len)
{
// Set start to the beginning of payload, right after handle's header.
NL_TEST_ASSERT(inSuite, config.handle->len == 0);
}
else
{
// Set start to somewhere between the end of the handle's
// header and the end of payload.
NL_TEST_ASSERT(inSuite, config.handle->len == (config.init_len - (verify_start - config.payload_ptr)));
}
}
}
}
/**
* Test PacketBuffer::DataLength() function.
*/
void PacketBufferTest::CheckDataLength(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config : test->configurations)
{
test->PrepareTestBuffer(&config, kRecordHandle);
NL_TEST_ASSERT(inSuite, 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.
*/
void PacketBufferTest::CheckSetDataLength(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config_1 : test->configurations)
{
for (auto & config_2 : test->configurations)
{
for (size_t i = 0; i < theContext->length_count; ++i)
{
const uint16_t length = theContext->lengths[i];
test->PrepareTestBuffer(&config_1, kRecordHandle | kAllowHandleReuse);
test->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))
{
NL_TEST_ASSERT(inSuite, config_2.handle->len == (config_2.end_buffer - config_2.payload_ptr));
NL_TEST_ASSERT(inSuite, config_2.handle->tot_len == (config_2.end_buffer - config_2.payload_ptr));
NL_TEST_ASSERT(inSuite, config_2.handle->next == nullptr);
}
else
{
NL_TEST_ASSERT(inSuite, config_2.handle->len == length);
NL_TEST_ASSERT(inSuite, config_2.handle->tot_len == length);
NL_TEST_ASSERT(inSuite, config_2.handle->next == 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))
{
NL_TEST_ASSERT(inSuite, config_2.handle->len == (config_2.end_buffer - config_2.payload_ptr));
NL_TEST_ASSERT(inSuite, config_2.handle->tot_len == (config_2.end_buffer - config_2.payload_ptr));
NL_TEST_ASSERT(inSuite, config_2.handle->next == nullptr);
NL_TEST_ASSERT(inSuite,
config_1.handle->tot_len ==
(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
{
NL_TEST_ASSERT(inSuite, config_2.handle->len == length);
NL_TEST_ASSERT(inSuite, config_2.handle->tot_len == length);
NL_TEST_ASSERT(inSuite, config_2.handle->next == nullptr);
NL_TEST_ASSERT(
inSuite,
config_1.handle->tot_len ==
(config_1.init_len + static_cast<int32_t>(length) - static_cast<int32_t>(config_2.init_len)));
}
}
}
}
}
}
/**
* Test PacketBuffer::TotalLength() function.
*/
void PacketBufferTest::CheckTotalLength(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config : test->configurations)
{
test->PrepareTestBuffer(&config, kRecordHandle);
NL_TEST_ASSERT(inSuite, config.handle->TotalLength() == config.init_len);
}
}
/**
* Test PacketBuffer::MaxDataLength() function.
*/
void PacketBufferTest::CheckMaxDataLength(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config : test->configurations)
{
test->PrepareTestBuffer(&config, kRecordHandle);
NL_TEST_ASSERT(inSuite, config.handle->MaxDataLength() == (config.end_buffer - config.payload_ptr));
}
}
/**
* Test PacketBuffer::AvailableDataLength() function.
*/
void PacketBufferTest::CheckAvailableDataLength(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config : test->configurations)
{
test->PrepareTestBuffer(&config, kRecordHandle);
NL_TEST_ASSERT(inSuite,
config.handle->AvailableDataLength() == ((config.end_buffer - config.payload_ptr) - config.init_len));
}
}
/**
* Test PacketBuffer::ReservedSize() function.
*/
void PacketBufferTest::CheckReservedSize(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config : test->configurations)
{
test->PrepareTestBuffer(&config, kRecordHandle);
const size_t kAllocSize = config.handle->AllocSize();
if (config.reserved_size > kAllocSize)
{
NL_TEST_ASSERT(inSuite, config.handle->ReservedSize() == kAllocSize);
}
else
{
NL_TEST_ASSERT(inSuite, config.handle->ReservedSize() == config.reserved_size);
}
}
}
/**
* Test PacketBuffer::HasChainedBuffer() function.
*/
void PacketBufferTest::CheckHasChainedBuffer(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config_1 : test->configurations)
{
for (auto & config_2 : test->configurations)
{
if (&config_1 == &config_2)
{
continue;
}
test->PrepareTestBuffer(&config_1);
test->PrepareTestBuffer(&config_2);
NL_TEST_ASSERT(inSuite, config_1.handle->HasChainedBuffer() == false);
NL_TEST_ASSERT(inSuite, config_2.handle->HasChainedBuffer() == false);
config_1.handle->AddToEnd(config_2.handle.Retain());
NL_TEST_ASSERT(inSuite, config_1.handle->HasChainedBuffer() == true);
NL_TEST_ASSERT(inSuite, config_2.handle->HasChainedBuffer() == false);
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.
*/
void PacketBufferTest::CheckAddToEnd(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config_1 : test->configurations)
{
for (auto & config_2 : test->configurations)
{
for (auto & config_3 : test->configurations)
{
if (&config_1 == &config_2 || &config_1 == &config_3 || &config_2 == &config_3)
{
continue;
}
test->PrepareTestBuffer(&config_1);
test->PrepareTestBuffer(&config_2);
test->PrepareTestBuffer(&config_3);
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 1);
NL_TEST_ASSERT(inSuite, config_2.handle->ref == 1);
NL_TEST_ASSERT(inSuite, config_3.handle->ref == 1);
config_1.handle->AddToEnd(config_2.handle.Retain());
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 1); // config_1.handle
NL_TEST_ASSERT(inSuite, config_2.handle->ref == 2); // config_2.handle and config_1.handle->next
NL_TEST_ASSERT(inSuite, config_3.handle->ref == 1); // config_3.handle
NL_TEST_ASSERT(inSuite, config_1.handle->tot_len == (config_1.init_len + config_2.init_len));
NL_TEST_ASSERT(inSuite, config_1.handle->next == config_2.handle.Get());
NL_TEST_ASSERT(inSuite, config_2.handle->next == nullptr);
NL_TEST_ASSERT(inSuite, config_3.handle->next == nullptr);
config_1.handle->AddToEnd(config_3.handle.Retain());
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 1); // config_1.handle
NL_TEST_ASSERT(inSuite, config_2.handle->ref == 2); // config_2.handle and config_1.handle->next
NL_TEST_ASSERT(inSuite, config_3.handle->ref == 2); // config_3.handle and config_2.handle->next
NL_TEST_ASSERT(inSuite, config_1.handle->tot_len == (config_1.init_len + config_2.init_len + config_3.init_len));
NL_TEST_ASSERT(inSuite, config_1.handle->next == config_2.handle.Get());
NL_TEST_ASSERT(inSuite, config_2.handle->next == config_3.handle.Get());
NL_TEST_ASSERT(inSuite, config_3.handle->next == 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.
*/
void PacketBufferTest::CheckPopHead(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
// Single buffer test.
for (auto & config_1 : test->configurations)
{
test->PrepareTestBuffer(&config_1, kRecordHandle | kAllowHandleReuse);
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 2);
const PacketBuffer * const buffer_1 = config_1.handle.mBuffer;
const PacketBufferHandle popped = config_1.handle.PopHead();
NL_TEST_ASSERT(inSuite, config_1.handle.IsNull());
NL_TEST_ASSERT(inSuite, popped.mBuffer == buffer_1);
NL_TEST_ASSERT(inSuite, popped->next == nullptr);
NL_TEST_ASSERT(inSuite, popped->tot_len == config_1.init_len);
NL_TEST_ASSERT(inSuite, popped->ref == 2);
}
test->ResetHandles();
// Chained buffers test.
for (auto & config_1 : test->configurations)
{
for (auto & config_2 : test->configurations)
{
if (&config_1 == &config_2)
{
continue;
}
test->PrepareTestBuffer(&config_1, kRecordHandle | kAllowHandleReuse);
test->PrepareTestBuffer(&config_2, kRecordHandle | kAllowHandleReuse);
config_1.handle->AddToEnd(config_2.handle.Retain());
const PacketBufferHandle popped = config_1.handle.PopHead();
NL_TEST_ASSERT(inSuite, config_1.handle == config_2.handle);
NL_TEST_ASSERT(inSuite, config_1.handle->next == nullptr);
NL_TEST_ASSERT(inSuite, 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.
*/
void PacketBufferTest::CheckCompactHead(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
// Single buffer test.
for (auto & config : test->configurations)
{
for (size_t i = 0; i < theContext->length_count; ++i)
{
const uint16_t length = theContext->lengths[i];
test->PrepareTestBuffer(&config, kRecordHandle | kAllowHandleReuse);
config.handle->SetDataLength(length, config.handle);
const uint16_t data_length = config.handle->DataLength();
config.handle->CompactHead();
NL_TEST_ASSERT(inSuite, config.handle->payload == (config.start_buffer + PacketBuffer::kStructureSize));
NL_TEST_ASSERT(inSuite, config.handle->tot_len == data_length);
}
config.handle = nullptr;
}
NL_TEST_ASSERT(inSuite, test->ResetHandles());
// Chained buffers test.
for (auto & config_1 : test->configurations)
{
for (auto & config_2 : test->configurations)
{
if (&config_1 == &config_2)
{
continue;
}
// start with various initial length for the first buffer
for (size_t i = 0; i < theContext->length_count; ++i)
{
const uint16_t length_1 = theContext->lengths[i];
// start with various initial length for the second buffer
for (size_t j = 0; j < theContext->length_count; ++j)
{
const uint16_t length_2 = theContext->lengths[j];
test->PrepareTestBuffer(&config_1, kRecordHandle | kAllowHandleReuse);
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 2);
// CompactHead requires that there be no other references to the chained buffer,
// so we manage it manually.
test->PrepareTestBuffer(&config_2);
NL_TEST_ASSERT(inSuite, config_2.handle->ref == 1);
PacketBuffer * buffer_2 = std::move(config_2.handle).UnsafeRelease();
NL_TEST_ASSERT(inSuite, config_2.handle.IsNull());
config_1.handle->SetDataLength(length_1, config_1.handle);
const uint16_t data_length_1 = 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 uint16_t data_length_2 = buffer_2->DataLength();
config_1.handle->CompactHead();
NL_TEST_ASSERT(inSuite, config_1.handle->payload == (config_1.start_buffer + PacketBuffer::kStructureSize));
if (config_1.handle->tot_len > config_1.handle->MaxDataLength())
{
NL_TEST_ASSERT(inSuite, config_1.handle->len == config_1.handle->MaxDataLength());
NL_TEST_ASSERT(inSuite, buffer_2->len == config_1.handle->tot_len - config_1.handle->MaxDataLength());
NL_TEST_ASSERT(inSuite, config_1.handle->next == buffer_2);
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 2);
NL_TEST_ASSERT(inSuite, buffer_2->ref == 1);
}
else
{
NL_TEST_ASSERT(inSuite, 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 */
NL_TEST_ASSERT(inSuite, config_1.handle->next == buffer_2);
NL_TEST_ASSERT(inSuite, buffer_2->ref == 1);
}
else
{
/* make sure the second buffer is freed */
NL_TEST_ASSERT(inSuite, config_1.handle->next == nullptr);
buffer_2 = nullptr;
}
}
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 2);
config_1.handle = nullptr;
// Verify and release handles.
NL_TEST_ASSERT(inSuite, test->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.
*/
void PacketBufferTest::CheckConsumeHead(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config : test->configurations)
{
for (size_t i = 0; i < theContext->length_count; ++i)
{
const uint16_t length = theContext->lengths[i];
test->PrepareTestBuffer(&config, kRecordHandle | kAllowHandleReuse);
config.handle->ConsumeHead(length);
if (length > config.init_len)
{
NL_TEST_ASSERT(inSuite, config.handle->payload == (config.payload_ptr + config.init_len));
NL_TEST_ASSERT(inSuite, config.handle->len == 0);
NL_TEST_ASSERT(inSuite, config.handle->tot_len == 0);
}
else
{
NL_TEST_ASSERT(inSuite, config.handle->payload == (config.payload_ptr + length));
NL_TEST_ASSERT(inSuite, config.handle->len == (config.handle->len - length));
NL_TEST_ASSERT(inSuite, 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.
*/
void PacketBufferTest::CheckConsume(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config_1 : test->configurations)
{
for (auto & config_2 : test->configurations)
{
if (&config_1 == &config_2)
{
continue;
}
// consume various amounts of memory
for (size_t i = 0; i < theContext->length_count; ++i)
{
const uint16_t consumeLength = theContext->lengths[i];
// start with various initial length for the first buffer
for (size_t j = 0; j < theContext->length_count; ++j)
{
const uint16_t len_1 = theContext->lengths[j];
// start with various initial length for the second buffer
for (size_t k = 0; k < theContext->length_count; ++k)
{
const uint16_t len_2 = theContext->lengths[k];
test->PrepareTestBuffer(&config_1);
test->PrepareTestBuffer(&config_2);
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 1);
NL_TEST_ASSERT(inSuite, 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 uint16_t buf_1_len = config_1.handle->len;
const uint16_t buf_2_len = config_2.handle->len;
PacketBufferHandle original_handle_1 = config_1.handle.Retain();
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 2); // config_1.handle and original_handle_1
NL_TEST_ASSERT(inSuite, config_2.handle->ref == 2); // config_2.handle and config_1.handle->next
config_1.handle.Consume(consumeLength);
if (consumeLength == 0)
{
NL_TEST_ASSERT(inSuite, config_1.handle == original_handle_1);
NL_TEST_ASSERT(inSuite, config_1.handle->len == buf_1_len);
NL_TEST_ASSERT(inSuite, config_2.handle->len == buf_2_len);
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 2); // config_1.handle and original_handle_1
NL_TEST_ASSERT(inSuite, config_2.handle->ref == 2); // config_2.handle and config_1.handle->next
}
else if (consumeLength < buf_1_len)
{
NL_TEST_ASSERT(inSuite, config_1.handle == original_handle_1);
NL_TEST_ASSERT(inSuite, config_1.handle->len == buf_1_len - consumeLength);
NL_TEST_ASSERT(inSuite, config_2.handle->len == buf_2_len);
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 2); // config_1.handle and original_handle_1
NL_TEST_ASSERT(inSuite, 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)))
{
NL_TEST_ASSERT(inSuite, config_1.handle == config_2.handle);
NL_TEST_ASSERT(inSuite, config_2.handle->len == buf_1_len + buf_2_len - consumeLength);
NL_TEST_ASSERT(inSuite, original_handle_1->ref == 1); // original_handle_1
NL_TEST_ASSERT(inSuite, config_2.handle->ref == 2); // config_1.handle and config_2.handle
}
else
{
NL_TEST_ASSERT(inSuite, config_1.handle.IsNull());
NL_TEST_ASSERT(inSuite, original_handle_1->ref == 1); // original_handle_1
NL_TEST_ASSERT(inSuite, 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.
*/
void PacketBufferTest::CheckEnsureReservedSize(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config : test->configurations)
{
for (size_t i = 0; i < theContext->length_count; ++i)
{
const uint16_t length = theContext->lengths[i];
test->PrepareTestBuffer(&config, kRecordHandle | kAllowHandleReuse);
const uint16_t kAllocSize = config.handle->AllocSize();
uint16_t reserved_size = config.reserved_size;
if (PacketBuffer::kStructureSize + config.reserved_size > kAllocSize)
{
reserved_size = static_cast<uint16_t>(kAllocSize - PacketBuffer::kStructureSize);
}
if (length <= reserved_size)
{
NL_TEST_ASSERT(inSuite, config.handle->EnsureReservedSize(length) == true);
continue;
}
if ((length + config.init_len) > (kAllocSize - PacketBuffer::kStructureSize))
{
NL_TEST_ASSERT(inSuite, config.handle->EnsureReservedSize(length) == false);
continue;
}
NL_TEST_ASSERT(inSuite, config.handle->EnsureReservedSize(length) == true);
NL_TEST_ASSERT(inSuite, 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.
*/
void PacketBufferTest::CheckAlignPayload(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config : test->configurations)
{
for (size_t n = 0; n < theContext->length_count; ++n)
{
test->PrepareTestBuffer(&config, kRecordHandle | kAllowHandleReuse);
const uint16_t kAllocSize = config.handle->AllocSize();
if (theContext->lengths[n] == 0)
{
NL_TEST_ASSERT(inSuite, config.handle->AlignPayload(theContext->lengths[n]) == false);
continue;
}
uint16_t reserved_size = config.reserved_size;
if (config.reserved_size > kAllocSize)
{
reserved_size = kAllocSize;
}
const uint16_t payload_offset =
static_cast<uint16_t>(reinterpret_cast<uintptr_t>(config.handle->Start()) % theContext->lengths[n]);
uint16_t payload_shift = 0;
if (payload_offset > 0)
payload_shift = static_cast<uint16_t>(theContext->lengths[n] - payload_offset);
if (payload_shift <= kAllocSize - reserved_size)
{
NL_TEST_ASSERT(inSuite, config.handle->AlignPayload(theContext->lengths[n]) == true);
NL_TEST_ASSERT(inSuite, ((unsigned long) config.handle->Start() % theContext->lengths[n]) == 0);
}
else
{
NL_TEST_ASSERT(inSuite, config.handle->AlignPayload(theContext->lengths[n]) == false);
}
}
}
}
/**
* Test PacketBuffer::Next() function.
*/
void PacketBufferTest::CheckNext(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config_1 : test->configurations)
{
for (auto & config_2 : test->configurations)
{
test->PrepareTestBuffer(&config_1, kRecordHandle | kAllowHandleReuse);
test->PrepareTestBuffer(&config_2, kRecordHandle | kAllowHandleReuse);
if (&config_1 != &config_2)
{
NL_TEST_ASSERT(inSuite, config_1.handle->Next().IsNull());
config_1.handle->AddToEnd(config_2.handle.Retain());
NL_TEST_ASSERT(inSuite, config_1.handle->Next() == config_2.handle);
NL_TEST_ASSERT(inSuite, config_1.handle->ChainedBuffer() == config_2.handle.Get());
}
else
{
NL_TEST_ASSERT(inSuite, !config_1.handle->HasChainedBuffer());
}
NL_TEST_ASSERT(inSuite, !config_2.handle->HasChainedBuffer());
}
}
}
/**
* Test PacketBuffer::Last() function.
*/
void PacketBufferTest::CheckLast(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config_1 : test->configurations)
{
for (auto & config_2 : test->configurations)
{
for (auto & config_3 : test->configurations)
{
if (&config_1 == &config_2 || &config_1 == &config_3 || &config_2 == &config_3)
{
continue;
}
test->PrepareTestBuffer(&config_1);
test->PrepareTestBuffer(&config_2);
test->PrepareTestBuffer(&config_3);
NL_TEST_ASSERT(inSuite, config_1.handle->Last() == config_1.handle);
NL_TEST_ASSERT(inSuite, config_2.handle->Last() == config_2.handle);
NL_TEST_ASSERT(inSuite, config_3.handle->Last() == config_3.handle);
config_1.handle->AddToEnd(config_2.handle.Retain());
NL_TEST_ASSERT(inSuite, config_1.handle->Last() == config_2.handle);
NL_TEST_ASSERT(inSuite, config_2.handle->Last() == config_2.handle);
NL_TEST_ASSERT(inSuite, config_3.handle->Last() == config_3.handle);
config_1.handle->AddToEnd(config_3.handle.Retain());
NL_TEST_ASSERT(inSuite, config_1.handle->Last() == config_3.handle);
NL_TEST_ASSERT(inSuite, config_2.handle->Last() == config_3.handle);
NL_TEST_ASSERT(inSuite, config_3.handle->Last() == config_3.handle);
config_1.handle = nullptr;
config_2.handle = nullptr;
config_3.handle = nullptr;
}
}
}
}
/**
* Test PacketBuffer::Read() function.
*/
void PacketBufferTest::CheckRead(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
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 : test->configurations)
{
for (auto & config_2 : test->configurations)
{
if (&config_1 == &config_2)
{
continue;
}
test->PrepareTestBuffer(&config_1, kAllowHandleReuse);
test->PrepareTestBuffer(&config_2, kAllowHandleReuse);
const uint16_t length_1 = config_1.handle->MaxDataLength();
const uint16_t length_2 = config_2.handle->MaxDataLength();
const size_t length_sum = length_1 + length_2;
const uint16_t length_total = static_cast<uint16_t>(length_sum);
NL_TEST_ASSERT(inSuite, 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());
NL_TEST_ASSERT(inSuite, 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);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, result[0] == payloads[0]);
}
// Check a read that spans packet buffers.
CHIP_ERROR err = config_1.handle->Read(result, length_total);
NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
NL_TEST_ASSERT(inSuite, memcmp(payloads, result, length_total) == 0);
// Check a read that is too long fails.
err = config_1.handle->Read(result, length_total + 1);
NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_BUFFER_TOO_SMALL);
// Check that running off the end of a corrupt buffer chain is detected.
if (length_total < UINT16_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);
NL_TEST_ASSERT(inSuite, 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);
NL_TEST_ASSERT(inSuite, 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.
*/
void PacketBufferTest::CheckAddRef(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config : test->configurations)
{
test->PrepareTestBuffer(&config, kRecordHandle);
const auto refs = config.handle->ref;
config.handle->AddRef();
NL_TEST_ASSERT(inSuite, 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.
*/
void PacketBufferTest::CheckFree(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
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 : test->configurations)
{
for (auto & config_2 : test->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);
NL_TEST_ASSERT(inSuite, !config_1.handle.IsNull());
NL_TEST_ASSERT(inSuite, !config_2.handle.IsNull());
test->PrepareTestBuffer(&config_1, kAllowHandleReuse);
test->PrepareTestBuffer(&config_2, kAllowHandleReuse);
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 1);
NL_TEST_ASSERT(inSuite, 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.
NL_TEST_ASSERT(inSuite, config_1.handle->ref == initial_refs_1 - 1);
// Verify that chain is maintained.
NL_TEST_ASSERT(inSuite, config_1.handle->next == config_2.handle.Get());
// Verify that chained buffer ref count has not changed.
NL_TEST_ASSERT(inSuite, config_2.handle->ref == initial_refs_2);
}
else
{
if (initial_refs_2 > 1)
{
// Verify that chained buffer ref count is decremented.
NL_TEST_ASSERT(inSuite, 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.
*/
void PacketBufferTest::CheckFreeHead(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config_1 : test->configurations)
{
for (auto & config_2 : test->configurations)
{
if (&config_1 == &config_2)
{
continue;
}
// Test PacketBuffer::FreeHead
test->PrepareTestBuffer(&config_1, kAllowHandleReuse);
test->PrepareTestBuffer(&config_2, kAllowHandleReuse);
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 1);
NL_TEST_ASSERT(inSuite, config_2.handle->ref == 1);
PacketBufferHandle handle_1 = config_1.handle.Retain();
config_1.handle->AddToEnd(config_2.handle.Retain());
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 2);
NL_TEST_ASSERT(inSuite, config_2.handle->ref == 2); // config_2.handle and config_1.handle->next
PacketBuffer * const returned = PacketBuffer::FreeHead(std::move(config_1.handle).UnsafeRelease());
NL_TEST_ASSERT(inSuite, handle_1->ref == 1);
NL_TEST_ASSERT(inSuite, config_2.handle->ref == 2); // config_2.handle and returned
NL_TEST_ASSERT(inSuite, returned == config_2.handle.Get());
config_1.handle = nullptr;
NL_TEST_ASSERT(inSuite, config_2.handle->ref == 2);
config_2.handle = nullptr;
NL_TEST_ASSERT(inSuite, returned->ref == 1);
PacketBuffer::Free(returned);
// Test PacketBufferHandle::FreeHead
test->PrepareTestBuffer(&config_1, kAllowHandleReuse);
test->PrepareTestBuffer(&config_2, kAllowHandleReuse);
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 1);
NL_TEST_ASSERT(inSuite, config_2.handle->ref == 1);
handle_1 = config_1.handle.Retain();
config_1.handle->AddToEnd(config_2.handle.Retain());
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 2);
NL_TEST_ASSERT(inSuite, 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();
NL_TEST_ASSERT(inSuite, buffer_1->ref == 1);
NL_TEST_ASSERT(inSuite, config_1.handle == config_2.handle);
NL_TEST_ASSERT(inSuite, config_2.handle->ref == 2); // config_2.handle and config_1.handle
config_1.handle = nullptr;
config_2.handle = nullptr;
}
}
}
void PacketBufferTest::CheckHandleConstruct(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
PacketBufferHandle handle_1;
NL_TEST_ASSERT(inSuite, handle_1.IsNull());
PacketBufferHandle handle_2(nullptr);
NL_TEST_ASSERT(inSuite, handle_2.IsNull());
PacketBufferHandle handle_3(PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSize));
NL_TEST_ASSERT(inSuite, !handle_3.IsNull());
// Private constructor.
PacketBuffer * const buffer_3 = std::move(handle_3).UnsafeRelease();
PacketBufferHandle handle_4(buffer_3);
NL_TEST_ASSERT(inSuite, handle_4.Get() == buffer_3);
}
void PacketBufferTest::CheckHandleMove(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config_1 : test->configurations)
{
for (auto & config_2 : test->configurations)
{
if (&config_1 == &config_2)
{
continue;
}
test->PrepareTestBuffer(&config_1, kRecordHandle);
test->PrepareTestBuffer(&config_2, kRecordHandle);
const PacketBuffer * const buffer_1 = config_1.handle.Get();
const PacketBuffer * const buffer_2 = config_2.handle.Get();
NL_TEST_ASSERT(inSuite, buffer_1 != buffer_2);
NL_TEST_ASSERT(inSuite, buffer_1->ref == 2); // test.handles and config_1.handle
NL_TEST_ASSERT(inSuite, buffer_2->ref == 2); // test.handles and config_2.handle
config_1.handle = std::move(config_2.handle);
NL_TEST_ASSERT(inSuite, config_1.handle.Get() == buffer_2);
NL_TEST_ASSERT(inSuite, config_2.handle.Get() == nullptr);
NL_TEST_ASSERT(inSuite, buffer_1->ref == 1); // test.handles
NL_TEST_ASSERT(inSuite, buffer_2->ref == 2); // test.handles and config_1.handle
config_1.handle = nullptr;
}
// Verify and release handles.
NL_TEST_ASSERT(inSuite, test->ResetHandles());
}
}
void PacketBufferTest::CheckHandleRelease(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config_1 : test->configurations)
{
test->PrepareTestBuffer(&config_1);
PacketBuffer * const buffer_1 = config_1.handle.Get();
PacketBuffer * const taken_1 = std::move(config_1.handle).UnsafeRelease();
NL_TEST_ASSERT(inSuite, buffer_1 == taken_1);
NL_TEST_ASSERT(inSuite, config_1.handle.IsNull());
NL_TEST_ASSERT(inSuite, buffer_1->ref == 1);
PacketBuffer::Free(buffer_1);
}
}
void PacketBufferTest::CheckHandleFree(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config_1 : test->configurations)
{
test->PrepareTestBuffer(&config_1, kRecordHandle);
const PacketBuffer * const buffer_1 = config_1.handle.Get();
NL_TEST_ASSERT(inSuite, buffer_1->ref == 2); // test.handles and config_1.handle
config_1.handle = nullptr;
NL_TEST_ASSERT(inSuite, config_1.handle.IsNull());
NL_TEST_ASSERT(inSuite, config_1.handle.Get() == nullptr);
NL_TEST_ASSERT(inSuite, buffer_1->ref == 1); // test.handles only
}
}
void PacketBufferTest::CheckHandleRetain(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config_1 : test->configurations)
{
test->PrepareTestBuffer(&config_1, kRecordHandle);
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 2); // test.handles and config_1.handle
PacketBufferHandle handle_1 = config_1.handle.Retain();
NL_TEST_ASSERT(inSuite, config_1.handle == handle_1);
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 3); // test.handles and config_1.handle and handle_1
}
}
void PacketBufferTest::CheckHandleAdopt(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config_1 : test->configurations)
{
test->PrepareTestBuffer(&config_1, kRecordHandle);
PacketBuffer * buffer_1 = std::move(config_1.handle).UnsafeRelease();
NL_TEST_ASSERT(inSuite, config_1.handle.IsNull());
NL_TEST_ASSERT(inSuite, buffer_1->ref == 2); // test.handles and buffer_1
config_1.handle = PacketBufferHandle::Adopt(buffer_1);
NL_TEST_ASSERT(inSuite, config_1.handle.Get() == buffer_1);
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 2); // test.handles and config_1.handle
config_1.handle = nullptr;
NL_TEST_ASSERT(inSuite, config_1.handle.IsNull());
NL_TEST_ASSERT(inSuite, buffer_1->ref == 1); // test.handles only
}
}
void PacketBufferTest::CheckHandleHold(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config_1 : test->configurations)
{
test->PrepareTestBuffer(&config_1, kRecordHandle);
PacketBuffer * buffer_1 = std::move(config_1.handle).UnsafeRelease();
NL_TEST_ASSERT(inSuite, config_1.handle.IsNull());
NL_TEST_ASSERT(inSuite, buffer_1->ref == 2); // test.handles and buffer_1
config_1.handle = PacketBufferHandle::Hold(buffer_1);
NL_TEST_ASSERT(inSuite, config_1.handle.Get() == buffer_1);
NL_TEST_ASSERT(inSuite, config_1.handle->ref == 3); // test.handles and config_1.handle and buffer_1
config_1.handle = nullptr;
NL_TEST_ASSERT(inSuite, config_1.handle.IsNull());
NL_TEST_ASSERT(inSuite, buffer_1->ref == 2); // test.handles only and buffer_1
PacketBuffer::Free(buffer_1);
}
}
void PacketBufferTest::CheckHandleAdvance(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
for (auto & config_1 : test->configurations)
{
for (auto & config_2 : test->configurations)
{
for (auto & config_3 : test->configurations)
{
if (&config_1 == &config_2 || &config_1 == &config_3 || &config_2 == &config_3)
{
continue;
}
test->PrepareTestBuffer(&config_1);
test->PrepareTestBuffer(&config_2);
test->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());
NL_TEST_ASSERT(inSuite, config_1.handle->ChainedBuffer() == config_2.handle.Get());
NL_TEST_ASSERT(inSuite, config_2.handle->ChainedBuffer() == config_3.handle.Get());
NL_TEST_ASSERT(inSuite, config_3.handle->HasChainedBuffer() == false);
NL_TEST_ASSERT(inSuite, handle_1->ref == 2); // handle_1 and config_1.handle
NL_TEST_ASSERT(inSuite, handle_2->ref == 3); // handle_2 and config_2.handle and config_1.handle->next
NL_TEST_ASSERT(inSuite, handle_3->ref == 3); // handle_3 and config_3.handle and config_2.handle->next
config_1.handle.Advance();
NL_TEST_ASSERT(inSuite, config_1.handle == handle_2);
NL_TEST_ASSERT(inSuite, handle_1->ref == 1); // handle_1 only
NL_TEST_ASSERT(inSuite, handle_2->ref == 4); // handle_2, config_[12].handle, handle_1->next
NL_TEST_ASSERT(inSuite, handle_3->ref == 3); // handle_3, config_3.handle, config_2.handle->next
config_1.handle.Advance();
NL_TEST_ASSERT(inSuite, config_1.handle == handle_3);
NL_TEST_ASSERT(inSuite, handle_1->ref == 1); // handle_1 only
NL_TEST_ASSERT(inSuite, handle_2->ref == 3); // handle_2, config_2.handle, handle_1->next
NL_TEST_ASSERT(inSuite, handle_3->ref == 4); // handle_3, config_[13].handle, handle_2->next
config_1.handle = nullptr;
config_2.handle = nullptr;
config_3.handle = nullptr;
}
}
}
}
void PacketBufferTest::CheckHandleRightSize(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
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);
NL_TEST_ASSERT(inSuite, handle->ref == 1);
// RightSize should do nothing if there is another reference to the buffer.
{
PacketBufferHandle anotherHandle = handle.Retain();
handle.RightSize();
NL_TEST_ASSERT(inSuite, handle.mBuffer == buffer);
}
#if CHIP_SYSTEM_PACKETBUFFER_HAS_RIGHTSIZE
handle.RightSize();
NL_TEST_ASSERT(inSuite, handle.mBuffer != buffer);
NL_TEST_ASSERT(inSuite, handle->DataLength() == sizeof kPayload);
NL_TEST_ASSERT(inSuite, memcmp(handle->Start(), kPayload, sizeof kPayload) == 0);
#else // CHIP_SYSTEM_PACKETBUFFER_HAS_RIGHTSIZE
// For this configuration, RightSize() does nothing.
handle.RightSize();
NL_TEST_ASSERT(inSuite, handle.mBuffer == buffer);
#endif // CHIP_SYSTEM_PACKETBUFFER_HAS_RIGHTSIZE
}
void PacketBufferTest::CheckHandleCloneData(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
uint8_t lPayload[2 * PacketBuffer::kMaxSizeWithoutReserve];
for (uint8_t & payload : lPayload)
{
payload = static_cast<uint8_t>(random());
}
for (auto & config_1 : test->configurations)
{
for (auto & config_2 : test->configurations)
{
if (&config_1 == &config_2)
{
continue;
}
test->PrepareTestBuffer(&config_1);
test->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();
NL_TEST_ASSERT(inSuite, !clone_1.IsNull());
NL_TEST_ASSERT(inSuite, clone_1->DataLength() == config_1.handle->DataLength());
NL_TEST_ASSERT(inSuite, 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());
NL_TEST_ASSERT(inSuite, memcmp(clone_1->Start(), payload_1, clone_1->DataLength()) != 0);
NL_TEST_ASSERT(inSuite, memcmp(config_1.handle->Start(), payload_1, config_1.handle->DataLength()) == 0);
}
// Clone buffer chain.
config_1.handle->AddToEnd(config_2.handle.Retain());
NL_TEST_ASSERT(inSuite, config_1.handle->HasChainedBuffer());
clone_1 = config_1.handle.CloneData();
PacketBufferHandle clone_1_next = clone_1->Next();
NL_TEST_ASSERT(inSuite, !clone_1.IsNull());
NL_TEST_ASSERT(inSuite, clone_1->HasChainedBuffer());
NL_TEST_ASSERT(inSuite, clone_1->DataLength() == config_1.handle->DataLength());
NL_TEST_ASSERT(inSuite, clone_1->TotalLength() == config_1.handle->TotalLength());
NL_TEST_ASSERT(inSuite, clone_1_next->DataLength() == config_2.handle->DataLength());
NL_TEST_ASSERT(inSuite, memcmp(clone_1->Start(), payload_1, clone_1->DataLength()) == 0);
NL_TEST_ASSERT(inSuite, memcmp(clone_1_next->Start(), payload_2, clone_1_next->DataLength()) == 0);
if (clone_1->DataLength())
{
ScrambleData(clone_1->Start(), clone_1->DataLength());
NL_TEST_ASSERT(inSuite, memcmp(clone_1->Start(), payload_1, clone_1->DataLength()) != 0);
NL_TEST_ASSERT(inSuite, 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());
NL_TEST_ASSERT(inSuite, memcmp(clone_1_next->Start(), payload_2, clone_1_next->DataLength()) != 0);
NL_TEST_ASSERT(inSuite, 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(PacketBuffer::kStructureSize + kOversizeDataSize));
NL_TEST_ASSERT(inSuite, p != nullptr);
p->next = nullptr;
p->payload = reinterpret_cast<uint8_t *>(p) + PacketBuffer::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);
NL_TEST_ASSERT(inSuite, handle->DataLength() == PacketBuffer::kMaxSizeWithoutReserve);
PacketBufferHandle clone = handle.CloneData();
NL_TEST_ASSERT(inSuite, !clone.IsNull());
NL_TEST_ASSERT(inSuite, clone->DataLength() == PacketBuffer::kMaxSizeWithoutReserve);
NL_TEST_ASSERT(inSuite, 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);
NL_TEST_ASSERT(inSuite, handle->DataLength() == kOversizeDataSize);
clone = handle.CloneData();
NL_TEST_ASSERT(inSuite, 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
}
void PacketBufferTest::CheckPacketBufferWriter(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * const theContext = static_cast<struct TestContext *>(inContext);
PacketBufferTest * const test = theContext->test;
NL_TEST_ASSERT(inSuite, test->mContext == theContext);
static const char kPayload[] = "Hello, world!";
PacketBufferWriter yay(PacketBufferHandle::New(sizeof(kPayload)));
PacketBufferWriter nay(PacketBufferHandle::New(sizeof(kPayload)), sizeof(kPayload) - 2);
NL_TEST_ASSERT(inSuite, !yay.IsNull());
NL_TEST_ASSERT(inSuite, !nay.IsNull());
yay.Put(kPayload);
yay.Put('\0');
nay.Put(kPayload);
nay.Put('\0');
NL_TEST_ASSERT(inSuite, yay.Fit());
NL_TEST_ASSERT(inSuite, !nay.Fit());
PacketBufferHandle yayBuffer = yay.Finalize();
PacketBufferHandle nayBuffer = nay.Finalize();
NL_TEST_ASSERT(inSuite, yay.IsNull());
NL_TEST_ASSERT(inSuite, nay.IsNull());
NL_TEST_ASSERT(inSuite, !yayBuffer.IsNull());
NL_TEST_ASSERT(inSuite, nayBuffer.IsNull());
NL_TEST_ASSERT(inSuite, memcmp(yayBuffer->Start(), kPayload, sizeof kPayload) == 0);
}
/**
* Test Suite. It lists all the test functions.
*/
// clang-format off
const nlTest sTests[] =
{
NL_TEST_DEF("PacketBuffer::New", PacketBufferTest::CheckNew),
NL_TEST_DEF("PacketBuffer::Start", PacketBufferTest::CheckStart),
NL_TEST_DEF("PacketBuffer::SetStart", PacketBufferTest::CheckSetStart),
NL_TEST_DEF("PacketBuffer::DataLength", PacketBufferTest::CheckDataLength),
NL_TEST_DEF("PacketBuffer::SetDataLength", PacketBufferTest::CheckSetDataLength),
NL_TEST_DEF("PacketBuffer::TotalLength", PacketBufferTest::CheckTotalLength),
NL_TEST_DEF("PacketBuffer::MaxDataLength", PacketBufferTest::CheckMaxDataLength),
NL_TEST_DEF("PacketBuffer::AvailableDataLength", PacketBufferTest::CheckAvailableDataLength),
NL_TEST_DEF("PacketBuffer::HasChainedBuffer", PacketBufferTest::CheckHasChainedBuffer),
NL_TEST_DEF("PacketBuffer::ReservedSize", PacketBufferTest::CheckReservedSize),
NL_TEST_DEF("PacketBuffer::AddToEnd", PacketBufferTest::CheckAddToEnd),
NL_TEST_DEF("PacketBuffer::PopHead", PacketBufferTest::CheckPopHead),
NL_TEST_DEF("PacketBuffer::CompactHead", PacketBufferTest::CheckCompactHead),
NL_TEST_DEF("PacketBuffer::ConsumeHead", PacketBufferTest::CheckConsumeHead),
NL_TEST_DEF("PacketBuffer::Consume", PacketBufferTest::CheckConsume),
NL_TEST_DEF("PacketBuffer::EnsureReservedSize", PacketBufferTest::CheckEnsureReservedSize),
NL_TEST_DEF("PacketBuffer::AlignPayload", PacketBufferTest::CheckAlignPayload),
NL_TEST_DEF("PacketBuffer::Next", PacketBufferTest::CheckNext),
NL_TEST_DEF("PacketBuffer::Last", PacketBufferTest::CheckLast),
NL_TEST_DEF("PacketBuffer::Read", PacketBufferTest::CheckRead),
NL_TEST_DEF("PacketBuffer::AddRef", PacketBufferTest::CheckAddRef),
NL_TEST_DEF("PacketBuffer::Free", PacketBufferTest::CheckFree),
NL_TEST_DEF("PacketBuffer::FreeHead", PacketBufferTest::CheckFreeHead),
NL_TEST_DEF("PacketBuffer::HandleConstruct", PacketBufferTest::CheckHandleConstruct),
NL_TEST_DEF("PacketBuffer::HandleMove", PacketBufferTest::CheckHandleMove),
NL_TEST_DEF("PacketBuffer::HandleRelease", PacketBufferTest::CheckHandleRelease),
NL_TEST_DEF("PacketBuffer::HandleFree", PacketBufferTest::CheckHandleFree),
NL_TEST_DEF("PacketBuffer::HandleRetain", PacketBufferTest::CheckHandleRetain),
NL_TEST_DEF("PacketBuffer::HandleAdopt", PacketBufferTest::CheckHandleAdopt),
NL_TEST_DEF("PacketBuffer::HandleHold", PacketBufferTest::CheckHandleHold),
NL_TEST_DEF("PacketBuffer::HandleAdvance", PacketBufferTest::CheckHandleAdvance),
NL_TEST_DEF("PacketBuffer::HandleRightSize", PacketBufferTest::CheckHandleRightSize),
NL_TEST_DEF("PacketBuffer::HandleCloneData", PacketBufferTest::CheckHandleCloneData),
NL_TEST_DEF("PacketBuffer::PacketBufferWriter", PacketBufferTest::CheckPacketBufferWriter),
NL_TEST_SENTINEL()
};
// clang-format on
int TestSystemPacketBuffer()
{
// clang-format off
nlTestSuite theSuite = {
.name ="chip-system-packetbuffer",
.tests = &sTests[0],
.setup = PacketBufferTest::TestSetup,
.tear_down = PacketBufferTest::TestTeardown,
// .initialize = PacketBufferTest::TestInitialize,
.terminate = PacketBufferTest::TestTerminate
};
// clang-format on
// Run test suite against one context.
nlTestRunner(&theSuite, &sContext);
return (nlTestRunnerStats(&theSuite));
}
CHIP_REGISTER_TEST_SUITE(TestSystemPacketBuffer)