| /* |
| * |
| * 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); |
| } |