Files
openthread/tests/unit/test_spinel_buffer.cpp
T
Abtin Keshavarzian 4de7bc578e [random] introduce template-based NonCrypto random APIs (#13142)
This commit introduces a new set of template-based APIs for
non-cryptographic random number generation in the `Random::NonCrypto`
namespace. These new methods provide a cleaner, type-safe, and more
robust interface compared to the previous methods.

Key additions:
- `Generate<UintType>()`: Returns a random value of the given
  unsigned integer type (`uint8_t`, `uint16_t`, or `uint32_t`).
- `GenerateUpToExcluding<UintType>(aMax)`: Returns a random value in
  the range `[0, aMax)`.
- `GenerateFromMinUpToExcluding<UintType>(aMin, aMax)`: Returns a
  random value in the range `[aMin, aMax)`.
- `GenerateInClosedRange<UintType>(aMin, aMax)`: Returns a random
  value in the closed range `[aMin, aMax]`.

The introduction of `GenerateInClosedRange` is an improvement as it
safely handles ranges up to the maximum value of the integer type
(e.g., `0xffff`) without the risk of overflow.

All call sites across the OpenThread core stack and tests have been
updated to adopt these new APIs. The public `otRandomNonCrypto`
functions are also updated to leverage the new internal methods.

Doxygen documentation is added for all new template methods,
detailing their behavior, including edge cases where the upper bound
is smaller than or equal to the lower bound.
2026-05-25 19:39:59 -07:00

1057 lines
44 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 "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:
constexpr uint16_t kTestBufferSize = 800;
constexpr uint16_t kTestIterationAttemps = 10000;
constexpr uint16_t 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;
constexpr uint8_t kNumPrios = 2; // Number of priority levels.
constexpr uint16_t kTestFrame1Size =
sizeof(sMottoText) + sizeof(sMysteryText) + sizeof(sMottoText) + sizeof(sHelloText);
constexpr uint16_t kTestFrame2Size = sizeof(sMysteryText) + sizeof(sHelloText) + sizeof(sOpenThreadText);
constexpr uint16_t kTestFrame3Size = sizeof(sMysteryText);
constexpr uint16_t 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.
*/
constexpr uint16_t kFuzTestBufferSize = 2000; // Size of the buffer used during fuzz testing
constexpr uint32_t kFuzTestIterationAttempts = 500000; // Number of iterations to run
constexpr uint16_t kLensArraySize = 500; // Size of "Lengths" array.
constexpr uint16_t kMaxFrameLen = 400; // Maximum frame length
constexpr uint8_t kReadProbability = 50; // Probability to randomly choose to read vs write frame
constexpr uint8_t kHighPriorityProbability = 20; // Probability to write a high priority frame
constexpr bool kUseTrueRandomNumberGenerator = true; // 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::Generate<uint32_t>();
}
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;
}