blob: 288b2fba11971bea7b1cc1978269592ffa4574ea [file] [log] [blame]
/*
* 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 "common/code_utils.hpp"
#include "common/message.hpp"
#include "common/random.hpp"
#include "instance/instance.hpp"
#include "lib/spinel/spinel_buffer.hpp"
#include "test_platform.h"
#include "test_util.hpp"
namespace ot {
namespace Spinel {
// This module implements unit-test for Spinel::Buffer class.
// Test related constants:
enum
{
kTestBufferSize = 800,
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 const uint8_t sHexText[] = "0123456789abcdef";
static Instance *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;
enum
{
kNumPrios = 2, // Number of priority levels.
kTestFrame1Size = sizeof(sMottoText) + sizeof(sMysteryText) + sizeof(sMottoText) + sizeof(sHelloText),
kTestFrame2Size = sizeof(sMysteryText) + sizeof(sHelloText) + sizeof(sOpenThreadText),
kTestFrame3Size = sizeof(sMysteryText),
kTestFrame4Size = sizeof(sOpenThreadText),
};
Spinel::Buffer::FrameTag sTagHistoryArray[kNumPrios][kTagArraySize];
uint32_t sTagHistoryHead[kNumPrios] = {0};
uint32_t sTagHistoryTail[kNumPrios] = {0};
Spinel::Buffer::FrameTag sExpectedRemovedTag = Spinel::Buffer::kInvalidTag;
void ClearTagHistory(void)
{
for (uint8_t priority = 0; priority < kNumPrios; priority++)
{
sTagHistoryHead[priority] = sTagHistoryTail[priority];
}
}
void AddTagToHistory(Spinel::Buffer::FrameTag aTag, Spinel::Buffer::Priority aPriority)
{
uint8_t priority = static_cast<uint8_t>(aPriority);
sTagHistoryArray[priority][sTagHistoryTail[priority]] = aTag;
if (++sTagHistoryTail[priority] == kTagArraySize)
{
sTagHistoryTail[priority] = 0;
}
VerifyOrQuit(sTagHistoryTail[priority] != sTagHistoryHead[priority],
"Ran out of space in `TagHistoryArray`, increase its size.");
}
void VerifyAndRemoveTagFromHistory(Spinel::Buffer::FrameTag aTag, Spinel::Buffer::Priority aPriority)
{
uint8_t priority = static_cast<uint8_t>(aPriority);
VerifyOrQuit(sTagHistoryHead[priority] != sTagHistoryTail[priority], "Tag history is empty,");
VerifyOrQuit(aTag == sTagHistoryArray[priority][sTagHistoryHead[priority]],
"Removed tag does not match the added one");
if (++sTagHistoryHead[priority] == kTagArraySize)
{
sTagHistoryHead[priority] = 0;
}
if (sExpectedRemovedTag != Spinel::Buffer::kInvalidTag)
{
VerifyOrQuit(sExpectedRemovedTag == aTag, "Removed tag does match the previous OutFrameGetTag()");
sExpectedRemovedTag = Spinel::Buffer::kInvalidTag;
}
}
void FrameAddedCallback(void *aContext,
Spinel::Buffer::FrameTag aTag,
Spinel::Buffer::Priority aPriority,
Spinel::Buffer *aNcpBuffer)
{
CallbackContext *callbackContext = reinterpret_cast<CallbackContext *>(aContext);
VerifyOrQuit(aNcpBuffer != nullptr, "Null Spinel::Buffer in the callback");
VerifyOrQuit(callbackContext != nullptr, "Null context in the callback");
VerifyOrQuit(aTag != Spinel::Buffer::kInvalidTag, "Invalid tag in the callback");
VerifyOrQuit(aTag == aNcpBuffer->InFrameGetLastTag(), "InFrameGetLastTag() does not match the tag from callback");
AddTagToHistory(aTag, aPriority);
callbackContext->mFrameAddedCount++;
}
void FrameRemovedCallback(void *aContext,
Spinel::Buffer::FrameTag aTag,
Spinel::Buffer::Priority aPriority,
Spinel::Buffer *aNcpBuffer)
{
CallbackContext *callbackContext = reinterpret_cast<CallbackContext *>(aContext);
VerifyOrQuit(aNcpBuffer != nullptr, "Null Spinel::Buffer in the callback");
VerifyOrQuit(callbackContext != nullptr, "Null context in the callback");
VerifyOrQuit(aTag != Spinel::Buffer::kInvalidTag, "Invalid tag in the callback");
VerifyAndRemoveTagFromHistory(aTag, aPriority);
callbackContext->mFrameRemovedCount++;
}
// Reads bytes from the ncp buffer, and verifies that it matches with the given content buffer.
void ReadAndVerifyContent(Spinel::Buffer &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(Spinel::Buffer &aNcpBuffer, Spinel::Buffer::Priority aPriority)
{
Message *message;
CallbackContext oldContext;
message = sMessagePool->Allocate(Message::kTypeIp6);
VerifyOrQuit(message != nullptr, "Null Message");
SuccessOrQuit(message->SetLength(sizeof(sMottoText)));
message->Write(0, sMottoText);
oldContext = sContext;
aNcpBuffer.InFrameBegin(aPriority);
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sMottoText, sizeof(sMottoText)));
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sMysteryText, sizeof(sMysteryText)));
SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message));
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sHelloText, sizeof(sHelloText)));
SuccessOrQuit(aNcpBuffer.InFrameEnd());
VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
}
void VerifyAndRemoveFrame1(Spinel::Buffer &aNcpBuffer)
{
CallbackContext oldContext = sContext;
sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame1Size);
SuccessOrQuit(aNcpBuffer.OutFrameBegin());
VerifyOrQuit(sExpectedRemovedTag == aNcpBuffer.OutFrameGetTag());
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame1Size);
ReadAndVerifyContent(aNcpBuffer, sMottoText, sizeof(sMottoText));
ReadAndVerifyContent(aNcpBuffer, sMysteryText, sizeof(sMysteryText));
ReadAndVerifyContent(aNcpBuffer, sMottoText, sizeof(sMottoText));
ReadAndVerifyContent(aNcpBuffer, sHelloText, sizeof(sHelloText));
VerifyOrQuit(aNcpBuffer.OutFrameHasEnded(), "Frame longer than expected.");
VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame.");
VerifyOrQuit(sExpectedRemovedTag == aNcpBuffer.OutFrameGetTag());
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame1Size);
SuccessOrQuit(aNcpBuffer.OutFrameRemove());
VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
}
void WriteTestFrame2(Spinel::Buffer &aNcpBuffer, Spinel::Buffer::Priority aPriority)
{
Message *message1;
Message *message2;
CallbackContext oldContext = sContext;
message1 = sMessagePool->Allocate(Message::kTypeIp6);
VerifyOrQuit(message1 != nullptr, "Null Message");
SuccessOrQuit(message1->SetLength(sizeof(sMysteryText)));
message1->Write(0, sMysteryText);
message2 = sMessagePool->Allocate(Message::kTypeIp6);
VerifyOrQuit(message2 != nullptr, "Null Message");
SuccessOrQuit(message2->SetLength(sizeof(sHelloText)));
message2->Write(0, sHelloText);
aNcpBuffer.InFrameBegin(aPriority);
SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message1));
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)));
SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message2));
SuccessOrQuit(aNcpBuffer.InFrameEnd());
VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
}
void VerifyAndRemoveFrame2(Spinel::Buffer &aNcpBuffer)
{
CallbackContext oldContext = sContext;
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame2Size);
SuccessOrQuit(aNcpBuffer.OutFrameBegin());
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame2Size);
ReadAndVerifyContent(aNcpBuffer, sMysteryText, sizeof(sMysteryText));
ReadAndVerifyContent(aNcpBuffer, sOpenThreadText, sizeof(sOpenThreadText));
ReadAndVerifyContent(aNcpBuffer, sHelloText, sizeof(sHelloText));
VerifyOrQuit(aNcpBuffer.OutFrameHasEnded(), "Frame longer than expected.");
VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame.");
sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame2Size);
SuccessOrQuit(aNcpBuffer.OutFrameRemove());
VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
}
void WriteTestFrame3(Spinel::Buffer &aNcpBuffer, Spinel::Buffer::Priority aPriority)
{
Message *message1;
CallbackContext oldContext = sContext;
message1 = sMessagePool->Allocate(Message::kTypeIp6);
VerifyOrQuit(message1 != nullptr, "Null Message");
// An empty message with no content.
SuccessOrQuit(message1->SetLength(0));
aNcpBuffer.InFrameBegin(aPriority);
SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message1));
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sMysteryText, sizeof(sMysteryText)));
SuccessOrQuit(aNcpBuffer.InFrameEnd());
VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
}
void VerifyAndRemoveFrame3(Spinel::Buffer &aNcpBuffer)
{
CallbackContext oldContext = sContext;
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText));
SuccessOrQuit(aNcpBuffer.OutFrameBegin());
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText));
ReadAndVerifyContent(aNcpBuffer, sMysteryText, sizeof(sMysteryText));
VerifyOrQuit(aNcpBuffer.OutFrameHasEnded(), "Frame longer than expected.");
VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame.");
sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText));
SuccessOrQuit(aNcpBuffer.OutFrameRemove());
VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
}
void WriteTestFrame4(Spinel::Buffer &aNcpBuffer, Spinel::Buffer::Priority aPriority)
{
CallbackContext oldContext = sContext;
aNcpBuffer.InFrameBegin(aPriority);
SuccessOrQuit(aNcpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)));
SuccessOrQuit(aNcpBuffer.InFrameEnd());
VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
}
void VerifyAndRemoveFrame4(Spinel::Buffer &aNcpBuffer)
{
CallbackContext oldContext = sContext;
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sOpenThreadText));
SuccessOrQuit(aNcpBuffer.OutFrameBegin());
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sOpenThreadText));
ReadAndVerifyContent(aNcpBuffer, sOpenThreadText, sizeof(sOpenThreadText));
VerifyOrQuit(aNcpBuffer.OutFrameHasEnded(), "Frame longer than expected.");
VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame.");
sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sOpenThreadText));
SuccessOrQuit(aNcpBuffer.OutFrameRemove());
VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
}
// This function implements the Spinel::Buffer tests
void TestBuffer(void)
{
unsigned i, j;
uint8_t buffer[kTestBufferSize];
Spinel::Buffer ncpBuffer(buffer, kTestBufferSize);
Message *message;
uint8_t readBuffer[16];
uint16_t readLen, readOffset;
Spinel::Buffer::WritePosition pos1, pos2;
sInstance = testInitInstance();
sMessagePool = &sInstance->Get<MessagePool>();
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(), "Not empty after init.");
VerifyOrQuit(ncpBuffer.InFrameGetLastTag() == Spinel::Buffer::kInvalidTag, "Incorrect tag after init.");
VerifyOrQuit(ncpBuffer.OutFrameGetTag() == Spinel::Buffer::kInvalidTag, "Incorrect OutFrameTag after init.");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 2: Write and read a single frame");
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
DumpBuffer("\nBuffer after frame1 (low priority)", buffer, kTestBufferSize);
printf("\nFrameLen is %u", ncpBuffer.OutFrameGetLength());
VerifyAndRemoveFrame1(ncpBuffer);
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
DumpBuffer("\nBuffer after frame1 (high priority)", buffer, kTestBufferSize);
printf("\nFrameLen is %u", ncpBuffer.OutFrameGetLength());
VerifyAndRemoveFrame1(ncpBuffer);
printf("\nIterations: ");
// Always add as low priority.
for (j = 0; j < kTestIterationAttemps; j++)
{
printf("*");
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.IsEmpty() == false, "IsEmpty() is incorrect when buffer is non-empty");
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() is incorrect when buffer is empty.");
}
// Always add as high priority.
for (j = 0; j < kTestIterationAttemps; j++)
{
printf("*");
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
VerifyOrQuit(ncpBuffer.IsEmpty() == false, "IsEmpty() is incorrect when buffer is non-empty");
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() is incorrect when buffer is empty.");
}
// Every 5th add as high priority.
for (j = 0; j < kTestIterationAttemps; j++)
{
printf("*");
WriteTestFrame1(ncpBuffer, ((j % 5) == 0) ? Spinel::Buffer::kPriorityHigh : Spinel::Buffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.IsEmpty() == false, "IsEmpty() is incorrect when buffer is non-empty");
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() is incorrect when buffer is empty.");
}
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 3: Multiple frames write and read (same priority)");
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
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, Spinel::Buffer::kPriorityLow);
WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame3(ncpBuffer);
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityLow);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame3(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() is incorrect when buffer is empty.");
}
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 4: Multiple frames write and read (mixed priority)");
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityHigh);
VerifyAndRemoveFrame3(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityHigh);
WriteTestFrame4(ncpBuffer, Spinel::Buffer::kPriorityHigh);
VerifyAndRemoveFrame3(ncpBuffer);
VerifyAndRemoveFrame4(ncpBuffer);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityHigh);
WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame4(ncpBuffer, Spinel::Buffer::kPriorityHigh);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame4(ncpBuffer);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame3(ncpBuffer);
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityHigh);
WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame4(ncpBuffer, Spinel::Buffer::kPriorityHigh);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame4(ncpBuffer);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame3(ncpBuffer);
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityHigh);
WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame4(ncpBuffer, Spinel::Buffer::kPriorityLow);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame3(ncpBuffer);
VerifyAndRemoveFrame4(ncpBuffer);
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityHigh);
WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityHigh);
VerifyAndRemoveFrame2(ncpBuffer);
WriteTestFrame4(ncpBuffer, Spinel::Buffer::kPriorityHigh);
VerifyAndRemoveFrame3(ncpBuffer);
VerifyAndRemoveFrame4(ncpBuffer);
VerifyAndRemoveFrame1(ncpBuffer);
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 5: Frame discard when buffer full and partial read restart");
printf("\nIterations: ");
for (j = 0; j < kTestIterationAttemps; j++)
{
bool frame1IsHighPriority = ((j % 3) == 0);
printf("*");
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityHigh);
ncpBuffer.InFrameBegin((j % 2) == 0 ? Spinel::Buffer::kPriorityHigh : Spinel::Buffer::kPriorityLow);
SuccessOrQuit(ncpBuffer.InFrameFeedData(sHelloText, sizeof(sHelloText)));
message = sMessagePool->Allocate(Message::kTypeIp6);
VerifyOrQuit(message != nullptr, "Null Message");
SuccessOrQuit(message->SetLength(sizeof(sMysteryText)));
message->Write(0, sMysteryText);
SuccessOrQuit(ncpBuffer.InFrameFeedMessage(message));
// Start writing a new frame in middle of an unfinished frame. Ensure the first one is discarded.
WriteTestFrame1(ncpBuffer, frame1IsHighPriority ? Spinel::Buffer::kPriorityHigh : Spinel::Buffer::kPriorityLow);
// Note that message will not be freed by the NCP buffer since the frame associated with it was discarded and
// not yet finished/ended.
otMessageFree(message);
VerifyAndRemoveFrame3(ncpBuffer);
// Start reading few bytes from the frame
SuccessOrQuit(ncpBuffer.OutFrameBegin());
ncpBuffer.OutFrameReadByte();
ncpBuffer.OutFrameReadByte();
ncpBuffer.OutFrameReadByte();
// Now reset the read pointer and read/verify the frame from start.
if (frame1IsHighPriority)
{
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
}
else
{
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame1(ncpBuffer);
}
VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() is incorrect when buffer is empty.");
}
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 6: Clear() and empty buffer method tests");
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
ncpBuffer.Clear();
ClearTagHistory();
VerifyOrQuit(ncpBuffer.InFrameGetLastTag() == Spinel::Buffer::kInvalidTag, "Incorrect last tag after Clear().");
VerifyOrQuit(ncpBuffer.OutFrameGetTag() == Spinel::Buffer::kInvalidTag, "Incorrect OutFrameTag after Clear().");
VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() is incorrect when buffer is empty.");
VerifyOrQuit(ncpBuffer.OutFrameHasEnded(), "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, Spinel::Buffer::kPriorityLow);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() is incorrect when buffer is empty.");
VerifyOrQuit(ncpBuffer.OutFrameHasEnded(), "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 7: OutFrameRead() in parts\n");
ncpBuffer.InFrameBegin(Spinel::Buffer::kPriorityLow);
SuccessOrQuit(ncpBuffer.InFrameFeedData(sMottoText, sizeof(sMottoText)));
SuccessOrQuit(ncpBuffer.InFrameEnd());
SuccessOrQuit(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.");
SuccessOrQuit(ncpBuffer.OutFrameRemove());
printf("\n -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 8: Remove a frame without reading it first");
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
SuccessOrQuit(ncpBuffer.OutFrameRemove());
VerifyAndRemoveFrame2(ncpBuffer);
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 9: Check length when front frame gets changed (a higher priority frame is added)");
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityHigh);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame3Size);
VerifyAndRemoveFrame3(ncpBuffer);
VerifyAndRemoveFrame1(ncpBuffer);
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nTest 10: Active out frame remaining unchanged when a higher priority frame is written while reading it");
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
SuccessOrQuit(ncpBuffer.OutFrameBegin());
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityHigh);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText));
SuccessOrQuit(ncpBuffer.OutFrameBegin());
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText));
ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
ReadAndVerifyContent(ncpBuffer, sHelloText, sizeof(sHelloText));
VerifyOrQuit(ncpBuffer.OutFrameHasEnded(), "Frame longer than expected.");
WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityHigh);
WriteTestFrame4(ncpBuffer, Spinel::Buffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame3(ncpBuffer);
VerifyAndRemoveFrame4(ncpBuffer);
// Repeat test reversing frame priority orders.
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
SuccessOrQuit(ncpBuffer.OutFrameBegin());
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText));
SuccessOrQuit(ncpBuffer.OutFrameBegin());
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText));
ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
ReadAndVerifyContent(ncpBuffer, sHelloText, sizeof(sHelloText));
VerifyOrQuit(ncpBuffer.OutFrameHasEnded(), "Frame longer than expected.");
WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityHigh);
WriteTestFrame4(ncpBuffer, Spinel::Buffer::kPriorityLow);
VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame3(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame4(ncpBuffer);
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\n Test 11: Read and remove in middle of an active input frame write");
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
ncpBuffer.InFrameBegin(Spinel::Buffer::kPriorityHigh);
SuccessOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)));
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty());
SuccessOrQuit(ncpBuffer.InFrameEnd());
VerifyAndRemoveFrame4(ncpBuffer);
// Repeat the test reversing priorities
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
ncpBuffer.InFrameBegin(Spinel::Buffer::kPriorityLow);
SuccessOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)));
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty());
SuccessOrQuit(ncpBuffer.InFrameEnd());
VerifyAndRemoveFrame4(ncpBuffer);
// Repeat the test with same priorities
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
ncpBuffer.InFrameBegin(Spinel::Buffer::kPriorityHigh);
SuccessOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)));
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty());
SuccessOrQuit(ncpBuffer.InFrameEnd());
VerifyAndRemoveFrame4(ncpBuffer);
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\n Test 12: Check returned error status");
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
ncpBuffer.InFrameBegin(Spinel::Buffer::kPriorityHigh);
VerifyOrQuit(ncpBuffer.InFrameFeedData(buffer, sizeof(buffer)) == OT_ERROR_NO_BUFS);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty());
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityHigh);
// Ensure writes with starting `InFrameBegin()` fail
VerifyOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, 1) == OT_ERROR_INVALID_STATE);
VerifyOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, 0) == OT_ERROR_INVALID_STATE);
VerifyOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, 0) == OT_ERROR_INVALID_STATE);
VerifyOrQuit(ncpBuffer.InFrameEnd() == OT_ERROR_INVALID_STATE);
message = sMessagePool->Allocate(Message::kTypeIp6);
VerifyOrQuit(message != nullptr, "Null Message");
SuccessOrQuit(message->SetLength(sizeof(sMysteryText)));
message->Write(0, sMysteryText);
VerifyOrQuit(ncpBuffer.InFrameFeedMessage(message) == OT_ERROR_INVALID_STATE);
message->Free();
VerifyOrQuit(ncpBuffer.InFrameEnd() == OT_ERROR_INVALID_STATE);
VerifyAndRemoveFrame2(ncpBuffer);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty());
VerifyOrQuit(ncpBuffer.OutFrameBegin() == OT_ERROR_NOT_FOUND, "OutFrameBegin() failed on empty queue");
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyOrQuit(ncpBuffer.IsEmpty());
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\n Test 13: Ensure we can utilize the full buffer size when frames removed during write");
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
ncpBuffer.InFrameBegin(Spinel::Buffer::kPriorityHigh);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
SuccessOrQuit(ncpBuffer.InFrameFeedData(buffer, sizeof(buffer) - 4));
SuccessOrQuit(ncpBuffer.InFrameEnd());
SuccessOrQuit(ncpBuffer.OutFrameRemove());
// Repeat the test with a low priority buffer write
WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
ncpBuffer.InFrameBegin(Spinel::Buffer::kPriorityLow);
VerifyAndRemoveFrame1(ncpBuffer);
VerifyAndRemoveFrame2(ncpBuffer);
SuccessOrQuit(ncpBuffer.InFrameFeedData(buffer, sizeof(buffer) - 4));
SuccessOrQuit(ncpBuffer.InFrameEnd());
SuccessOrQuit(ncpBuffer.OutFrameRemove());
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\n Test 14: Test InFrameOverwrite ");
printf("\nIterations: ");
for (j = 0; j < kTestIterationAttemps; j++)
{
uint16_t index;
bool addExtra = ((j % 7) != 0);
Spinel::Buffer::Priority priority;
printf("*");
priority = ((j % 3) == 0) ? Spinel::Buffer::kPriorityHigh : Spinel::Buffer::kPriorityLow;
index = static_cast<uint16_t>(j % sizeof(sHexText));
ncpBuffer.InFrameBegin(priority);
SuccessOrQuit(ncpBuffer.InFrameFeedData(sHexText, index));
SuccessOrQuit(ncpBuffer.InFrameGetPosition(pos1));
SuccessOrQuit(ncpBuffer.InFrameFeedData(sMysteryText, sizeof(sHexText) - index));
VerifyOrQuit(ncpBuffer.InFrameGetDistance(pos1) == sizeof(sHexText) - index);
if (addExtra)
{
SuccessOrQuit(ncpBuffer.InFrameFeedData(sHelloText, sizeof(sHelloText)));
}
SuccessOrQuit(ncpBuffer.InFrameOverwrite(pos1, sHexText + index, sizeof(sHexText) - index),
"InFrameOverwrite() failed.");
VerifyOrQuit(ncpBuffer.InFrameGetDistance(pos1) ==
sizeof(sHexText) - index + (addExtra ? sizeof(sHelloText) : 0),
"InFrameGetDistance() failed");
SuccessOrQuit(ncpBuffer.InFrameEnd());
VerifyOrQuit(ncpBuffer.InFrameGetPosition(pos2) == OT_ERROR_INVALID_STATE);
VerifyOrQuit(ncpBuffer.InFrameOverwrite(pos1, sHexText, 0) != OT_ERROR_NONE);
SuccessOrQuit(ncpBuffer.OutFrameBegin());
ReadAndVerifyContent(ncpBuffer, sHexText, sizeof(sHexText));
if (addExtra)
{
ReadAndVerifyContent(ncpBuffer, sHelloText, sizeof(sHelloText));
}
SuccessOrQuit(ncpBuffer.OutFrameRemove());
VerifyOrQuit(ncpBuffer.InFrameGetPosition(pos2) == OT_ERROR_INVALID_STATE);
}
printf(" -- PASS\n");
printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\n Test 15: Test InFrameReset()");
printf("\nIterations: ");
for (j = 0; j < kTestIterationAttemps; j++)
{
uint16_t index;
bool addExtra = ((j % 7) != 0);
Spinel::Buffer::Priority priority;
printf("*");
priority = ((j % 3) == 0) ? Spinel::Buffer::kPriorityHigh : Spinel::Buffer::kPriorityLow;
index = static_cast<uint16_t>(j % sizeof(sHexText));
ncpBuffer.InFrameBegin(priority);
SuccessOrQuit(ncpBuffer.InFrameFeedData(sHexText, index));
SuccessOrQuit(ncpBuffer.InFrameGetPosition(pos1));
SuccessOrQuit(ncpBuffer.InFrameFeedData(sMysteryText, sizeof(sHexText) - index));
VerifyOrQuit(ncpBuffer.InFrameGetDistance(pos1) == sizeof(sHexText) - index);
if (addExtra)
{
SuccessOrQuit(ncpBuffer.InFrameFeedData(sHelloText, sizeof(sHelloText)));
}
SuccessOrQuit(ncpBuffer.InFrameReset(pos1));
SuccessOrQuit(ncpBuffer.InFrameFeedData(sHexText + index, sizeof(sHexText) - index));
if (addExtra)
{
SuccessOrQuit(ncpBuffer.InFrameReset(pos1));
SuccessOrQuit(ncpBuffer.InFrameFeedData(sHexText + index, sizeof(sHexText) - index));
}
VerifyOrQuit(ncpBuffer.InFrameGetDistance(pos1) == sizeof(sHexText) - index);
SuccessOrQuit(ncpBuffer.InFrameEnd());
SuccessOrQuit(ncpBuffer.OutFrameBegin());
ReadAndVerifyContent(ncpBuffer, sHexText, sizeof(sHexText));
SuccessOrQuit(ncpBuffer.OutFrameRemove());
}
printf(" -- 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
kHighPriorityProbability = 20, // Probability (in percent) to write a high priority frame
kUseTrueRandomNumberGenerator = 1, // To use true random number generator or not.
};
uint8_t sFrameBuffer[kNumPrios][kFuzTestBufferSize];
uint32_t sFrameBufferTailIndex[kNumPrios] = {0};
uint32_t GetRandom(uint32_t max)
{
uint32_t value;
if (kUseTrueRandomNumberGenerator)
{
SuccessOrQuit(Random::Crypto::Fill(value));
}
else
{
value = Random::NonCrypto::GetUint32();
}
return value % max;
}
otError WriteRandomFrame(uint32_t aLength, Spinel::Buffer &aNcpBuffer, Spinel::Buffer::Priority aPriority)
{
otError error;
uint8_t byte;
uint8_t priority = static_cast<uint8_t>(aPriority);
CallbackContext oldContext = sContext;
uint32_t tail = sFrameBufferTailIndex[priority];
aNcpBuffer.InFrameBegin(aPriority);
while (aLength--)
{
byte = static_cast<uint8_t>(GetRandom(256));
SuccessOrExit(error = aNcpBuffer.InFrameFeedData(&byte, sizeof(byte)));
sFrameBuffer[priority][tail++] = byte;
}
SuccessOrExit(error = aNcpBuffer.InFrameEnd());
sFrameBufferTailIndex[priority] = 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, Spinel::Buffer &aNcpBuffer, uint8_t priority)
{
CallbackContext oldContext = sContext;
SuccessOrQuit(aNcpBuffer.OutFrameBegin());
VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == aLength);
// Read and verify that the content is same as sFrameBuffer values...
ReadAndVerifyContent(aNcpBuffer, sFrameBuffer[priority], static_cast<uint16_t>(aLength));
sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
SuccessOrQuit(aNcpBuffer.OutFrameRemove());
sFrameBufferTailIndex[priority] -= aLength;
memmove(sFrameBuffer[priority], sFrameBuffer[priority] + aLength, sFrameBufferTailIndex[priority]);
// 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 TestFuzzBuffer(void)
{
uint8_t buffer[kFuzTestBufferSize];
Spinel::Buffer ncpBuffer(buffer, kFuzTestBufferSize);
uint32_t lensArray[kNumPrios][kLensArraySize]; // Keeps track of length of written frames so far
uint32_t lensArrayStart[kNumPrios];
uint32_t lensArrayCount[kNumPrios];
sInstance = testInitInstance();
sMessagePool = &sInstance->Get<MessagePool>();
memset(buffer, 0, sizeof(buffer));
memset(lensArray, 0, sizeof(lensArray));
memset(lensArrayStart, 0, sizeof(lensArrayStart));
memset(lensArrayCount, 0, sizeof(lensArrayCount));
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] == 0 && lensArrayCount[1] == 0)
{
shouldRead = false;
}
else if (lensArrayCount[0] == kLensArraySize - 1 || lensArrayCount[1] == kLensArraySize - 1)
{
shouldRead = true;
}
else
{
// Randomly decide to read or write.
shouldRead = (GetRandom(100) < kReadProbability);
}
if (shouldRead)
{
uint32_t len;
uint8_t priority;
priority = (lensArrayCount[Spinel::Buffer::kPriorityHigh] != 0) ? Spinel::Buffer::kPriorityHigh
: Spinel::Buffer::kPriorityLow;
len = lensArray[priority][lensArrayStart[priority]];
lensArrayStart[priority] = (lensArrayStart[priority] + 1) % kLensArraySize;
lensArrayCount[priority]--;
printf("R%c%d ", priority == Spinel::Buffer::kPriorityHigh ? 'H' : 'L', len);
SuccessOrQuit(ReadRandomFrame(len, ncpBuffer, priority), "Failed to read random frame.");
}
else
{
uint32_t len = GetRandom(kMaxFrameLen) + 1;
Spinel::Buffer::Priority priority;
if (GetRandom(100) < kHighPriorityProbability)
{
priority = Spinel::Buffer::kPriorityHigh;
}
else
{
priority = Spinel::Buffer::kPriorityLow;
}
if (WriteRandomFrame(len, ncpBuffer, priority) == OT_ERROR_NONE)
{
lensArray[priority][(lensArrayStart[priority] + lensArrayCount[priority]) % kLensArraySize] = len;
lensArrayCount[priority]++;
printf("W%c%d ", priority == Spinel::Buffer::kPriorityHigh ? 'H' : 'L', len);
}
else
{
printf("FULL ");
}
}
if (lensArrayCount[0] == 0 && lensArrayCount[1] == 0)
{
VerifyOrQuit(ncpBuffer.IsEmpty());
printf("EMPTY ");
}
}
printf("\n -- PASS\n");
testFreeInstance(sInstance);
}
} // namespace Spinel
} // namespace ot
int main(void)
{
ot::Spinel::TestBuffer();
ot::Spinel::TestFuzzBuffer();
printf("\nAll tests passed.\n");
return 0;
}