blob: eabd7cdcee7c5ad70ec6dfe1d9c84d1de28e8ca7 [file] [log] [blame]
/*
*
* Copyright (c) 2015-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::Inet::InetBuffer</tt>,
* a class that provides structure for network buffer management.
*
*/
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <stdint.h>
#include <InetLayer/InetConfig.h>
#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
#include <string.h>
#include <InetLayer/InetBuffer.h>
#include <nltest.h>
#include "ToolCommon.h"
#define MEM_ALIGN_SIZE(SZ, ALIGN) (((SZ) + (ALIGN) - 1) & ~((ALIGN) - 1))
#if INET_LWIP
#define INET_BUF_SIZE LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE)
#define INET_BUF_HEADER_SIZE LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf))
#else
#define INET_BUF_SIZE WEAVE_SYSTEM_PACKETBUFFER_SIZE
#define INET_BUF_HEADER_SIZE MEM_ALIGN_SIZE(sizeof(InetBuffer), 4)
#endif
#define INET_TO_PBUF(x) (static_cast<struct pbuf*>(static_cast<void *>(x)))
#define PBUF_TO_INET(x) (static_cast<InetBuffer*>(static_cast<void *>(x)))
using namespace nl::Inet;
// 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, WEAVE_SYSTEM_PACKETBUFFER_SIZE, NULL, NULL, NULL, NULL },
{ 0, INET_BUF_SIZE, NULL, NULL, NULL, NULL }
};
static const uint16_t sLengths[] = { 0, 1, 10, 128, INET_BUF_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.
/**
* Free allocated test buffer memory.
*/
static void BufferFree(struct TestContext *theContext)
{
if (theContext->buf != NULL)
{
InetBuffer::Free(PBUF_TO_INET(theContext->buf));
}
}
/**
* Allocate memory for a test buffer and configure according to test context.
*/
static void BufferAlloc(struct TestContext *theContext)
{
if (theContext->buf == NULL)
{
theContext->buf = INET_TO_PBUF(InetBuffer::New(0));
}
if (theContext->buf == NULL)
{
fprintf(stderr, "Failed to allocate %zuB memory: %s\n",
((size_t)INET_BUF_SIZE), strerror(errno));
exit(EXIT_FAILURE);
}
memset(theContext->buf, 0, INET_BUF_SIZE);
theContext->start_buffer = reinterpret_cast<uint8_t*>(theContext->buf);
theContext->end_buffer = reinterpret_cast<uint8_t*>(theContext->buf) + INET_BUF_SIZE;
if (INET_BUF_HEADER_SIZE + theContext->reserved_size > INET_BUF_SIZE)
{
theContext->payload_ptr = (theContext->start_buffer + INET_BUF_SIZE);
}
else
{
theContext->payload_ptr = (theContext->start_buffer + INET_BUF_HEADER_SIZE + theContext->reserved_size);
}
}
/**
* Setup buffer layout as it is used by InetBuffer class.
*/
static InetBuffer *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;
#if INET_LWIP
theContext->buf->type = PBUF_POOL;
#endif
return reinterpret_cast<InetBuffer*>(theContext->buf);
}
// Test functions invoked from the suite.
/**
* Test InetBuffer::Start() function.
*/
static void CheckStart(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (struct TestContext *)(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
InetBuffer *buffer = PrepareTestBuffer(theContext);
NL_TEST_ASSERT(inSuite, buffer->Start() == theContext->payload_ptr);
theContext++;
}
}
/**
* Test InetBuffer::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);
for (size_t ith = 0; ith < kTestElements; ith++)
{
const int start_offset[] = { -static_cast<int>(INET_BUF_SIZE), -128, -1, 0, 1, 128, static_cast<int>(INET_BUF_SIZE) };
for (size_t s = 0; s < sizeof(start_offset) / sizeof(int); s++)
{
uint8_t *test_start = theContext->payload_ptr + start_offset[s];
uint8_t *verify_start = test_start;
InetBuffer *buffer = PrepareTestBuffer(theContext);
buffer->SetStart(test_start);
if (verify_start <= theContext->start_buffer + INET_BUF_HEADER_SIZE)
{
// Set start before valid payload beginning.
verify_start = theContext->start_buffer + INET_BUF_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 InetBuffer::DataLength() function.
*/
static void CheckDataLength(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (struct TestContext *)(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
InetBuffer *buffer = PrepareTestBuffer(theContext);
NL_TEST_ASSERT(inSuite, buffer->DataLength() == theContext->buf->len);
theContext++;
}
}
/**
* Test InetBuffer::SetDataLength() function.
*
* Description: Take two initial configurations of InetBuffer from
* inContext and create two InetBuffer 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++)
{
InetBuffer *buffer_1 = PrepareTestBuffer(theFirstContext);
InetBuffer *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 InetBuffer::TotalLength() function.
*/
static void CheckTotalLength(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (struct TestContext *)(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
InetBuffer *buffer = PrepareTestBuffer(theContext);
NL_TEST_ASSERT(inSuite, buffer->TotalLength() == theContext->init_len);
theContext++;
}
}
/**
* Test InetBuffer::MaxDataLength() function.
*/
static void CheckMaxDataLength(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (struct TestContext *)(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
InetBuffer *buffer = PrepareTestBuffer(theContext);
NL_TEST_ASSERT(inSuite, buffer->MaxDataLength() ==
(theContext->end_buffer - theContext->payload_ptr));
theContext++;
}
}
/**
* Test InetBuffer::AvailableDataLength() function.
*/
static void CheckAvailableDataLength(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (struct TestContext *)(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
InetBuffer *buffer = PrepareTestBuffer(theContext);
NL_TEST_ASSERT(inSuite, buffer->AvailableDataLength() ==
((theContext->end_buffer - theContext->payload_ptr) - theContext->init_len));
theContext++;
}
}
/**
* Test InetBuffer::ReservedSize() function.
*/
static void CheckReservedSize(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (struct TestContext *)(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
InetBuffer *buffer = PrepareTestBuffer(theContext);
if (INET_BUF_HEADER_SIZE + theContext->reserved_size > INET_BUF_SIZE)
{
NL_TEST_ASSERT(inSuite, buffer->ReservedSize() ==
(INET_BUF_SIZE - INET_BUF_HEADER_SIZE));
}
else
{
NL_TEST_ASSERT(inSuite, buffer->ReservedSize() ==
theContext->reserved_size);
}
theContext++;
}
}
/**
* Test InetBuffer::AddToEnd() function.
*
* Description: Take three initial configurations of InetBuffer from
* inContext, create three InetBuffers based on those
* configurations and then link those buffers together with
* InetBuffer: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++)
{
InetBuffer *buffer_1 = NULL;
InetBuffer *buffer_2 = NULL;
InetBuffer *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 InetBuffer::DetachTail() function.
*
* Description: Take two initial configurations of InetBuffer from
* inContext and create two InetBuffer 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++)
{
InetBuffer *buffer_1 = PrepareTestBuffer(theFirstContext);
InetBuffer *buffer_2 = PrepareTestBuffer(theSecondContext);
InetBuffer *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 InetBuffer::CompactHead() function.
*
* Description: Take two initial configurations of InetBuffer from
* inContext and create two InetBuffer 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++)
{
InetBuffer *buffer_1 = PrepareTestBuffer(theFirstContext);
InetBuffer *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 + INET_BUF_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 InetBuffer::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++)
{
InetBuffer *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 InetBuffer::Consume() function.
*
* Description: Take two different initial configurations of InetBuffer from
* inContext and create two InetBuffer 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++)
{
InetBuffer *buffer_1;
InetBuffer *buffer_2;
InetBuffer *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 InetBuffer::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++)
{
InetBuffer *buffer = PrepareTestBuffer(theContext);
uint16_t reserved_size = theContext->reserved_size;
if (INET_BUF_HEADER_SIZE + theContext->reserved_size > INET_BUF_SIZE)
{
reserved_size = INET_BUF_SIZE - INET_BUF_HEADER_SIZE;
}
if (sLengths[n] <= reserved_size)
{
NL_TEST_ASSERT(inSuite, buffer->EnsureReservedSize(sLengths[n]) == true);
continue;
}
if ((sLengths[n] + theContext->init_len) > (INET_BUF_SIZE - INET_BUF_HEADER_SIZE))
{
NL_TEST_ASSERT(inSuite, buffer->EnsureReservedSize(sLengths[n]) == false);
continue;
}
NL_TEST_ASSERT(inSuite, buffer->EnsureReservedSize(sLengths[n]) == true);
NL_TEST_ASSERT(inSuite, theContext->buf->payload ==
(theContext->payload_ptr + sLengths[n] - reserved_size));
}
theContext++;
}
}
/**
* Test InetBuffer::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++)
{
InetBuffer *buffer = PrepareTestBuffer(theContext);
if (sLengths[n] == 0)
{
NL_TEST_ASSERT(inSuite, buffer->AlignPayload(sLengths[n]) == false);
continue;
}
uint16_t reserved_size = theContext->reserved_size;
if (INET_BUF_HEADER_SIZE + theContext->reserved_size > INET_BUF_SIZE)
{
reserved_size = INET_BUF_SIZE - INET_BUF_HEADER_SIZE;
}
uint16_t payload_offset = (unsigned long) buffer->Start() % sLengths[n];
uint16_t payload_shift = 0;
if (payload_offset > 0)
payload_shift = sLengths[n] - payload_offset;
if (payload_shift <= INET_BUF_SIZE - INET_BUF_HEADER_SIZE - reserved_size)
{
NL_TEST_ASSERT(inSuite, buffer->AlignPayload(sLengths[n]) == true);
NL_TEST_ASSERT(inSuite, ((unsigned long) buffer->Start() % sLengths[n]) == 0);
}
else
{
NL_TEST_ASSERT(inSuite, buffer->AlignPayload(sLengths[n]) == false);
}
}
theContext++;
}
}
/**
* Test InetBuffer::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++)
{
InetBuffer *buffer_1 = PrepareTestBuffer(theFirstContext);
InetBuffer *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 InetBuffer::AddRef() function.
*/
static void CheckAddRef(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (struct TestContext *)(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
InetBuffer *buffer = PrepareTestBuffer(theContext);
buffer->AddRef();
NL_TEST_ASSERT(inSuite, theContext->buf->ref == 2);
theContext++;
}
}
/**
* Test InetBuffer::New() and InetBuffer::Free() functions.
*
* Description: For every buffer-configuration from inContext, create a
* buffer's instance using New() method. Then, verify that
* when the size of the reserved space passed to New() is
* greater than INET_BUF_SIZE - INET_BUF_HEADER_SIZE, the method
* returns NULL. Otherwise, check for correctness of initializing
* the new buffer's internal state. Finally, free the buffer.
*/
static void CheckNewAndFree(nlTestSuite *inSuite, void *inContext)
{
struct TestContext *theContext = (struct TestContext *)(inContext);
InetBuffer *buffer;
for (size_t ith = 0; ith < kTestElements; ith++)
{
struct pbuf *pb = NULL;
buffer = InetBuffer::New(theContext->reserved_size);
if (theContext->reserved_size + INET_BUF_HEADER_SIZE > INET_BUF_SIZE)
{
NL_TEST_ASSERT(inSuite, buffer == NULL);
theContext++;
continue;
}
NL_TEST_ASSERT(inSuite, buffer != NULL);
if (buffer != NULL)
{
pb = INET_TO_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);
}
InetBuffer::Free(buffer);
theContext++;
}
// Use the rest of the buffer space
do
{
buffer = InetBuffer::New();
}
while (buffer != NULL);
}
/**
* Test InetBuffer::Free() function.
*
* Description: Take two different initial configurations of InetBuffer from
* inContext and create two InetBuffer 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++)
{
InetBuffer *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];
InetBuffer::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 InetBuffer::FreeHead() function.
*
* Description: Take two different initial configurations of InetBuffer from
* inContext and create two InetBuffer 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++)
{
InetBuffer *buffer_1;
InetBuffer *buffer_2;
InetBuffer *returned = NULL;
if (theFirstContext == theSecondContext)
{
continue;
}
buffer_1 = PrepareTestBuffer(theFirstContext);
buffer_2 = PrepareTestBuffer(theSecondContext);
theFirstContext->buf->next = theSecondContext->buf;
returned = InetBuffer::FreeHead(buffer_1);
NL_TEST_ASSERT(inSuite, returned == buffer_2);
theFirstContext->buf = NULL;
theSecondContext++;
}
theFirstContext++;
}
}
/**
* Test InetBuffer::BuildFreeList() function.
*/
static void CheckBuildFreeList(nlTestSuite *inSuite, void *inContext)
{
// BuildFreeList() is a private method called automatically.
(void)inSuite;
(void)inContext;
}
// Test Suite
/**
* Test Suite. It lists all the test functions.
*/
static const nlTest sTests[] = {
NL_TEST_DEF("InetBuffer::Start", CheckStart),
NL_TEST_DEF("InetBuffer::SetStart", CheckSetStart),
NL_TEST_DEF("InetBuffer::DataLength", CheckDataLength),
NL_TEST_DEF("InetBuffer::SetDataLength", CheckSetDataLength),
NL_TEST_DEF("InetBuffer::TotalLength", CheckTotalLength),
NL_TEST_DEF("InetBuffer::MaxDataLength", CheckMaxDataLength),
NL_TEST_DEF("InetBuffer::AvailableDataLength", CheckAvailableDataLength),
NL_TEST_DEF("InetBuffer::ReservedSize", CheckReservedSize),
NL_TEST_DEF("InetBuffer::AddToEnd", CheckAddToEnd),
NL_TEST_DEF("InetBuffer::DetachTail", CheckDetachTail),
NL_TEST_DEF("InetBuffer::CompactHead", CheckCompactHead),
NL_TEST_DEF("InetBuffer::ConsumeHead", CheckConsumeHead),
NL_TEST_DEF("InetBuffer::Consume", CheckConsume),
NL_TEST_DEF("InetBuffer::EnsureReservedSize", CheckEnsureReservedSize),
NL_TEST_DEF("InetBuffer::AlignPayload", CheckAlignPayload),
NL_TEST_DEF("InetBuffer::Next", CheckNext),
NL_TEST_DEF("InetBuffer::AddRef", CheckAddRef),
NL_TEST_DEF("InetBuffer::New & InetBuffer::Free", CheckNewAndFree),
NL_TEST_DEF("InetBuffer::Free", CheckFree),
NL_TEST_DEF("InetBuffer::FreeHead", CheckFreeHead),
NL_TEST_DEF("InetBuffer::BuildFreeList", CheckBuildFreeList),
NL_TEST_SENTINEL()
};
/**
* Set up the test suite.
* This is a work-around to initiate InetBuffer 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 = (struct 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 = (struct TestContext *)(inContext);
for (size_t ith = 0; ith < kTestElements; ith++)
{
BufferFree(theContext);
theContext++;
}
return (SUCCESS);
}
int main(void)
{
nlTestSuite theSuite = {
"inet-buffer",
&sTests[0],
TestSetup,
TestTeardown
};
// Init lwip
#if INET_LWIP
tcpip_init(NULL, NULL);
#endif
// 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);
}
#else // !INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
int main(void)
{
return 0;
}
#endif // !INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES