/* * 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 #include #include "openthread-instance.h" #include "common/code_utils.hpp" #include "common/message.hpp" #include "ncp/ncp_buffer.hpp" #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(&sInstance); 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(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(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(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; 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"); } /** * 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(&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(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(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; 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"); } } // namespace ot #ifdef ENABLE_TEST_MAIN int main(void) { ot::TestNcpFrameBuffer(); ot::TestFuzzNcpFrameBuffer(); printf("\nAll tests passed.\n"); return 0; } #endif