blob: 024a0f160f29c1d32736de48faa371e6b512ff32 [file] [log] [blame]
/*
*
* Copyright (c) 2016-2017 Nest Labs, Inc.
* All rights reserved.
*
* 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>nl::Weave::System::PacketBuffer</tt>, a class that
* provides structure for network packet buffer management.
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <SystemLayer/SystemPacketBuffer.h>
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
#include <lwip/tcpip.h>
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#include <nltest.h>
using ::nl::Weave::System::PacketBuffer;
#if !WEAVE_SYSTEM_CONFIG_USE_LWIP
using ::nl::Weave::System::pbuf;
#endif
// 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.
static struct TestContext sContext[] = {
{ 0, 0, NULL, NULL, NULL, NULL },
{ 0, 10, NULL, NULL, NULL, NULL },
{ 0, 128, NULL, NULL, NULL, NULL },
{ 0, 1536, NULL, NULL, NULL, NULL },
{ 0, WEAVE_SYSTEM_PACKETBUFFER_SIZE, NULL, NULL, NULL, NULL }
};
static const uint16_t sLengths[] = { 0, 1, 10, 128, WEAVE_SYSTEM_PACKETBUFFER_SIZE, UINT16_MAX };
// Number of test context examples.
static const size_t kTestElements = sizeof(sContext) / sizeof(struct TestContext);
static 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.
*/
static void BufferFree(struct TestContext* theContext)
{
if (theContext->buf != NULL)
{
PacketBuffer::Free(OF_LWIP_PBUF(theContext->buf));
theContext->buf = NULL;
}
}
/**
* Allocate memory for a test buffer and configure according to test context.
*/
static void BufferAlloc(struct TestContext* theContext)
{
const size_t lInitialSize = WEAVE_SYSTEM_PACKETBUFFER_HEADER_SIZE + theContext->reserved_size;
const size_t lAllocSize = WEAVE_SYSTEM_PACKETBUFFER_SIZE;
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
u8_t lType, lFlags;
#if LWIP_PBUF_FROM_CUSTOM_POOLS
u16_t lPool;
#endif // LWIP_PBUF_FROM_CUSTOM_POOLS
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
if (theContext->buf == NULL)
{
theContext->buf = TO_LWIP_PBUF(PacketBuffer::New(0));
}
if (theContext->buf == NULL)
{
fprintf(stderr, "Failed to allocate %zuB memory: %s\n", lAllocSize, strerror(errno));
exit(EXIT_FAILURE);
}
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
lType = theContext->buf->type;
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);
theContext->buf->type = lType;
theContext->buf->flags = lFlags;
#if LWIP_PBUF_FROM_CUSTOM_POOLS
theContext->buf->pool = lPool;
#endif // LWIP_PBUF_FROM_CUSTOM_POOLS
#else // !WEAVE_SYSTEM_CONFIG_USE_LWIP
memset(theContext->buf, 0, lAllocSize);
#if WEAVE_SYSTEM_CONFIG_PACKETBUFFER_MAXALLOC == 0
theContext->buf->alloc_size = lAllocSize;
#endif // WEAVE_SYSTEM_CONFIG_PACKETBUFFER_MAXALLOC == 0
#endif // WEAVE_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.
*/
static PacketBuffer* PrepareTestBuffer(struct TestContext* theContext)
{
BufferAlloc(theContext);
theContext->buf->next = NULL;
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.
*/
static void CheckStart(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (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.
*/
static void CheckSetStart(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (struct TestContext *)(inContext);
static const ptrdiff_t sSizePacketBuffer = WEAVE_SYSTEM_PACKETBUFFER_SIZE;
for (size_t ith = 0; ith < kTestElements; ith++)
{
static const ptrdiff_t start_offset[] =
{
-sSizePacketBuffer,
-128,
-1,
0,
1,
128,
sSizePacketBuffer
};
for (size_t s = 0; s < sizeof(start_offset) / sizeof(start_offset[0]); s++)
{
PacketBuffer *buffer = PrepareTestBuffer(theContext);
uint8_t *test_start = theContext->payload_ptr + start_offset[s];
uint8_t *verify_start = test_start;
buffer->SetStart(test_start);
if (verify_start < theContext->start_buffer + WEAVE_SYSTEM_PACKETBUFFER_HEADER_SIZE)
{
// Set start before valid payload beginning.
verify_start = theContext->start_buffer + WEAVE_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.
*/
static void CheckDataLength(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (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.
*/
static 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 (size_t n = 0; n < kTestLengths; n++)
{
PacketBuffer *buffer_1 = PrepareTestBuffer(theFirstContext);
PacketBuffer *buffer_2 = PrepareTestBuffer(theSecondContext);
if (theFirstContext == theSecondContext)
{
// headOfChain (the second arg) is NULL
buffer_2->SetDataLength(sLengths[n], NULL);
if (sLengths[n] > (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 == NULL);
}
else
{
NL_TEST_ASSERT(inSuite, theSecondContext->buf->len == sLengths[n]);
NL_TEST_ASSERT(inSuite, theSecondContext->buf->tot_len == sLengths[n]);
NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == NULL);
}
}
else
{
// headOfChain (the second arg) is buffer_1
buffer_2->SetDataLength(sLengths[n], buffer_1);
if (sLengths[n] > (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 == NULL);
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 == sLengths[n]);
NL_TEST_ASSERT(inSuite, theSecondContext->buf->tot_len == sLengths[n]);
NL_TEST_ASSERT(inSuite, theSecondContext->buf->next == NULL);
NL_TEST_ASSERT(inSuite, theFirstContext->buf->tot_len ==
(theFirstContext->init_len +
static_cast<int32_t>(sLengths[n]) -
static_cast<int32_t>(theSecondContext->init_len)));
}
}
}
theSecondContext++;
}
theFirstContext++;
}
}
/**
* Test PacketBuffer::TotalLength() function.
*/
static void CheckTotalLength(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (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.
*/
static void CheckMaxDataLength(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (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.
*/
static void CheckAvailableDataLength(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (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.
*/
static void CheckReservedSize(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (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.
*/
static 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 = NULL;
PacketBuffer *buffer_2 = NULL;
PacketBuffer *buffer_3 = NULL;
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 == NULL);
NL_TEST_ASSERT(inSuite, theThirdContext->buf->next == NULL);
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 == NULL);
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.
*/
static 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 = NULL;
if (theFirstContext != theSecondContext)
{
theFirstContext->buf->next = theSecondContext->buf;
theFirstContext->buf->tot_len += theSecondContext->init_len;
}
returned = buffer_1->DetachTail();
NL_TEST_ASSERT(inSuite, theFirstContext->buf->next == NULL);
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.
*/
static 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 (size_t k = 0; k < kTestLengths; k++)
{
// start with various initial length for the second buffer
for (size_t l = 0; l < kTestLengths; l++)
{
PacketBuffer *buffer_1 = PrepareTestBuffer(theFirstContext);
PacketBuffer *buffer_2 = PrepareTestBuffer(theSecondContext);
uint16_t len1 = 0;
uint16_t len2 = 0;
buffer_1->SetDataLength(sLengths[k], buffer_1);
len1 = buffer_1->DataLength();
if (theFirstContext != theSecondContext)
{
theFirstContext->buf->next = theSecondContext->buf;
// Add various lengths to the second buffer
buffer_2->SetDataLength(sLengths[l], buffer_1);
len2 = buffer_2->DataLength();
}
buffer_1->CompactHead();
NL_TEST_ASSERT(inSuite, theFirstContext->buf->payload ==
(theFirstContext->start_buffer + WEAVE_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 == NULL);
theSecondContext->buf = NULL;
}
}
}
}
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.
*/
static void CheckConsumeHead(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (struct TestContext *)(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
for (size_t n = 0; n < kTestLengths; n++)
{
PacketBuffer *buffer = PrepareTestBuffer(theContext);
buffer->ConsumeHead(sLengths[n]);
if (sLengths[n] > 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 + sLengths[n]));
NL_TEST_ASSERT(inSuite, theContext->buf->len == (theContext->buf->len - sLengths[n]));
NL_TEST_ASSERT(inSuite, theContext->buf->tot_len == (theContext->buf->tot_len - sLengths[n]));
}
if (theContext->buf->ref == 0) {
theContext->buf = NULL;
}
}
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.
*/
static 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 (size_t c = 0; c < kTestLengths; c++)
{
// start with various initial length for the first buffer
for (size_t k = 0; k < kTestLengths; k++)
{
// start with various initial length for the second buffer
for (size_t l = 0; l < kTestLengths; l++)
{
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(sLengths[k], buffer_1);
buffer_2->SetDataLength(sLengths[l], buffer_1);
buf_1_len = theFirstContext->buf->len;
buf_2_len = theSecondContext->buf->len;
returned = buffer_1->Consume(sLengths[c]);
if (sLengths[c] == 0)
{
NL_TEST_ASSERT(inSuite, returned == buffer_1);
continue;
}
if (sLengths[c] < buf_1_len)
{
NL_TEST_ASSERT(inSuite, returned == buffer_1);
}
else if ((sLengths[c] >= buf_1_len) && (sLengths[c] < buf_1_len + buf_2_len ||
(sLengths[c] == buf_1_len + buf_2_len && buf_2_len == 0)))
{
NL_TEST_ASSERT(inSuite, returned == buffer_2);
theFirstContext->buf = NULL;
}
else if (sLengths[c] >= (buf_1_len + buf_2_len))
{
NL_TEST_ASSERT(inSuite, returned == NULL);
theFirstContext->buf = NULL;
theSecondContext->buf = NULL;
}
}
}
}
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.
*/
static void CheckEnsureReservedSize(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (struct TestContext *)(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
for (size_t n = 0; n < kTestLengths; n++)
{
PacketBuffer& lBuffer = *PrepareTestBuffer(theContext);
const size_t kAllocSize = lBuffer.AllocSize();
uint16_t reserved_size = theContext->reserved_size;
if (WEAVE_SYSTEM_PACKETBUFFER_HEADER_SIZE + theContext->reserved_size > kAllocSize)
{
reserved_size = kAllocSize - WEAVE_SYSTEM_PACKETBUFFER_HEADER_SIZE;
}
if (sLengths[n] <= reserved_size)
{
NL_TEST_ASSERT(inSuite, lBuffer.EnsureReservedSize(sLengths[n]) == true);
continue;
}
if ((sLengths[n] + theContext->init_len) > (kAllocSize - WEAVE_SYSTEM_PACKETBUFFER_HEADER_SIZE))
{
NL_TEST_ASSERT(inSuite, lBuffer.EnsureReservedSize(sLengths[n]) == false);
continue;
}
NL_TEST_ASSERT(inSuite, lBuffer.EnsureReservedSize(sLengths[n]) == true);
NL_TEST_ASSERT(inSuite, theContext->buf->payload == (theContext->payload_ptr + sLengths[n] - 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.
*/
static void CheckAlignPayload(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (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 size_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 = (unsigned long) lBuffer.Start() % sLengths[n];
uint16_t payload_shift = 0;
if (payload_offset > 0)
payload_shift = 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.
*/
static 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() == NULL);
}
NL_TEST_ASSERT(inSuite, buffer_2->Next() == NULL);
theSecondContext++;
}
theFirstContext++;
}
}
/**
* Test PacketBuffer::AddRef() function.
*/
static void CheckAddRef(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (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 #WEAVE_SYSTEM_PACKETBUFFER_ALLOCSIZE_MAX, the method
* returns NULL. Otherwise, check for correctness of initializing
* the new buffer's internal state. Finally, free the buffer.
*/
static void CheckNewWithAvailableSizeAndFree(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (struct TestContext *)(inContext);
PacketBuffer *buffer;
for (size_t ith = 0; ith < kTestElements; ith++)
{
struct pbuf *pb = NULL;
buffer = PacketBuffer::NewWithAvailableSize(theContext->reserved_size, 0);
if (theContext->reserved_size > WEAVE_SYSTEM_PACKETBUFFER_ALLOCSIZE_MAX)
{
NL_TEST_ASSERT(inSuite, buffer == NULL);
theContext++;
continue;
}
NL_TEST_ASSERT(inSuite, theContext->reserved_size <= buffer->AllocSize());
NL_TEST_ASSERT(inSuite, buffer != NULL);
if (buffer != NULL)
{
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 == NULL);
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 != NULL);
}
/**
* 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.
*/
static 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 uint16_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 = NULL;
}
if (theSecondContext->buf->ref == 0)
{
theSecondContext->buf = NULL;
}
}
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.
*/
static 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 = NULL;
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 = NULL;
theSecondContext++;
}
theFirstContext++;
}
}
/**
* Test PacketBuffer::BuildFreeList() function.
*/
static 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.
*/
static 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()
};
/**
* 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.
*/
static 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.
*/
static int TestTeardown(void *inContext)
{
struct TestContext* theContext = reinterpret_cast<TestContext*>(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
BufferFree(theContext);
theContext++;
}
return (SUCCESS);
}
int main(void)
{
nlTestSuite theSuite = {
"weave-system-packetbuffer",
&sTests[0],
TestSetup,
TestTeardown
};
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
tcpip_init(NULL, NULL);
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
// Generate machine-readable, comma-separated value (CSV) output.
nl_test_set_output_style(OUTPUT_CSV);
// Run test suit againt one context.
nlTestRunner(&theSuite, &sContext);
return nlTestRunnerStats(&theSuite);
}