mirror of
https://github.com/espressif/openthread.git
synced 2026-07-02 10:20:24 +00:00
bbab3074ed
This commit new configuration option `enable-multiple-instances` and its corresponding option `OPENTHREAD_ENABLE_MULTIPLE_INSTANCES`. When enabled OpenThread supports handling of multiple instances. By default this is disabled. This commit also adds two optimizations for single instance case to simplify the code and also help reduce memory/RAM usage: (1) OpenThread objects/classes typically keep a reference to a higher level object (e.g., many classes keep track of owning `ThreadNetif`) to be able to access other objects/methods within OpenThread class hierarchy. In single instance mode, the reference member variables are removed and instead global functions are used to access the singleton objects from one class to the other. To implement this, a group of `<Object>Locator` classes are defined (e.g., `ThreadNetifLocator`, etc.) which are then used as base class of other OpenThread classes. (2) OpenThread objects which provide a callback/handler (e.g., `Timer`, `Tasklet`, etc.) have `void *mContext` member variable which is used to keep track of the owner of the object. In single instance mode the `mConext` member variables are removed since the owner is expected to be a singleton and can be uniquely determined from the callback function. To implement this, two changes are made: First the handler methods are modified to provide a reference to the object (e.g., `Timer` handler will provide a `Timer &aTimer` as a parameter of its handler callback). Second, a new base class `Context` is introduced which hides the implementation providing an arbitrary context information. A new static method `GetOwner(aContext)` is added to classes which own a callback providing objects. This method help convert a `Context` to the reference of the owner class object.
709 lines
25 KiB
C++
709 lines
25 KiB
C++
/*
|
|
* Copyright (c) 2016, The OpenThread Authors.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holder nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <openthread/openthread.h>
|
|
|
|
#include "openthread-instance.h"
|
|
#include "common/code_utils.hpp"
|
|
#include "common/message.hpp"
|
|
#include "ncp/ncp_buffer.hpp"
|
|
|
|
#include "test_platform.h"
|
|
#include "test_util.h"
|
|
|
|
namespace ot {
|
|
|
|
// This module implements unit-test for NcpFrameBuffer class.
|
|
|
|
enum
|
|
{
|
|
kTestBufferSize = 2500,
|
|
kTestIterationAttemps = 10000,
|
|
kTagArraySize = 1000,
|
|
};
|
|
|
|
// Messages used for building frames...
|
|
static const uint8_t sOpenThreadText[] = "OpenThread Rocks";
|
|
static const uint8_t sHelloText[] = "Hello there!";
|
|
static const uint8_t sMottoText[] = "Think good thoughts, say good words, do good deeds!";
|
|
static const uint8_t sMysteryText[] = "4871(\\):|(3$}{4|/4/2%14(\\)";
|
|
|
|
static otInstance *sInstance;
|
|
static MessagePool *sMessagePool;
|
|
|
|
struct CallbackContext
|
|
{
|
|
uint32_t mFrameAddedCount; // Number of times FrameAddedCallback is invoked.
|
|
uint32_t mFrameRemovedCount; // Number of times FrameRemovedCallback is invoked.
|
|
};
|
|
|
|
CallbackContext sContext;
|
|
|
|
NcpFrameBuffer::FrameTag sTagHistoryArray[kTagArraySize];
|
|
uint32_t sTagHistoryHead = 0;
|
|
uint32_t sTagHistoryTail = 0;
|
|
NcpFrameBuffer::FrameTag sExpectedRemovedTag = NcpFrameBuffer::kInvalidTag;
|
|
|
|
void ClearTagHistory(void)
|
|
{
|
|
sTagHistoryHead = sTagHistoryTail;
|
|
}
|
|
|
|
void AddTagToHistory(NcpFrameBuffer::FrameTag aTag)
|
|
{
|
|
sTagHistoryArray[sTagHistoryTail] = aTag;
|
|
|
|
if (++sTagHistoryTail == kTagArraySize)
|
|
{
|
|
sTagHistoryTail = 0;
|
|
}
|
|
|
|
VerifyOrQuit(sTagHistoryTail != sTagHistoryHead, "Ran out of space in `TagHistoryArray`, increase its size.");
|
|
}
|
|
|
|
void VerifyAndRemoveTagFromHistory(NcpFrameBuffer::FrameTag aTag)
|
|
{
|
|
VerifyOrQuit(sTagHistoryHead != sTagHistoryTail, "Tag history is empty,");
|
|
VerifyOrQuit(aTag == sTagHistoryArray[sTagHistoryHead], "Removed tag does not match the added one");
|
|
|
|
if (++sTagHistoryHead == kTagArraySize)
|
|
{
|
|
sTagHistoryHead = 0;
|
|
}
|
|
|
|
if (sExpectedRemovedTag != NcpFrameBuffer::kInvalidTag)
|
|
{
|
|
VerifyOrQuit(sExpectedRemovedTag == aTag, "Removed tag does match the previous OutFrameGetTag()");
|
|
sExpectedRemovedTag = NcpFrameBuffer::kInvalidTag;
|
|
}
|
|
}
|
|
|
|
void FrameAddedCallback(void *aContext, NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer *aNcpBuffer)
|
|
{
|
|
CallbackContext *callbackContext = reinterpret_cast<CallbackContext *>(aContext);
|
|
|
|
VerifyOrQuit(aNcpBuffer != NULL, "Null NcpFrameBuffer in the callback");
|
|
VerifyOrQuit(callbackContext != NULL, "Null context in the callback");
|
|
VerifyOrQuit(aTag != NcpFrameBuffer::kInvalidTag, "Invalid tag in the callback");
|
|
VerifyOrQuit(aTag == aNcpBuffer->InFrameGetLastTag(), "InFrameGetLastTag() does not match the tag from callback");
|
|
AddTagToHistory(aTag);
|
|
|
|
callbackContext->mFrameAddedCount++;
|
|
}
|
|
|
|
void FrameRemovedCallback(void *aContext, NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer *aNcpBuffer)
|
|
{
|
|
CallbackContext *callbackContext = reinterpret_cast<CallbackContext *>(aContext);
|
|
|
|
VerifyOrQuit(aNcpBuffer != NULL, "Null NcpFrameBuffer in the callback");
|
|
VerifyOrQuit(callbackContext != NULL, "Null context in the callback");
|
|
VerifyOrQuit(aTag != NcpFrameBuffer::kInvalidTag, "Invalid tag in the callback");
|
|
VerifyAndRemoveTagFromHistory(aTag);
|
|
|
|
callbackContext->mFrameRemovedCount++;
|
|
}
|
|
|
|
// Dump the buffer content to screen.
|
|
void DumpBuffer(const char *aTextMessage, uint8_t *aBuffer, uint16_t aBufferLength)
|
|
{
|
|
enum
|
|
{
|
|
kBytesPerLine = 32, // Number of bytes per line.
|
|
};
|
|
|
|
char charBuff[kBytesPerLine + 1];
|
|
uint16_t counter;
|
|
uint8_t byte;
|
|
|
|
printf("\n%s - len = %u\n ", aTextMessage, aBufferLength);
|
|
|
|
counter = 0;
|
|
|
|
while (aBufferLength--)
|
|
{
|
|
byte = *aBuffer++;
|
|
printf("%02X ", byte);
|
|
charBuff[counter] = isprint(byte) ? static_cast<char>(byte) : '.';
|
|
counter++;
|
|
|
|
if (counter == kBytesPerLine)
|
|
{
|
|
charBuff[counter] = 0;
|
|
printf(" %s\n ", charBuff);
|
|
counter = 0;
|
|
}
|
|
}
|
|
|
|
charBuff[counter] = 0;
|
|
|
|
while (counter++ < kBytesPerLine)
|
|
{
|
|
printf(" ");
|
|
}
|
|
|
|
printf(" %s\n", charBuff);
|
|
}
|
|
|
|
// Reads bytes from the ncp buffer, and verifies that it matches with the given content buffer.
|
|
void ReadAndVerifyContent(NcpFrameBuffer &aNcpBuffer, const uint8_t *aContentBuffer, uint16_t aBufferLength)
|
|
{
|
|
while (aBufferLength--)
|
|
{
|
|
VerifyOrQuit(aNcpBuffer.OutFrameHasEnded() == false, "Out frame ended before end of expected content.");
|
|
|
|
VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == *aContentBuffer++,
|
|
"Out frame read byte does not match expected content");
|
|
}
|
|
}
|
|
|
|
void WriteTestFrame1(NcpFrameBuffer &aNcpBuffer)
|
|
{
|
|
Message *message;
|
|
CallbackContext oldContext;
|
|
|
|
message = sMessagePool->New(Message::kTypeIp6, 0);
|
|
VerifyOrQuit(message != NULL, "Null Message");
|
|
SuccessOrQuit(message->SetLength(sizeof(sMottoText)), "Could not set the length of message.");
|
|
message->Write(0, sizeof(sMottoText), sMottoText);
|
|
|
|
oldContext = sContext;
|
|
SuccessOrQuit(aNcpBuffer.InFrameBegin(), "InFrameBegin() failed.");
|
|
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sMottoText, sizeof(sMottoText)), "InFrameFeedData() failed.");
|
|
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sMysteryText, sizeof(sMysteryText)), "InFrameFeedData() failed.");
|
|
SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message), "InFrameFeedMessage() failed.");
|
|
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sHelloText, sizeof(sHelloText)), "InFrameFeedData() failed.");
|
|
SuccessOrQuit(aNcpBuffer.InFrameEnd(), "InFrameEnd() failed.");
|
|
|
|
VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
|
|
VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
|
|
}
|
|
|
|
void VerifyAndRemoveFrame1(NcpFrameBuffer &aNcpBuffer)
|
|
{
|
|
CallbackContext oldContext = sContext;
|
|
|
|
sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
|
|
SuccessOrQuit(aNcpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly.");
|
|
VerifyOrQuit(sExpectedRemovedTag == aNcpBuffer.OutFrameGetTag(), "OutFrameGetTag() value changed unexpectedly.");
|
|
|
|
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMottoText) + sizeof(sMysteryText) + sizeof(sMottoText)
|
|
+ sizeof(sHelloText), "GetLength() is incorrect.");
|
|
|
|
ReadAndVerifyContent(aNcpBuffer, sMottoText, sizeof(sMottoText));
|
|
ReadAndVerifyContent(aNcpBuffer, sMysteryText, sizeof(sMysteryText));
|
|
ReadAndVerifyContent(aNcpBuffer, sMottoText, sizeof(sMottoText));
|
|
ReadAndVerifyContent(aNcpBuffer, sHelloText, sizeof(sHelloText));
|
|
|
|
VerifyOrQuit(aNcpBuffer.OutFrameHasEnded() == true, "Frame longer than expected.");
|
|
VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame.");
|
|
VerifyOrQuit(sExpectedRemovedTag == aNcpBuffer.OutFrameGetTag(), "OutFrameGetTag() value changed unexpectedly.");
|
|
SuccessOrQuit(aNcpBuffer.OutFrameRemove(), "Remove() failed.");
|
|
|
|
VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
|
|
VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
|
|
}
|
|
|
|
void WriteTestFrame2(NcpFrameBuffer &aNcpBuffer)
|
|
{
|
|
Message *message1;
|
|
Message *message2;
|
|
CallbackContext oldContext = sContext;
|
|
|
|
message1 = sMessagePool->New(Message::kTypeIp6, 0);
|
|
VerifyOrQuit(message1 != NULL, "Null Message");
|
|
SuccessOrQuit(message1->SetLength(sizeof(sMysteryText)), "Could not set the length of message.");
|
|
message1->Write(0, sizeof(sMysteryText), sMysteryText);
|
|
|
|
message2 = sMessagePool->New(Message::kTypeIp6, 0);
|
|
VerifyOrQuit(message2 != NULL, "Null Message");
|
|
SuccessOrQuit(message2->SetLength(sizeof(sHelloText)), "Could not set the length of message.");
|
|
message2->Write(0, sizeof(sHelloText), sHelloText);
|
|
|
|
SuccessOrQuit(aNcpBuffer.InFrameBegin(), "InFrameFeedBegin() failed.");
|
|
SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message1), "InFrameFeedMessage() failed.");
|
|
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)), "InFrameFeedData() failed.");
|
|
SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message2), "InFrameFeedMessage() failed.");
|
|
SuccessOrQuit(aNcpBuffer.InFrameEnd(), "InFrameEnd() failed.");
|
|
|
|
VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
|
|
VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
|
|
}
|
|
|
|
void VerifyAndRemoveFrame2(NcpFrameBuffer &aNcpBuffer)
|
|
{
|
|
CallbackContext oldContext = sContext;
|
|
|
|
SuccessOrQuit(aNcpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly.");
|
|
|
|
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText) + sizeof(sHelloText) + sizeof(sOpenThreadText),
|
|
"GetLength() is incorrect.");
|
|
|
|
ReadAndVerifyContent(aNcpBuffer, sMysteryText, sizeof(sMysteryText));
|
|
ReadAndVerifyContent(aNcpBuffer, sOpenThreadText, sizeof(sOpenThreadText));
|
|
ReadAndVerifyContent(aNcpBuffer, sHelloText, sizeof(sHelloText));
|
|
|
|
VerifyOrQuit(aNcpBuffer.OutFrameHasEnded() == true, "Frame longer than expected.");
|
|
VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame.");
|
|
sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
|
|
SuccessOrQuit(aNcpBuffer.OutFrameRemove(), "Remove() failed.");
|
|
|
|
VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
|
|
VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
|
|
}
|
|
|
|
void WriteTestFrame3(NcpFrameBuffer &aNcpBuffer)
|
|
{
|
|
Message *message1;
|
|
CallbackContext oldContext = sContext;
|
|
|
|
message1 = sMessagePool->New(Message::kTypeIp6, 0);
|
|
VerifyOrQuit(message1 != NULL, "Null Message");
|
|
|
|
// An empty message with no content.
|
|
SuccessOrQuit(message1->SetLength(0), "Could not set the length of message.");
|
|
|
|
SuccessOrQuit(aNcpBuffer.InFrameBegin(), "InFrameFeedBegin() failed.");
|
|
SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message1), "InFrameFeedMessage() failed.");
|
|
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sMysteryText, sizeof(sMysteryText)), "InFrameFeedData() failed.");
|
|
SuccessOrQuit(aNcpBuffer.InFrameEnd(), "InFrameEnd() failed.");
|
|
|
|
VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
|
|
VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
|
|
}
|
|
|
|
void VerifyAndRemoveFrame3(NcpFrameBuffer &aNcpBuffer)
|
|
{
|
|
CallbackContext oldContext = sContext;
|
|
|
|
SuccessOrQuit(aNcpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly.");
|
|
|
|
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText), "GetLength() is incorrect.");
|
|
|
|
ReadAndVerifyContent(aNcpBuffer, sMysteryText, sizeof(sMysteryText));
|
|
|
|
VerifyOrQuit(aNcpBuffer.OutFrameHasEnded() == true, "Frame longer than expected.");
|
|
VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame.");
|
|
sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
|
|
SuccessOrQuit(aNcpBuffer.OutFrameRemove(), "Remove() failed.");
|
|
|
|
VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
|
|
VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
|
|
}
|
|
|
|
// This function implements the NcpFrameBuffer tests
|
|
void TestNcpFrameBuffer(void)
|
|
{
|
|
unsigned i, j;
|
|
uint8_t buffer[kTestBufferSize];
|
|
NcpFrameBuffer ncpBuffer(buffer, kTestBufferSize);
|
|
|
|
Message *message;
|
|
uint8_t readBuffer[16];
|
|
uint16_t readLen, readOffset;
|
|
|
|
sInstance = testInitInstance();
|
|
sMessagePool = &sInstance->mIp6.mMessagePool;
|
|
|
|
for (i = 0; i < sizeof(buffer); i++)
|
|
{
|
|
buffer[i] = 0;
|
|
}
|
|
|
|
sContext.mFrameAddedCount = 0;
|
|
sContext.mFrameRemovedCount = 0;
|
|
ClearTagHistory();
|
|
|
|
// Set the callbacks.
|
|
ncpBuffer.SetFrameAddedCallback(FrameAddedCallback, &sContext);
|
|
ncpBuffer.SetFrameRemovedCallback(FrameRemovedCallback, &sContext);
|
|
|
|
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
printf("\nTest 1: Check initial buffer state");
|
|
|
|
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "Not empty after init.");
|
|
VerifyOrQuit(ncpBuffer.InFrameGetLastTag() == NcpFrameBuffer::kInvalidTag, "Incorrect tag after init.");
|
|
VerifyOrQuit(ncpBuffer.OutFrameGetTag() == NcpFrameBuffer::kInvalidTag, "Incorrect OutFrameTag after init.");
|
|
|
|
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
printf("\nTest 2: Write a frame 1 ");
|
|
|
|
WriteTestFrame1(ncpBuffer);
|
|
DumpBuffer("\nBuffer after frame1", buffer, kTestBufferSize);
|
|
printf("\nFrameLen is %u", ncpBuffer.OutFrameGetLength());
|
|
|
|
VerifyAndRemoveFrame1(ncpBuffer);
|
|
|
|
printf("\nIterations: ");
|
|
|
|
// Repeat this multiple times.
|
|
for (j = 0; j < kTestIterationAttemps; j++)
|
|
{
|
|
printf("*");
|
|
WriteTestFrame1(ncpBuffer);
|
|
VerifyOrQuit(ncpBuffer.IsEmpty() == false, "IsEmpty() is incorrect when buffer is non-empty");
|
|
|
|
VerifyAndRemoveFrame1(ncpBuffer);
|
|
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() is incorrect when buffer is empty.");
|
|
}
|
|
|
|
printf(" -- PASS\n");
|
|
|
|
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
printf("\nTest 3: Multiple frames write and read ");
|
|
|
|
WriteTestFrame2(ncpBuffer);
|
|
WriteTestFrame3(ncpBuffer);
|
|
WriteTestFrame2(ncpBuffer);
|
|
WriteTestFrame2(ncpBuffer);
|
|
|
|
DumpBuffer("\nBuffer after multiple frames", buffer, kTestBufferSize);
|
|
|
|
VerifyAndRemoveFrame2(ncpBuffer);
|
|
VerifyAndRemoveFrame3(ncpBuffer);
|
|
VerifyAndRemoveFrame2(ncpBuffer);
|
|
VerifyAndRemoveFrame2(ncpBuffer);
|
|
|
|
printf("\nIterations: ");
|
|
|
|
// Repeat this multiple times.
|
|
for (j = 0; j < kTestIterationAttemps; j++)
|
|
{
|
|
printf("*");
|
|
|
|
WriteTestFrame2(ncpBuffer);
|
|
WriteTestFrame3(ncpBuffer);
|
|
WriteTestFrame2(ncpBuffer);
|
|
|
|
VerifyAndRemoveFrame2(ncpBuffer);
|
|
VerifyAndRemoveFrame3(ncpBuffer);
|
|
|
|
WriteTestFrame2(ncpBuffer);
|
|
WriteTestFrame3(ncpBuffer);
|
|
|
|
VerifyAndRemoveFrame2(ncpBuffer);
|
|
VerifyAndRemoveFrame2(ncpBuffer);
|
|
VerifyAndRemoveFrame3(ncpBuffer);
|
|
|
|
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() is incorrect when buffer is empty.");
|
|
}
|
|
|
|
printf(" -- PASS\n");
|
|
|
|
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
printf("\nTest 4: Frame discard when buffer full and partial read restart");
|
|
|
|
for (j = 0; j < kTestIterationAttemps; j++)
|
|
{
|
|
WriteTestFrame2(ncpBuffer);
|
|
WriteTestFrame3(ncpBuffer);
|
|
|
|
ncpBuffer.InFrameBegin();
|
|
ncpBuffer.InFrameFeedData(sHelloText, sizeof(sHelloText));
|
|
|
|
message = sMessagePool->New(Message::kTypeIp6, 0);
|
|
VerifyOrQuit(message != NULL, "Null Message");
|
|
SuccessOrQuit(message->SetLength(sizeof(sMysteryText)), "Could not set the length of message.");
|
|
message->Write(0, sizeof(sMysteryText), sMysteryText);
|
|
|
|
ncpBuffer.InFrameFeedMessage(message);
|
|
|
|
// Now cause a restart the current frame and test if it's discarded ok.
|
|
WriteTestFrame2(ncpBuffer);
|
|
|
|
if (j == 0)
|
|
{
|
|
DumpBuffer("\nAfter frame gets discarded", buffer, kTestBufferSize);
|
|
printf("\nIterations: ");
|
|
}
|
|
else
|
|
{
|
|
printf("*");
|
|
}
|
|
|
|
VerifyAndRemoveFrame2(ncpBuffer);
|
|
|
|
// Start reading few bytes from the frame
|
|
ncpBuffer.OutFrameBegin();
|
|
ncpBuffer.OutFrameReadByte();
|
|
ncpBuffer.OutFrameReadByte();
|
|
ncpBuffer.OutFrameReadByte();
|
|
|
|
// Now reset the read pointer and read/verify the frame from start.
|
|
VerifyAndRemoveFrame3(ncpBuffer);
|
|
VerifyAndRemoveFrame2(ncpBuffer);
|
|
|
|
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() is incorrect when buffer is empty.");
|
|
}
|
|
|
|
printf(" -- PASS\n");
|
|
|
|
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
printf("\nTest 5: Clear() and empty buffer method tests");
|
|
|
|
WriteTestFrame1(ncpBuffer);
|
|
|
|
ncpBuffer.Clear();
|
|
ClearTagHistory();
|
|
|
|
VerifyOrQuit(ncpBuffer.InFrameGetLastTag() == NcpFrameBuffer::kInvalidTag, "Incorrect last tag after Clear().");
|
|
VerifyOrQuit(ncpBuffer.OutFrameGetTag() == NcpFrameBuffer::kInvalidTag, "Incorrect OutFrameTag after Clear().");
|
|
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() is incorrect when buffer is empty.");
|
|
VerifyOrQuit(ncpBuffer.OutFrameHasEnded() == true, "OutFrameHasEnded() is incorrect when no data in buffer.");
|
|
VerifyOrQuit(ncpBuffer.OutFrameRemove() == OT_ERROR_NOT_FOUND,
|
|
"Remove() returned incorrect error status when buffer is empty.");
|
|
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == 0, "OutFrameGetLength() returned non-zero length when buffer is empty.");
|
|
|
|
WriteTestFrame1(ncpBuffer);
|
|
VerifyAndRemoveFrame1(ncpBuffer);
|
|
|
|
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() is incorrect when buffer is empty.");
|
|
VerifyOrQuit(ncpBuffer.OutFrameHasEnded() == true, "OutFrameHasEnded() is incorrect when no data in buffer.");
|
|
VerifyOrQuit(ncpBuffer.OutFrameRemove() == OT_ERROR_NOT_FOUND,
|
|
"Remove() returned incorrect error status when buffer is empty.");
|
|
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == 0, "OutFrameGetLength() returned non-zero length when buffer is empty.");
|
|
|
|
printf(" -- PASS\n");
|
|
|
|
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
|
|
printf("\nTest 6: OutFrameRead() in parts\n");
|
|
|
|
ncpBuffer.InFrameBegin();
|
|
ncpBuffer.InFrameFeedData(sMottoText, sizeof(sMottoText));
|
|
ncpBuffer.InFrameEnd();
|
|
|
|
ncpBuffer.OutFrameBegin();
|
|
readOffset = 0;
|
|
|
|
while ((readLen = ncpBuffer.OutFrameRead(sizeof(readBuffer), readBuffer)) != 0)
|
|
{
|
|
DumpBuffer("Read() returned", readBuffer, readLen);
|
|
|
|
VerifyOrQuit(memcmp(readBuffer, sMottoText + readOffset, readLen) == 0,
|
|
"Read() does not match expected content.");
|
|
|
|
readOffset += readLen;
|
|
}
|
|
|
|
VerifyOrQuit(readOffset == sizeof(sMottoText), "Read len does not match expected length.");
|
|
printf("\n -- PASS\n");
|
|
|
|
testFreeInstance(sInstance);
|
|
}
|
|
|
|
/**
|
|
* NCP Buffer Fuzz testing
|
|
*
|
|
* Randomly decide if to read or write a frame to the NCP buffer (use `kReadProbability` in percent to control the
|
|
* behavior).
|
|
*
|
|
* When writing a frame, use a random length (1 up to `kMaxFrameLen`) and generate random byte sequences.
|
|
* When reading a frame ensure the length and the content matches what was written earlier.
|
|
* Handle the cases where buffer gets full or empty.
|
|
*
|
|
*/
|
|
|
|
enum
|
|
{
|
|
kFuzTestBufferSize = 2000, // Size of the buffer used during fuzz testing
|
|
kFuzTestIterationAttempts = 500000, // Number of iterations to run
|
|
kLensArraySize = 500, // Size of "Lengths" array.
|
|
kMaxFrameLen = 400, // Maximum frame length
|
|
kReadProbability = 50, // Probability (in percent) to randomly choose to read vs write frame
|
|
kUseTrueRandomNumberGenerator = 1, // To use true random number generator or not.
|
|
};
|
|
|
|
uint8_t sFrameBuffer[kFuzTestBufferSize];
|
|
uint32_t sFrameBufferTailIndex = 0;
|
|
|
|
uint32_t GetRandom(uint32_t max)
|
|
{
|
|
uint32_t value;
|
|
|
|
if (kUseTrueRandomNumberGenerator)
|
|
{
|
|
otPlatRandomGetTrue(reinterpret_cast<uint8_t *>(&value), sizeof(value));
|
|
}
|
|
else
|
|
{
|
|
value = otPlatRandomGet();
|
|
}
|
|
|
|
return value % max;
|
|
}
|
|
|
|
otError WriteRandomFrame(uint32_t aLength, NcpFrameBuffer &aNcpBuffer)
|
|
{
|
|
otError error;
|
|
uint8_t byte;
|
|
CallbackContext oldContext = sContext;
|
|
uint32_t tail = sFrameBufferTailIndex;
|
|
|
|
SuccessOrExit(error = aNcpBuffer.InFrameBegin());
|
|
|
|
while (aLength--)
|
|
{
|
|
byte = static_cast<uint8_t>(GetRandom(256));
|
|
SuccessOrExit(error = aNcpBuffer.InFrameFeedData(&byte, sizeof(byte)));
|
|
sFrameBuffer[tail++] = byte;
|
|
}
|
|
|
|
SuccessOrExit(error = aNcpBuffer.InFrameEnd());
|
|
|
|
sFrameBufferTailIndex = tail;
|
|
|
|
// check the callbacks
|
|
VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
|
|
VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
otError ReadRandomFrame(uint32_t aLength, NcpFrameBuffer &aNcpBuffer)
|
|
{
|
|
CallbackContext oldContext = sContext;
|
|
|
|
SuccessOrQuit(aNcpBuffer.OutFrameBegin(), "OutFrameBegin failed");
|
|
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == aLength, "OutFrameGetLength() does not match");
|
|
|
|
// Read and verify that the content is same as sFrameBuffer values...
|
|
ReadAndVerifyContent(aNcpBuffer, sFrameBuffer, static_cast<uint16_t>(aLength));
|
|
sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
|
|
|
|
SuccessOrQuit(aNcpBuffer.OutFrameRemove(), "OutFrameRemove failed");
|
|
|
|
sFrameBufferTailIndex -= aLength;
|
|
memmove(sFrameBuffer, sFrameBuffer + aLength, sFrameBufferTailIndex);
|
|
|
|
// If successful check the callbacks
|
|
VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
|
|
VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
|
|
// This runs a fuzz test of NCP buffer
|
|
void TestFuzzNcpFrameBuffer(void)
|
|
{
|
|
uint8_t buffer[kFuzTestBufferSize];
|
|
NcpFrameBuffer ncpBuffer(buffer, kFuzTestBufferSize);
|
|
|
|
uint32_t lensArray[kLensArraySize]; // Keeps track of length of written frames so far
|
|
uint32_t lensArrayStart;
|
|
uint32_t lensArrayCount;
|
|
|
|
sInstance = testInitInstance();
|
|
sMessagePool = &sInstance->mIp6.mMessagePool;
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
|
|
memset(lensArray, 0, sizeof(lensArray));
|
|
lensArrayStart = 0;
|
|
lensArrayCount = 0;
|
|
|
|
sContext.mFrameAddedCount = 0;
|
|
sContext.mFrameRemovedCount = 0;
|
|
ClearTagHistory();
|
|
|
|
ncpBuffer.SetFrameAddedCallback(FrameAddedCallback, &sContext);
|
|
ncpBuffer.SetFrameRemovedCallback(FrameRemovedCallback, &sContext);
|
|
|
|
for (uint32_t iter = 0; iter < kFuzTestIterationAttempts; iter++)
|
|
{
|
|
bool shouldRead;
|
|
|
|
if (lensArrayCount == 0)
|
|
{
|
|
shouldRead = false;
|
|
}
|
|
else if (lensArrayCount == kLensArraySize - 1)
|
|
{
|
|
shouldRead = true;
|
|
}
|
|
else
|
|
{
|
|
// Randomly decide to read or write.
|
|
shouldRead = (GetRandom(100) < kReadProbability);
|
|
}
|
|
|
|
if (shouldRead)
|
|
{
|
|
uint32_t len = lensArray[lensArrayStart];
|
|
|
|
lensArrayStart = (lensArrayStart + 1) % kLensArraySize;
|
|
lensArrayCount--;
|
|
|
|
printf("R%d ", len);
|
|
|
|
SuccessOrQuit(ReadRandomFrame(len, ncpBuffer), "Failed to read random frame.");
|
|
}
|
|
else
|
|
{
|
|
uint32_t len = GetRandom(kMaxFrameLen) + 1;
|
|
|
|
if (WriteRandomFrame(len, ncpBuffer) == OT_ERROR_NONE)
|
|
{
|
|
lensArray[(lensArrayStart + lensArrayCount) % kLensArraySize] = len;
|
|
lensArrayCount++;
|
|
|
|
printf("W%d ", len);
|
|
}
|
|
else
|
|
{
|
|
printf("FULL ");
|
|
}
|
|
}
|
|
|
|
if (lensArrayCount == 0)
|
|
{
|
|
VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty failed.");
|
|
printf("EMPTY ");
|
|
}
|
|
|
|
}
|
|
|
|
printf("\n -- PASS\n");
|
|
|
|
testFreeInstance(sInstance);
|
|
}
|
|
|
|
} // namespace ot
|
|
|
|
#ifdef ENABLE_TEST_MAIN
|
|
int main(void)
|
|
{
|
|
ot::TestNcpFrameBuffer();
|
|
ot::TestFuzzNcpFrameBuffer();
|
|
printf("\nAll tests passed.\n");
|
|
return 0;
|
|
}
|
|
#endif
|