blob: a7c0ce2d5b3a14b8ee928f3a8382453a17cdafad [file] [log] [blame]
/*
*
* Copyright (c) 2020 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 "TestSystemLayer.h"
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <support/CodeUtils.h>
#include <support/TestUtils.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 >= 1)
#define PBUF_TYPE(pbuf) (pbuf)->type_internal
#else
#define PBUF_TYPE(pbuf) (pbuf)->type
#endif // (LWIP_VERSION_MAJOR >= 2 && LWIP_VERSION_MINOR >= 1)
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
using ::chip::System::PacketBuffer;
#if !CHIP_SYSTEM_CONFIG_USE_LWIP
using ::chip::System::pbuf;
#endif
namespace {
// Test input vector format.
struct TestContext
{
uint16_t init_len;
uint16_t reserved_size;
uint8_t * start_buffer;
uint8_t * end_buffer;
uint8_t * payload_ptr;
struct pbuf * buf;
};
// Test input data.
// clang-format off
struct TestContext sContext[] =
{
{ 0, 0, nullptr, nullptr, nullptr, nullptr },
{ 0, 10, nullptr, nullptr, nullptr, nullptr },
{ 0, 128, nullptr, nullptr, nullptr, nullptr },
{ 0, 1536, nullptr, nullptr, nullptr, nullptr },
{ 0, CHIP_SYSTEM_PACKETBUFFER_SIZE, nullptr, nullptr, nullptr, nullptr }
};
// clang-format on
const uint16_t sLengths[] = { 0, 1, 10, 128, CHIP_SYSTEM_PACKETBUFFER_SIZE, UINT16_MAX };
// Number of test context examples.
const size_t kTestElements = sizeof(sContext) / sizeof(struct TestContext);
const size_t kTestLengths = sizeof(sLengths) / sizeof(uint16_t);
// 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)))
/**
* Free allocated test buffer memory.
*/
void BufferFree(struct TestContext * theContext)
{
if (theContext->buf != nullptr)
{
PacketBuffer::Free(OF_LWIP_PBUF(theContext->buf));
theContext->buf = nullptr;
}
}
/**
* Allocate memory for a test buffer and configure according to test context.
*/
void BufferAlloc(struct TestContext * theContext)
{
const size_t lInitialSize = CHIP_SYSTEM_PACKETBUFFER_HEADER_SIZE + theContext->reserved_size;
const size_t lAllocSize = CHIP_SYSTEM_PACKETBUFFER_SIZE;
#if CHIP_SYSTEM_CONFIG_USE_LWIP
u8_t lType, lFlags;
#if LWIP_PBUF_FROM_CUSTOM_POOLS
u16_t lPool;
#endif // LWIP_PBUF_FROM_CUSTOM_POOLS
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
if (theContext->buf == nullptr)
{
theContext->buf = TO_LWIP_PBUF(PacketBuffer::New(0));
}
if (theContext->buf == nullptr)
{
fprintf(stderr, "Failed to allocate %zuB memory: %s\n", lAllocSize, strerror(errno));
exit(EXIT_FAILURE);
}
#if CHIP_SYSTEM_CONFIG_USE_LWIP
lType = PBUF_TYPE(theContext->buf);
lFlags = theContext->buf->flags;
#if LWIP_PBUF_FROM_CUSTOM_POOLS
lPool = theContext->buf->pool;
#endif // LWIP_PBUF_FROM_CUSTOM_POOLS
memset(theContext->buf, 0, lAllocSize);
PBUF_TYPE(theContext->buf) = lType;
theContext->buf->flags = lFlags;
#if LWIP_PBUF_FROM_CUSTOM_POOLS
theContext->buf->pool = lPool;
#endif // LWIP_PBUF_FROM_CUSTOM_POOLS
#else // !CHIP_SYSTEM_CONFIG_USE_LWIP
memset(theContext->buf, 0, lAllocSize);
#if CHIP_SYSTEM_CONFIG_PACKETBUFFER_MAXALLOC == 0
theContext->buf->alloc_size = lAllocSize;
#endif // CHIP_SYSTEM_CONFIG_PACKETBUFFER_MAXALLOC == 0
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
theContext->start_buffer = reinterpret_cast<uint8_t *>(theContext->buf);
theContext->end_buffer = reinterpret_cast<uint8_t *>(theContext->buf) + lAllocSize;
if (lInitialSize > lAllocSize)
{
theContext->payload_ptr = theContext->end_buffer;
}
else
{
theContext->payload_ptr = theContext->start_buffer + lInitialSize;
}
}
/**
* Setup buffer layout as it is used by PacketBuffer class.
*/
PacketBuffer * PrepareTestBuffer(struct TestContext * theContext)
{
BufferAlloc(theContext);
theContext->buf->next = nullptr;
theContext->buf->payload = theContext->payload_ptr;
theContext->buf->ref = 1;
theContext->buf->len = theContext->init_len;
theContext->buf->tot_len = theContext->init_len;
return reinterpret_cast<PacketBuffer *>(theContext->buf);
}
// Test functions invoked from the suite.
/**
* Test PacketBuffer::Start() function.
*/
void CheckStart(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
PacketBuffer * buffer = PrepareTestBuffer(theContext);
NL_TEST_ASSERT(inSuite, buffer->Start() == theContext->payload_ptr);
theContext++;
}
}
/**
* 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 CheckSetStart(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theContext = static_cast<struct TestContext *>(inContext);
static const ptrdiff_t sSizePacketBuffer = CHIP_SYSTEM_PACKETBUFFER_SIZE;
for (size_t ith = 0; ith < kTestElements; ith++)
{
// clang-format off
static const ptrdiff_t start_offset[] =
{
-sSizePacketBuffer,
-128,
-1,
0,
1,
128,
sSizePacketBuffer
};
// clang-format on
for (ptrdiff_t offset : start_offset)
{
PacketBuffer * buffer = PrepareTestBuffer(theContext);
uint8_t * test_start = theContext->payload_ptr + offset;
uint8_t * verify_start = test_start;
buffer->SetStart(test_start);
if (verify_start < theContext->start_buffer + CHIP_SYSTEM_PACKETBUFFER_HEADER_SIZE)
{
// Set start before valid payload beginning.
verify_start = theContext->start_buffer + CHIP_SYSTEM_PACKETBUFFER_HEADER_SIZE;
}
if (verify_start > theContext->end_buffer)
{
// Set start after valid payload beginning.
verify_start = theContext->end_buffer;
}
NL_TEST_ASSERT(inSuite, theContext->buf->payload == verify_start);
if ((verify_start - theContext->payload_ptr) > theContext->init_len)
{
// Set start to the beginning of payload, right after buffer's header.
NL_TEST_ASSERT(inSuite, theContext->buf->len == 0);
}
else
{
// Set start to somewhere between the end of the buffer's
// header and the end of payload.
NL_TEST_ASSERT(inSuite, theContext->buf->len == (theContext->init_len - (verify_start - theContext->payload_ptr)));
}
}
theContext++;
}
}
/**
* Test PacketBuffer::DataLength() function.
*/
void CheckDataLength(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
PacketBuffer * buffer = PrepareTestBuffer(theContext);
NL_TEST_ASSERT(inSuite, buffer->DataLength() == theContext->buf->len);
theContext++;
}
}
/**
* 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 lenghts were correctly adjusted.
*/
void CheckSetDataLength(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theFirstContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
struct TestContext * theSecondContext = static_cast<struct TestContext *>(inContext);
for (size_t jth = 0; jth < kTestElements; jth++)
{
for (uint16_t length : sLengths)
{
PacketBuffer * buffer_1 = PrepareTestBuffer(theFirstContext);
PacketBuffer * buffer_2 = PrepareTestBuffer(theSecondContext);
if (theFirstContext == theSecondContext)
{
// headOfChain (the second arg) is NULL
buffer_2->SetDataLength(length, nullptr);
if (length > (theSecondContext->end_buffer - theSecondContext->payload_ptr))
{
NL_TEST_ASSERT(
inSuite, theSecondContext->buf->len == (theSecondContext->end_buffer - theSecondContext->payload_ptr));
NL_TEST_ASSERT(inSuite,
theSecondContext->buf->tot_len ==
(theSecondContext->end_buffer - theSecondContext->payload_ptr));
NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == nullptr);
}
else
{
NL_TEST_ASSERT(inSuite, theSecondContext->buf->len == length);
NL_TEST_ASSERT(inSuite, theSecondContext->buf->tot_len == length);
NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == nullptr);
}
}
else
{
// headOfChain (the second arg) is buffer_1
buffer_2->SetDataLength(length, buffer_1);
if (length > (theSecondContext->end_buffer - theSecondContext->payload_ptr))
{
NL_TEST_ASSERT(
inSuite, theSecondContext->buf->len == (theSecondContext->end_buffer - theSecondContext->payload_ptr));
NL_TEST_ASSERT(inSuite,
theSecondContext->buf->tot_len ==
(theSecondContext->end_buffer - theSecondContext->payload_ptr));
NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == nullptr);
NL_TEST_ASSERT(inSuite,
theFirstContext->buf->tot_len ==
(theFirstContext->init_len +
static_cast<int32_t>(theSecondContext->end_buffer - theSecondContext->payload_ptr) -
static_cast<int32_t>(theSecondContext->init_len)));
}
else
{
NL_TEST_ASSERT(inSuite, theSecondContext->buf->len == length);
NL_TEST_ASSERT(inSuite, theSecondContext->buf->tot_len == length);
NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == nullptr);
NL_TEST_ASSERT(inSuite,
theFirstContext->buf->tot_len ==
(theFirstContext->init_len + static_cast<int32_t>(length) -
static_cast<int32_t>(theSecondContext->init_len)));
}
}
}
theSecondContext++;
}
theFirstContext++;
}
}
/**
* Test PacketBuffer::TotalLength() function.
*/
void CheckTotalLength(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
PacketBuffer * buffer = PrepareTestBuffer(theContext);
NL_TEST_ASSERT(inSuite, buffer->TotalLength() == theContext->init_len);
theContext++;
}
}
/**
* Test PacketBuffer::MaxDataLength() function.
*/
void CheckMaxDataLength(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
PacketBuffer * buffer = PrepareTestBuffer(theContext);
NL_TEST_ASSERT(inSuite, buffer->MaxDataLength() == (theContext->end_buffer - theContext->payload_ptr));
theContext++;
}
}
/**
* Test PacketBuffer::AvailableDataLength() function.
*/
void CheckAvailableDataLength(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
PacketBuffer * buffer = PrepareTestBuffer(theContext);
NL_TEST_ASSERT(
inSuite, buffer->AvailableDataLength() == ((theContext->end_buffer - theContext->payload_ptr) - theContext->init_len));
theContext++;
}
}
/**
* Test PacketBuffer::ReservedSize() function.
*/
void CheckReservedSize(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
PacketBuffer & lBuffer = *PrepareTestBuffer(theContext);
const size_t kAllocSize = lBuffer.AllocSize();
if (theContext->reserved_size > kAllocSize)
{
NL_TEST_ASSERT(inSuite, lBuffer.ReservedSize() == kAllocSize);
}
else
{
NL_TEST_ASSERT(inSuite, lBuffer.ReservedSize() == theContext->reserved_size);
}
theContext++;
}
}
/**
* 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 CheckAddToEnd(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theFirstContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
struct TestContext * theSecondContext = static_cast<struct TestContext *>(inContext);
for (size_t jth = 0; jth < kTestElements; jth++)
{
struct TestContext * theThirdContext = static_cast<struct TestContext *>(inContext);
for (size_t kth = 0; kth < kTestElements; kth++)
{
PacketBuffer * buffer_1 = nullptr;
PacketBuffer * buffer_2 = nullptr;
PacketBuffer * buffer_3 = nullptr;
if (theFirstContext == theSecondContext || theFirstContext == theThirdContext ||
theSecondContext == theThirdContext)
{
theThirdContext++;
continue;
}
buffer_1 = PrepareTestBuffer(theFirstContext);
buffer_2 = PrepareTestBuffer(theSecondContext);
buffer_3 = PrepareTestBuffer(theThirdContext);
buffer_1->AddToEnd(buffer_2);
NL_TEST_ASSERT(inSuite, theFirstContext->buf->tot_len == (theFirstContext->init_len + theSecondContext->init_len));
NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == theSecondContext->buf);
NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == nullptr);
NL_TEST_ASSERT(inSuite, theThirdContext->buf->next == nullptr);
buffer_1->AddToEnd(buffer_3);
NL_TEST_ASSERT(inSuite,
theFirstContext->buf->tot_len ==
(theFirstContext->init_len + theSecondContext->init_len + theThirdContext->init_len));
NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == theSecondContext->buf);
NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == theThirdContext->buf);
NL_TEST_ASSERT(inSuite, theThirdContext->buf->next == nullptr);
theThirdContext++;
}
theSecondContext++;
}
theFirstContext++;
}
}
/**
* Test PacketBuffer::DetachTail() 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 DetachTail()
* on the first buffer to unlink the second buffer. After the call,
* verify correct internal state of the first buffer.
*/
void CheckDetachTail(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theFirstContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
struct TestContext * theSecondContext = static_cast<struct TestContext *>(inContext);
for (size_t jth = 0; jth < kTestElements; jth++)
{
PacketBuffer * buffer_1 = PrepareTestBuffer(theFirstContext);
PacketBuffer * buffer_2 = PrepareTestBuffer(theSecondContext);
PacketBuffer * returned = nullptr;
if (theFirstContext != theSecondContext)
{
theFirstContext->buf->next = theSecondContext->buf;
theFirstContext->buf->tot_len = static_cast<uint16_t>(theFirstContext->buf->tot_len + theSecondContext->init_len);
}
returned = buffer_1->DetachTail();
NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == nullptr);
NL_TEST_ASSERT(inSuite, theFirstContext->buf->tot_len == theFirstContext->init_len);
if (theFirstContext != theSecondContext)
{
NL_TEST_ASSERT(inSuite, returned == buffer_2);
}
theSecondContext++;
}
theFirstContext++;
}
}
/**
* 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 CheckCompactHead(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theFirstContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
struct TestContext * theSecondContext = static_cast<struct TestContext *>(inContext);
for (size_t jth = 0; jth < kTestElements; jth++)
{
// start with various initial length for the first buffer
for (uint16_t firstLength : sLengths)
{
// start with various initial length for the second buffer
for (uint16_t secondLength : sLengths)
{
PacketBuffer * buffer_1 = PrepareTestBuffer(theFirstContext);
PacketBuffer * buffer_2 = PrepareTestBuffer(theSecondContext);
uint16_t len1 = 0;
uint16_t len2 = 0;
buffer_1->SetDataLength(firstLength, buffer_1);
len1 = buffer_1->DataLength();
if (theFirstContext != theSecondContext)
{
theFirstContext->buf->next = theSecondContext->buf;
// Add various lengths to the second buffer
buffer_2->SetDataLength(secondLength, buffer_1);
len2 = buffer_2->DataLength();
}
buffer_1->CompactHead();
NL_TEST_ASSERT(inSuite,
theFirstContext->buf->payload ==
(theFirstContext->start_buffer + CHIP_SYSTEM_PACKETBUFFER_HEADER_SIZE));
/* verify length of the first buffer */
if (theFirstContext == theSecondContext)
{
NL_TEST_ASSERT(inSuite, theFirstContext->buf->tot_len == len1);
}
else if (theFirstContext->buf->tot_len > buffer_1->MaxDataLength())
{
NL_TEST_ASSERT(inSuite, theFirstContext->buf->len == buffer_1->MaxDataLength());
NL_TEST_ASSERT(inSuite,
theSecondContext->buf->len == theFirstContext->buf->tot_len - buffer_1->MaxDataLength());
}
else
{
NL_TEST_ASSERT(inSuite, theFirstContext->buf->len == theFirstContext->buf->tot_len);
if (len1 >= buffer_1->MaxDataLength() && len2 == 0)
{
/* make sure the second buffer is not freed */
NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == theSecondContext->buf);
}
else
{
/* make sure the second buffer is freed */
NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == nullptr);
theSecondContext->buf = nullptr;
}
}
}
}
theSecondContext++;
}
theFirstContext++;
}
}
/**
* 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 CheckConsumeHead(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
for (uint16_t length : sLengths)
{
PacketBuffer * buffer = PrepareTestBuffer(theContext);
buffer->ConsumeHead(length);
if (length > theContext->init_len)
{
NL_TEST_ASSERT(inSuite, theContext->buf->payload == (theContext->payload_ptr + theContext->init_len));
NL_TEST_ASSERT(inSuite, theContext->buf->len == 0);
NL_TEST_ASSERT(inSuite, theContext->buf->tot_len == 0);
}
else
{
NL_TEST_ASSERT(inSuite, theContext->buf->payload == (theContext->payload_ptr + length));
NL_TEST_ASSERT(inSuite, theContext->buf->len == (theContext->buf->len - length));
NL_TEST_ASSERT(inSuite, theContext->buf->tot_len == (theContext->buf->tot_len - length));
}
if (theContext->buf->ref == 0)
{
theContext->buf = nullptr;
}
}
theContext++;
}
}
/**
* 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 CheckConsume(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theFirstContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
struct TestContext * theSecondContext = static_cast<struct TestContext *>(inContext);
for (size_t jth = 0; jth < kTestElements; jth++)
{
// consume various amounts of memory
for (uint16_t consumeLength : sLengths)
{
// start with various initial length for the first buffer
for (uint16_t firstLength : sLengths)
{
// start with various initial length for the second buffer
for (uint16_t secondLength : sLengths)
{
PacketBuffer * buffer_1;
PacketBuffer * buffer_2;
PacketBuffer * returned;
uint16_t buf_1_len = 0;
uint16_t buf_2_len = 0;
if (theFirstContext == theSecondContext)
{
continue;
}
buffer_1 = PrepareTestBuffer(theFirstContext);
buffer_2 = PrepareTestBuffer(theSecondContext);
theFirstContext->buf->next = theSecondContext->buf;
// Add various lengths to buffers
buffer_1->SetDataLength(firstLength, buffer_1);
buffer_2->SetDataLength(secondLength, buffer_1);
buf_1_len = theFirstContext->buf->len;
buf_2_len = theSecondContext->buf->len;
returned = buffer_1->Consume(consumeLength);
if (consumeLength == 0)
{
NL_TEST_ASSERT(inSuite, returned == buffer_1);
continue;
}
if (consumeLength < buf_1_len)
{
NL_TEST_ASSERT(inSuite, returned == buffer_1);
}
else if ((consumeLength >= buf_1_len) &&
(consumeLength < buf_1_len + buf_2_len ||
(consumeLength == buf_1_len + buf_2_len && buf_2_len == 0)))
{
NL_TEST_ASSERT(inSuite, returned == buffer_2);
theFirstContext->buf = nullptr;
}
else if (consumeLength >= (buf_1_len + buf_2_len))
{
NL_TEST_ASSERT(inSuite, returned == nullptr);
theFirstContext->buf = nullptr;
theSecondContext->buf = nullptr;
}
}
}
}
theSecondContext++;
}
theFirstContext++;
}
}
/**
* 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 CheckEnsureReservedSize(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
for (uint16_t length : sLengths)
{
PacketBuffer & lBuffer = *PrepareTestBuffer(theContext);
const uint16_t kAllocSize = lBuffer.AllocSize();
uint16_t reserved_size = theContext->reserved_size;
if (CHIP_SYSTEM_PACKETBUFFER_HEADER_SIZE + theContext->reserved_size > kAllocSize)
{
reserved_size = static_cast<uint16_t>(kAllocSize - CHIP_SYSTEM_PACKETBUFFER_HEADER_SIZE);
}
if (length <= reserved_size)
{
NL_TEST_ASSERT(inSuite, lBuffer.EnsureReservedSize(length) == true);
continue;
}
if ((length + theContext->init_len) > (kAllocSize - CHIP_SYSTEM_PACKETBUFFER_HEADER_SIZE))
{
NL_TEST_ASSERT(inSuite, lBuffer.EnsureReservedSize(length) == false);
continue;
}
NL_TEST_ASSERT(inSuite, lBuffer.EnsureReservedSize(length) == true);
NL_TEST_ASSERT(inSuite, theContext->buf->payload == (theContext->payload_ptr + length - reserved_size));
}
theContext++;
}
}
/**
* 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 CheckAlignPayload(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
for (size_t n = 0; n < kTestLengths - 1; n++)
{
PacketBuffer & lBuffer = *PrepareTestBuffer(theContext);
const uint16_t kAllocSize = lBuffer.AllocSize();
if (sLengths[n] == 0)
{
NL_TEST_ASSERT(inSuite, lBuffer.AlignPayload(sLengths[n]) == false);
continue;
}
uint16_t reserved_size = theContext->reserved_size;
if (theContext->reserved_size > kAllocSize)
{
reserved_size = kAllocSize;
}
uint16_t payload_offset = static_cast<uint16_t>(reinterpret_cast<uintptr_t>(lBuffer.Start()) % sLengths[n]);
uint16_t payload_shift = 0;
if (payload_offset > 0)
payload_shift = static_cast<uint16_t>(sLengths[n] - payload_offset);
if (payload_shift <= kAllocSize - reserved_size)
{
NL_TEST_ASSERT(inSuite, lBuffer.AlignPayload(sLengths[n]) == true);
NL_TEST_ASSERT(inSuite, ((unsigned long) lBuffer.Start() % sLengths[n]) == 0);
}
else
{
NL_TEST_ASSERT(inSuite, lBuffer.AlignPayload(sLengths[n]) == false);
}
}
theContext++;
}
}
/**
* Test PacketBuffer::Next() function.
*/
void CheckNext(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theFirstContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
struct TestContext * theSecondContext = static_cast<struct TestContext *>(inContext);
for (size_t jth = 0; jth < kTestElements; jth++)
{
PacketBuffer * buffer_1 = PrepareTestBuffer(theFirstContext);
PacketBuffer * buffer_2 = PrepareTestBuffer(theSecondContext);
if (theFirstContext != theSecondContext)
{
theFirstContext->buf->next = theSecondContext->buf;
NL_TEST_ASSERT(inSuite, buffer_1->Next() == buffer_2);
}
else
{
NL_TEST_ASSERT(inSuite, buffer_1->Next() == nullptr);
}
NL_TEST_ASSERT(inSuite, buffer_2->Next() == nullptr);
theSecondContext++;
}
theFirstContext++;
}
}
/**
* Test PacketBuffer::AddRef() function.
*/
void CheckAddRef(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
PacketBuffer * buffer = PrepareTestBuffer(theContext);
buffer->AddRef();
NL_TEST_ASSERT(inSuite, theContext->buf->ref == 2);
theContext++;
}
}
/**
* Test PacketBuffer::NewWithAvailableSize() and PacketBuffer::Free() functions.
*
* Description: For every buffer-configuration from inContext, create a
* buffer's instance using NewWithAvailableSize() method. Then, verify that
* when the size of the reserved space passed to NewWithAvailableSize() is
* greater than #CHIP_SYSTEM_CONFIG_PACKETBUFFER_CAPACITY_MAX, the method
* returns NULL. Otherwise, check for correctness of initializing
* the new buffer's internal state. Finally, free the buffer.
*/
void CheckNewWithAvailableSizeAndFree(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theContext = static_cast<struct TestContext *>(inContext);
PacketBuffer * buffer;
for (size_t ith = 0; ith < kTestElements; ith++)
{
struct pbuf * pb = nullptr;
buffer = PacketBuffer::NewWithAvailableSize(theContext->reserved_size, 0);
if (theContext->reserved_size > CHIP_SYSTEM_CONFIG_PACKETBUFFER_CAPACITY_MAX)
{
NL_TEST_ASSERT(inSuite, buffer == nullptr);
theContext++;
continue;
}
NL_TEST_ASSERT(inSuite, theContext->reserved_size <= buffer->AllocSize());
NL_TEST_ASSERT(inSuite, buffer != nullptr);
if (buffer != nullptr)
{
pb = TO_LWIP_PBUF(buffer);
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);
}
PacketBuffer::Free(buffer);
theContext++;
}
// Use the rest of the buffer space
do
{
buffer = PacketBuffer::NewWithAvailableSize(0, 0);
} while (buffer != nullptr);
}
/**
* 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 CheckFree(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theFirstContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
struct TestContext * theSecondContext = static_cast<struct TestContext *>(inContext);
for (size_t jth = 0; jth < kTestElements; jth++)
{
const uint8_t init_ref_count[] = { 1, 2, 3 };
const int refs = sizeof(init_ref_count) / sizeof(uint16_t);
// start with various buffer ref counts
for (size_t r = 0; r < refs; r++)
{
PacketBuffer * buffer_1;
if (theFirstContext == theSecondContext)
{
continue;
}
buffer_1 = PrepareTestBuffer(theFirstContext);
(void) PrepareTestBuffer(theSecondContext);
theFirstContext->buf->next = theSecondContext->buf;
// Add various buffer ref counts
theFirstContext->buf->ref = init_ref_count[r];
theSecondContext->buf->ref = init_ref_count[(r + 1) % refs];
PacketBuffer::Free(buffer_1);
NL_TEST_ASSERT(inSuite, theFirstContext->buf->ref == (init_ref_count[r] - 1));
if (init_ref_count[r] == 1)
{
NL_TEST_ASSERT(inSuite, theSecondContext->buf->ref == (init_ref_count[(r + 1) % refs] - 1));
}
else
{
NL_TEST_ASSERT(inSuite, theSecondContext->buf->ref == (init_ref_count[(r + 1) % refs]));
}
if (init_ref_count[r] > 1)
{
NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == theSecondContext->buf);
}
if (theFirstContext->buf->ref == 0)
{
theFirstContext->buf = nullptr;
}
if (theSecondContext->buf->ref == 0)
{
theSecondContext->buf = nullptr;
}
}
theSecondContext++;
}
theFirstContext++;
}
}
/**
* 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 CheckFreeHead(nlTestSuite * inSuite, void * inContext)
{
struct TestContext * theFirstContext = static_cast<struct TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
struct TestContext * theSecondContext = static_cast<struct TestContext *>(inContext);
for (size_t jth = 0; jth < kTestElements; jth++)
{
PacketBuffer * buffer_1;
PacketBuffer * buffer_2;
PacketBuffer * returned = nullptr;
if (theFirstContext == theSecondContext)
{
continue;
}
buffer_1 = PrepareTestBuffer(theFirstContext);
buffer_2 = PrepareTestBuffer(theSecondContext);
theFirstContext->buf->next = theSecondContext->buf;
returned = PacketBuffer::FreeHead(buffer_1);
NL_TEST_ASSERT(inSuite, returned == buffer_2);
theFirstContext->buf = nullptr;
theSecondContext++;
}
theFirstContext++;
}
}
/**
* Test PacketBuffer::BuildFreeList() function.
*/
void CheckBuildFreeList(nlTestSuite * inSuite, void * inContext)
{
// BuildFreeList() is a private method called automatically.
(void) inSuite;
(void) inContext;
}
/**
* Test Suite. It lists all the test functions.
*/
// clang-format off
const nlTest sTests[] =
{
NL_TEST_DEF("PacketBuffer::NewWithAvailableSize&PacketBuffer::Free", CheckNewWithAvailableSizeAndFree),
NL_TEST_DEF("PacketBuffer::Start", CheckStart),
NL_TEST_DEF("PacketBuffer::SetStart", CheckSetStart),
NL_TEST_DEF("PacketBuffer::DataLength", CheckDataLength),
NL_TEST_DEF("PacketBuffer::SetDataLength", CheckSetDataLength),
NL_TEST_DEF("PacketBuffer::TotalLength", CheckTotalLength),
NL_TEST_DEF("PacketBuffer::MaxDataLength", CheckMaxDataLength),
NL_TEST_DEF("PacketBuffer::AvailableDataLength", CheckAvailableDataLength),
NL_TEST_DEF("PacketBuffer::ReservedSize", CheckReservedSize),
NL_TEST_DEF("PacketBuffer::AddToEnd", CheckAddToEnd),
NL_TEST_DEF("PacketBuffer::DetachTail", CheckDetachTail),
NL_TEST_DEF("PacketBuffer::CompactHead", CheckCompactHead),
NL_TEST_DEF("PacketBuffer::ConsumeHead", CheckConsumeHead),
NL_TEST_DEF("PacketBuffer::Consume", CheckConsume),
NL_TEST_DEF("PacketBuffer::EnsureReservedSize", CheckEnsureReservedSize),
NL_TEST_DEF("PacketBuffer::AlignPayload", CheckAlignPayload),
NL_TEST_DEF("PacketBuffer::Next", CheckNext),
NL_TEST_DEF("PacketBuffer::AddRef", CheckAddRef),
NL_TEST_DEF("PacketBuffer::Free", CheckFree),
NL_TEST_DEF("PacketBuffer::FreeHead", CheckFreeHead),
NL_TEST_DEF("PacketBuffer::BuildFreeList", CheckBuildFreeList),
NL_TEST_SENTINEL()
};
// clang-format on
/**
* Set up the test suite.
*
* This is a work-around to initiate PacketBuffer protected class instance's data and set it to a known state, before an instance
* is created.
*/
int TestSetup(void * inContext)
{
struct TestContext * theContext = reinterpret_cast<TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
BufferAlloc(theContext);
theContext++;
}
return (SUCCESS);
}
/**
* Tear down the test suite.
*
* Free memory reserved at TestSetup.
*/
int TestTeardown(void * inContext)
{
struct TestContext * theContext = reinterpret_cast<TestContext *>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
BufferFree(theContext);
theContext++;
}
return (SUCCESS);
}
} // namespace
int TestSystemPacketBuffer(void)
{
// clang-format off
nlTestSuite theSuite =
{
"chip-system-packetbuffer",
&sTests[0],
TestSetup,
TestTeardown
};
// clang-format on
// Run test suit againt one context.
nlTestRunner(&theSuite, &sContext);
return (nlTestRunnerStats(&theSuite));
}
CHIP_REGISTER_TEST_SUITE(TestSystemPacketBuffer)