1 /*
2  *  Copyright (c) 2016, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <ctype.h>
30 
31 #include "common/code_utils.hpp"
32 #include "common/message.hpp"
33 #include "common/random.hpp"
34 #include "instance/instance.hpp"
35 #include "lib/spinel/spinel_buffer.hpp"
36 
37 #include "test_platform.h"
38 #include "test_util.hpp"
39 
40 namespace ot {
41 namespace Spinel {
42 
43 // This module implements unit-test for Spinel::Buffer class.
44 
45 // Test related constants:
46 enum
47 {
48     kTestBufferSize       = 800,
49     kTestIterationAttemps = 10000,
50     kTagArraySize         = 1000,
51 };
52 
53 //  Messages used for building frames...
54 static const uint8_t sOpenThreadText[] = "OpenThread Rocks";
55 static const uint8_t sHelloText[]      = "Hello there!";
56 static const uint8_t sMottoText[]      = "Think good thoughts, say good words, do good deeds!";
57 static const uint8_t sMysteryText[]    = "4871(\\):|(3$}{4|/4/2%14(\\)";
58 static const uint8_t sHexText[]        = "0123456789abcdef";
59 
60 static Instance    *sInstance;
61 static MessagePool *sMessagePool;
62 
63 struct CallbackContext
64 {
65     uint32_t mFrameAddedCount;   // Number of times FrameAddedCallback is invoked.
66     uint32_t mFrameRemovedCount; // Number of times FrameRemovedCallback is invoked.
67 };
68 
69 CallbackContext sContext;
70 
71 enum
72 {
73     kNumPrios = 2, // Number of priority levels.
74 
75     kTestFrame1Size = sizeof(sMottoText) + sizeof(sMysteryText) + sizeof(sMottoText) + sizeof(sHelloText),
76     kTestFrame2Size = sizeof(sMysteryText) + sizeof(sHelloText) + sizeof(sOpenThreadText),
77     kTestFrame3Size = sizeof(sMysteryText),
78     kTestFrame4Size = sizeof(sOpenThreadText),
79 };
80 
81 Spinel::Buffer::FrameTag sTagHistoryArray[kNumPrios][kTagArraySize];
82 uint32_t                 sTagHistoryHead[kNumPrios] = {0};
83 uint32_t                 sTagHistoryTail[kNumPrios] = {0};
84 Spinel::Buffer::FrameTag sExpectedRemovedTag        = Spinel::Buffer::kInvalidTag;
85 
ClearTagHistory(void)86 void ClearTagHistory(void)
87 {
88     for (uint8_t priority = 0; priority < kNumPrios; priority++)
89     {
90         sTagHistoryHead[priority] = sTagHistoryTail[priority];
91     }
92 }
93 
AddTagToHistory(Spinel::Buffer::FrameTag aTag,Spinel::Buffer::Priority aPriority)94 void AddTagToHistory(Spinel::Buffer::FrameTag aTag, Spinel::Buffer::Priority aPriority)
95 {
96     uint8_t priority = static_cast<uint8_t>(aPriority);
97 
98     sTagHistoryArray[priority][sTagHistoryTail[priority]] = aTag;
99 
100     if (++sTagHistoryTail[priority] == kTagArraySize)
101     {
102         sTagHistoryTail[priority] = 0;
103     }
104 
105     VerifyOrQuit(sTagHistoryTail[priority] != sTagHistoryHead[priority],
106                  "Ran out of space in `TagHistoryArray`, increase its size.");
107 }
108 
VerifyAndRemoveTagFromHistory(Spinel::Buffer::FrameTag aTag,Spinel::Buffer::Priority aPriority)109 void VerifyAndRemoveTagFromHistory(Spinel::Buffer::FrameTag aTag, Spinel::Buffer::Priority aPriority)
110 {
111     uint8_t priority = static_cast<uint8_t>(aPriority);
112 
113     VerifyOrQuit(sTagHistoryHead[priority] != sTagHistoryTail[priority], "Tag history is empty,");
114     VerifyOrQuit(aTag == sTagHistoryArray[priority][sTagHistoryHead[priority]],
115                  "Removed tag does not match the added one");
116 
117     if (++sTagHistoryHead[priority] == kTagArraySize)
118     {
119         sTagHistoryHead[priority] = 0;
120     }
121 
122     if (sExpectedRemovedTag != Spinel::Buffer::kInvalidTag)
123     {
124         VerifyOrQuit(sExpectedRemovedTag == aTag, "Removed tag does match the previous OutFrameGetTag()");
125         sExpectedRemovedTag = Spinel::Buffer::kInvalidTag;
126     }
127 }
128 
FrameAddedCallback(void * aContext,Spinel::Buffer::FrameTag aTag,Spinel::Buffer::Priority aPriority,Spinel::Buffer * aNcpBuffer)129 void FrameAddedCallback(void                    *aContext,
130                         Spinel::Buffer::FrameTag aTag,
131                         Spinel::Buffer::Priority aPriority,
132                         Spinel::Buffer          *aNcpBuffer)
133 {
134     CallbackContext *callbackContext = reinterpret_cast<CallbackContext *>(aContext);
135 
136     VerifyOrQuit(aNcpBuffer != nullptr, "Null Spinel::Buffer in the callback");
137     VerifyOrQuit(callbackContext != nullptr, "Null context in the callback");
138     VerifyOrQuit(aTag != Spinel::Buffer::kInvalidTag, "Invalid tag in the callback");
139     VerifyOrQuit(aTag == aNcpBuffer->InFrameGetLastTag(), "InFrameGetLastTag() does not match the tag from callback");
140 
141     AddTagToHistory(aTag, aPriority);
142 
143     callbackContext->mFrameAddedCount++;
144 }
145 
FrameRemovedCallback(void * aContext,Spinel::Buffer::FrameTag aTag,Spinel::Buffer::Priority aPriority,Spinel::Buffer * aNcpBuffer)146 void FrameRemovedCallback(void                    *aContext,
147                           Spinel::Buffer::FrameTag aTag,
148                           Spinel::Buffer::Priority aPriority,
149                           Spinel::Buffer          *aNcpBuffer)
150 {
151     CallbackContext *callbackContext = reinterpret_cast<CallbackContext *>(aContext);
152 
153     VerifyOrQuit(aNcpBuffer != nullptr, "Null Spinel::Buffer in the callback");
154     VerifyOrQuit(callbackContext != nullptr, "Null context in the callback");
155     VerifyOrQuit(aTag != Spinel::Buffer::kInvalidTag, "Invalid tag in the callback");
156 
157     VerifyAndRemoveTagFromHistory(aTag, aPriority);
158 
159     callbackContext->mFrameRemovedCount++;
160 }
161 
162 // Reads bytes from the ncp buffer, and verifies that it matches with the given content buffer.
ReadAndVerifyContent(Spinel::Buffer & aNcpBuffer,const uint8_t * aContentBuffer,uint16_t aBufferLength)163 void ReadAndVerifyContent(Spinel::Buffer &aNcpBuffer, const uint8_t *aContentBuffer, uint16_t aBufferLength)
164 {
165     while (aBufferLength--)
166     {
167         VerifyOrQuit(aNcpBuffer.OutFrameHasEnded() == false, "Out frame ended before end of expected content.");
168 
169         VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == *aContentBuffer++,
170                      "Out frame read byte does not match expected content");
171     }
172 }
173 
WriteTestFrame1(Spinel::Buffer & aNcpBuffer,Spinel::Buffer::Priority aPriority)174 void WriteTestFrame1(Spinel::Buffer &aNcpBuffer, Spinel::Buffer::Priority aPriority)
175 {
176     Message        *message;
177     CallbackContext oldContext;
178 
179     message = sMessagePool->Allocate(Message::kTypeIp6);
180     VerifyOrQuit(message != nullptr, "Null Message");
181     SuccessOrQuit(message->SetLength(sizeof(sMottoText)));
182     message->Write(0, sMottoText);
183 
184     oldContext = sContext;
185     aNcpBuffer.InFrameBegin(aPriority);
186     SuccessOrQuit(aNcpBuffer.InFrameFeedData(sMottoText, sizeof(sMottoText)));
187     SuccessOrQuit(aNcpBuffer.InFrameFeedData(sMysteryText, sizeof(sMysteryText)));
188     SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message));
189     SuccessOrQuit(aNcpBuffer.InFrameFeedData(sHelloText, sizeof(sHelloText)));
190     SuccessOrQuit(aNcpBuffer.InFrameEnd());
191     VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
192     VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
193 }
194 
VerifyAndRemoveFrame1(Spinel::Buffer & aNcpBuffer)195 void VerifyAndRemoveFrame1(Spinel::Buffer &aNcpBuffer)
196 {
197     CallbackContext oldContext = sContext;
198 
199     sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
200     VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame1Size);
201     SuccessOrQuit(aNcpBuffer.OutFrameBegin());
202     VerifyOrQuit(sExpectedRemovedTag == aNcpBuffer.OutFrameGetTag());
203     VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame1Size);
204     ReadAndVerifyContent(aNcpBuffer, sMottoText, sizeof(sMottoText));
205     ReadAndVerifyContent(aNcpBuffer, sMysteryText, sizeof(sMysteryText));
206     ReadAndVerifyContent(aNcpBuffer, sMottoText, sizeof(sMottoText));
207     ReadAndVerifyContent(aNcpBuffer, sHelloText, sizeof(sHelloText));
208     VerifyOrQuit(aNcpBuffer.OutFrameHasEnded(), "Frame longer than expected.");
209     VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame.");
210     VerifyOrQuit(sExpectedRemovedTag == aNcpBuffer.OutFrameGetTag());
211     VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame1Size);
212     SuccessOrQuit(aNcpBuffer.OutFrameRemove());
213     VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
214     VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
215 }
216 
WriteTestFrame2(Spinel::Buffer & aNcpBuffer,Spinel::Buffer::Priority aPriority)217 void WriteTestFrame2(Spinel::Buffer &aNcpBuffer, Spinel::Buffer::Priority aPriority)
218 {
219     Message        *message1;
220     Message        *message2;
221     CallbackContext oldContext = sContext;
222 
223     message1 = sMessagePool->Allocate(Message::kTypeIp6);
224     VerifyOrQuit(message1 != nullptr, "Null Message");
225     SuccessOrQuit(message1->SetLength(sizeof(sMysteryText)));
226     message1->Write(0, sMysteryText);
227 
228     message2 = sMessagePool->Allocate(Message::kTypeIp6);
229     VerifyOrQuit(message2 != nullptr, "Null Message");
230     SuccessOrQuit(message2->SetLength(sizeof(sHelloText)));
231     message2->Write(0, sHelloText);
232 
233     aNcpBuffer.InFrameBegin(aPriority);
234     SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message1));
235     SuccessOrQuit(aNcpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)));
236     SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message2));
237     SuccessOrQuit(aNcpBuffer.InFrameEnd());
238 
239     VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
240     VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
241 }
242 
VerifyAndRemoveFrame2(Spinel::Buffer & aNcpBuffer)243 void VerifyAndRemoveFrame2(Spinel::Buffer &aNcpBuffer)
244 {
245     CallbackContext oldContext = sContext;
246 
247     VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame2Size);
248     SuccessOrQuit(aNcpBuffer.OutFrameBegin());
249     VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame2Size);
250     ReadAndVerifyContent(aNcpBuffer, sMysteryText, sizeof(sMysteryText));
251     ReadAndVerifyContent(aNcpBuffer, sOpenThreadText, sizeof(sOpenThreadText));
252     ReadAndVerifyContent(aNcpBuffer, sHelloText, sizeof(sHelloText));
253     VerifyOrQuit(aNcpBuffer.OutFrameHasEnded(), "Frame longer than expected.");
254     VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame.");
255     sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
256     VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame2Size);
257     SuccessOrQuit(aNcpBuffer.OutFrameRemove());
258 
259     VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
260     VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
261 }
262 
WriteTestFrame3(Spinel::Buffer & aNcpBuffer,Spinel::Buffer::Priority aPriority)263 void WriteTestFrame3(Spinel::Buffer &aNcpBuffer, Spinel::Buffer::Priority aPriority)
264 {
265     Message        *message1;
266     CallbackContext oldContext = sContext;
267 
268     message1 = sMessagePool->Allocate(Message::kTypeIp6);
269     VerifyOrQuit(message1 != nullptr, "Null Message");
270 
271     // An empty message with no content.
272     SuccessOrQuit(message1->SetLength(0));
273 
274     aNcpBuffer.InFrameBegin(aPriority);
275     SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message1));
276     SuccessOrQuit(aNcpBuffer.InFrameFeedData(sMysteryText, sizeof(sMysteryText)));
277     SuccessOrQuit(aNcpBuffer.InFrameEnd());
278 
279     VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
280     VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
281 }
282 
VerifyAndRemoveFrame3(Spinel::Buffer & aNcpBuffer)283 void VerifyAndRemoveFrame3(Spinel::Buffer &aNcpBuffer)
284 {
285     CallbackContext oldContext = sContext;
286 
287     VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText));
288     SuccessOrQuit(aNcpBuffer.OutFrameBegin());
289     VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText));
290     ReadAndVerifyContent(aNcpBuffer, sMysteryText, sizeof(sMysteryText));
291     VerifyOrQuit(aNcpBuffer.OutFrameHasEnded(), "Frame longer than expected.");
292     VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame.");
293     sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
294     VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText));
295     SuccessOrQuit(aNcpBuffer.OutFrameRemove());
296 
297     VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
298     VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
299 }
300 
WriteTestFrame4(Spinel::Buffer & aNcpBuffer,Spinel::Buffer::Priority aPriority)301 void WriteTestFrame4(Spinel::Buffer &aNcpBuffer, Spinel::Buffer::Priority aPriority)
302 {
303     CallbackContext oldContext = sContext;
304 
305     aNcpBuffer.InFrameBegin(aPriority);
306     SuccessOrQuit(aNcpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)));
307     SuccessOrQuit(aNcpBuffer.InFrameEnd());
308 
309     VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
310     VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
311 }
312 
VerifyAndRemoveFrame4(Spinel::Buffer & aNcpBuffer)313 void VerifyAndRemoveFrame4(Spinel::Buffer &aNcpBuffer)
314 {
315     CallbackContext oldContext = sContext;
316 
317     VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sOpenThreadText));
318     SuccessOrQuit(aNcpBuffer.OutFrameBegin());
319     VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sOpenThreadText));
320     ReadAndVerifyContent(aNcpBuffer, sOpenThreadText, sizeof(sOpenThreadText));
321     VerifyOrQuit(aNcpBuffer.OutFrameHasEnded(), "Frame longer than expected.");
322     VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame.");
323     sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
324     VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sOpenThreadText));
325     SuccessOrQuit(aNcpBuffer.OutFrameRemove());
326 
327     VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
328     VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
329 }
330 
331 // This function implements the Spinel::Buffer tests
TestBuffer(void)332 void TestBuffer(void)
333 {
334     unsigned       i, j;
335     uint8_t        buffer[kTestBufferSize];
336     Spinel::Buffer ncpBuffer(buffer, kTestBufferSize);
337 
338     Message                      *message;
339     uint8_t                       readBuffer[16];
340     uint16_t                      readLen, readOffset;
341     Spinel::Buffer::WritePosition pos1, pos2;
342 
343     sInstance    = testInitInstance();
344     sMessagePool = &sInstance->Get<MessagePool>();
345 
346     for (i = 0; i < sizeof(buffer); i++)
347     {
348         buffer[i] = 0;
349     }
350 
351     sContext.mFrameAddedCount   = 0;
352     sContext.mFrameRemovedCount = 0;
353     ClearTagHistory();
354 
355     // Set the callbacks.
356     ncpBuffer.SetFrameAddedCallback(FrameAddedCallback, &sContext);
357     ncpBuffer.SetFrameRemovedCallback(FrameRemovedCallback, &sContext);
358 
359     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
360     printf("\nTest 1: Check initial buffer state");
361 
362     VerifyOrQuit(ncpBuffer.IsEmpty(), "Not empty after init.");
363     VerifyOrQuit(ncpBuffer.InFrameGetLastTag() == Spinel::Buffer::kInvalidTag, "Incorrect tag after init.");
364     VerifyOrQuit(ncpBuffer.OutFrameGetTag() == Spinel::Buffer::kInvalidTag, "Incorrect OutFrameTag after init.");
365 
366     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
367     printf("\nTest 2: Write and read a single frame");
368 
369     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
370     DumpBuffer("\nBuffer after frame1 (low priority)", buffer, kTestBufferSize);
371     printf("\nFrameLen is %u", ncpBuffer.OutFrameGetLength());
372     VerifyAndRemoveFrame1(ncpBuffer);
373 
374     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
375     DumpBuffer("\nBuffer after frame1 (high priority)", buffer, kTestBufferSize);
376     printf("\nFrameLen is %u", ncpBuffer.OutFrameGetLength());
377     VerifyAndRemoveFrame1(ncpBuffer);
378 
379     printf("\nIterations: ");
380 
381     // Always add as low priority.
382     for (j = 0; j < kTestIterationAttemps; j++)
383     {
384         printf("*");
385         WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
386         VerifyOrQuit(ncpBuffer.IsEmpty() == false, "IsEmpty() is incorrect when buffer is non-empty");
387 
388         VerifyAndRemoveFrame1(ncpBuffer);
389         VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() is incorrect when buffer is empty.");
390     }
391 
392     // Always add as high priority.
393     for (j = 0; j < kTestIterationAttemps; j++)
394     {
395         printf("*");
396         WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
397         VerifyOrQuit(ncpBuffer.IsEmpty() == false, "IsEmpty() is incorrect when buffer is non-empty");
398 
399         VerifyAndRemoveFrame1(ncpBuffer);
400         VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() is incorrect when buffer is empty.");
401     }
402 
403     // Every 5th add as high priority.
404     for (j = 0; j < kTestIterationAttemps; j++)
405     {
406         printf("*");
407         WriteTestFrame1(ncpBuffer, ((j % 5) == 0) ? Spinel::Buffer::kPriorityHigh : Spinel::Buffer::kPriorityLow);
408         VerifyOrQuit(ncpBuffer.IsEmpty() == false, "IsEmpty() is incorrect when buffer is non-empty");
409 
410         VerifyAndRemoveFrame1(ncpBuffer);
411         VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() is incorrect when buffer is empty.");
412     }
413 
414     printf(" -- PASS\n");
415 
416     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
417     printf("\nTest 3: Multiple frames write and read (same priority)");
418 
419     WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
420     WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityLow);
421     WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
422     WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
423 
424     DumpBuffer("\nBuffer after multiple frames", buffer, kTestBufferSize);
425 
426     VerifyAndRemoveFrame2(ncpBuffer);
427     VerifyAndRemoveFrame3(ncpBuffer);
428     VerifyAndRemoveFrame2(ncpBuffer);
429     VerifyAndRemoveFrame2(ncpBuffer);
430 
431     printf("\nIterations: ");
432 
433     // Repeat this multiple times.
434     for (j = 0; j < kTestIterationAttemps; j++)
435     {
436         printf("*");
437 
438         WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
439         WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityLow);
440         WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
441 
442         VerifyAndRemoveFrame2(ncpBuffer);
443         VerifyAndRemoveFrame3(ncpBuffer);
444 
445         WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
446         WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityLow);
447 
448         VerifyAndRemoveFrame2(ncpBuffer);
449         VerifyAndRemoveFrame2(ncpBuffer);
450         VerifyAndRemoveFrame3(ncpBuffer);
451 
452         VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() is incorrect when buffer is empty.");
453     }
454 
455     printf(" -- PASS\n");
456 
457     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
458     printf("\nTest 4: Multiple frames write and read (mixed priority)");
459 
460     WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
461     WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityHigh);
462     VerifyAndRemoveFrame3(ncpBuffer);
463     VerifyAndRemoveFrame2(ncpBuffer);
464 
465     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
466     WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
467     WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityHigh);
468     WriteTestFrame4(ncpBuffer, Spinel::Buffer::kPriorityHigh);
469     VerifyAndRemoveFrame3(ncpBuffer);
470     VerifyAndRemoveFrame4(ncpBuffer);
471     VerifyAndRemoveFrame1(ncpBuffer);
472     VerifyAndRemoveFrame2(ncpBuffer);
473 
474     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
475     WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityHigh);
476     WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityLow);
477     WriteTestFrame4(ncpBuffer, Spinel::Buffer::kPriorityHigh);
478     VerifyAndRemoveFrame2(ncpBuffer);
479     VerifyAndRemoveFrame4(ncpBuffer);
480     VerifyAndRemoveFrame1(ncpBuffer);
481     VerifyAndRemoveFrame3(ncpBuffer);
482 
483     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
484     WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityHigh);
485     WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityLow);
486     WriteTestFrame4(ncpBuffer, Spinel::Buffer::kPriorityHigh);
487     VerifyAndRemoveFrame2(ncpBuffer);
488     VerifyAndRemoveFrame4(ncpBuffer);
489     VerifyAndRemoveFrame1(ncpBuffer);
490     VerifyAndRemoveFrame3(ncpBuffer);
491 
492     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
493     WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityHigh);
494     WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityLow);
495     WriteTestFrame4(ncpBuffer, Spinel::Buffer::kPriorityLow);
496     VerifyAndRemoveFrame1(ncpBuffer);
497     VerifyAndRemoveFrame2(ncpBuffer);
498     VerifyAndRemoveFrame3(ncpBuffer);
499     VerifyAndRemoveFrame4(ncpBuffer);
500 
501     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
502     WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityHigh);
503     WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityHigh);
504     VerifyAndRemoveFrame2(ncpBuffer);
505     WriteTestFrame4(ncpBuffer, Spinel::Buffer::kPriorityHigh);
506     VerifyAndRemoveFrame3(ncpBuffer);
507     VerifyAndRemoveFrame4(ncpBuffer);
508     VerifyAndRemoveFrame1(ncpBuffer);
509 
510     printf(" -- PASS\n");
511 
512     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
513     printf("\nTest 5: Frame discard when buffer full and partial read restart");
514 
515     printf("\nIterations: ");
516 
517     for (j = 0; j < kTestIterationAttemps; j++)
518     {
519         bool frame1IsHighPriority = ((j % 3) == 0);
520 
521         printf("*");
522 
523         WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
524         WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityHigh);
525 
526         ncpBuffer.InFrameBegin((j % 2) == 0 ? Spinel::Buffer::kPriorityHigh : Spinel::Buffer::kPriorityLow);
527         SuccessOrQuit(ncpBuffer.InFrameFeedData(sHelloText, sizeof(sHelloText)));
528 
529         message = sMessagePool->Allocate(Message::kTypeIp6);
530         VerifyOrQuit(message != nullptr, "Null Message");
531         SuccessOrQuit(message->SetLength(sizeof(sMysteryText)));
532         message->Write(0, sMysteryText);
533 
534         SuccessOrQuit(ncpBuffer.InFrameFeedMessage(message));
535 
536         // Start writing a new frame in middle of an unfinished frame. Ensure the first one is discarded.
537         WriteTestFrame1(ncpBuffer, frame1IsHighPriority ? Spinel::Buffer::kPriorityHigh : Spinel::Buffer::kPriorityLow);
538 
539         // Note that message will not be freed by the NCP buffer since the frame associated with it was discarded and
540         // not yet finished/ended.
541         otMessageFree(message);
542 
543         VerifyAndRemoveFrame3(ncpBuffer);
544 
545         // Start reading few bytes from the frame
546         SuccessOrQuit(ncpBuffer.OutFrameBegin());
547         ncpBuffer.OutFrameReadByte();
548         ncpBuffer.OutFrameReadByte();
549         ncpBuffer.OutFrameReadByte();
550 
551         // Now reset the read pointer and read/verify the frame from start.
552         if (frame1IsHighPriority)
553         {
554             VerifyAndRemoveFrame1(ncpBuffer);
555             VerifyAndRemoveFrame2(ncpBuffer);
556         }
557         else
558         {
559             VerifyAndRemoveFrame2(ncpBuffer);
560             VerifyAndRemoveFrame1(ncpBuffer);
561         }
562 
563         VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() is incorrect when buffer is empty.");
564     }
565 
566     printf(" -- PASS\n");
567 
568     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
569     printf("\nTest 6: Clear() and empty buffer method tests");
570 
571     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
572 
573     ncpBuffer.Clear();
574     ClearTagHistory();
575 
576     VerifyOrQuit(ncpBuffer.InFrameGetLastTag() == Spinel::Buffer::kInvalidTag, "Incorrect last tag after Clear().");
577     VerifyOrQuit(ncpBuffer.OutFrameGetTag() == Spinel::Buffer::kInvalidTag, "Incorrect OutFrameTag after Clear().");
578     VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() is incorrect when buffer is empty.");
579     VerifyOrQuit(ncpBuffer.OutFrameHasEnded(), "OutFrameHasEnded() is incorrect when no data in buffer.");
580     VerifyOrQuit(ncpBuffer.OutFrameRemove() == OT_ERROR_NOT_FOUND,
581                  "Remove() returned incorrect error status when buffer is empty.");
582     VerifyOrQuit(ncpBuffer.OutFrameGetLength() == 0,
583                  "OutFrameGetLength() returned non-zero length when buffer is empty.");
584 
585     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
586     VerifyAndRemoveFrame1(ncpBuffer);
587 
588     VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() is incorrect when buffer is empty.");
589     VerifyOrQuit(ncpBuffer.OutFrameHasEnded(), "OutFrameHasEnded() is incorrect when no data in buffer.");
590     VerifyOrQuit(ncpBuffer.OutFrameRemove() == OT_ERROR_NOT_FOUND,
591                  "Remove() returned incorrect error status when buffer is empty.");
592     VerifyOrQuit(ncpBuffer.OutFrameGetLength() == 0,
593                  "OutFrameGetLength() returned non-zero length when buffer is empty.");
594 
595     printf(" -- PASS\n");
596 
597     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
598     printf("\nTest 7: OutFrameRead() in parts\n");
599 
600     ncpBuffer.InFrameBegin(Spinel::Buffer::kPriorityLow);
601     SuccessOrQuit(ncpBuffer.InFrameFeedData(sMottoText, sizeof(sMottoText)));
602     SuccessOrQuit(ncpBuffer.InFrameEnd());
603 
604     SuccessOrQuit(ncpBuffer.OutFrameBegin());
605     readOffset = 0;
606 
607     while ((readLen = ncpBuffer.OutFrameRead(sizeof(readBuffer), readBuffer)) != 0)
608     {
609         DumpBuffer("Read() returned", readBuffer, readLen);
610 
611         VerifyOrQuit(memcmp(readBuffer, sMottoText + readOffset, readLen) == 0,
612                      "Read() does not match expected content.");
613 
614         readOffset += readLen;
615     }
616 
617     VerifyOrQuit(readOffset == sizeof(sMottoText), "Read len does not match expected length.");
618 
619     SuccessOrQuit(ncpBuffer.OutFrameRemove());
620 
621     printf("\n -- PASS\n");
622 
623     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
624     printf("\nTest 8: Remove a frame without reading it first");
625 
626     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
627     WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
628     VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
629     SuccessOrQuit(ncpBuffer.OutFrameRemove());
630     VerifyAndRemoveFrame2(ncpBuffer);
631     printf(" -- PASS\n");
632 
633     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
634     printf("\nTest 9: Check length when front frame gets changed (a higher priority frame is added)");
635     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
636     VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
637     WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityHigh);
638     VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame3Size);
639     VerifyAndRemoveFrame3(ncpBuffer);
640     VerifyAndRemoveFrame1(ncpBuffer);
641     printf(" -- PASS\n");
642 
643     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
644     printf("\nTest 10: Active out frame remaining unchanged when a higher priority frame is written while reading it");
645     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
646     VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
647     SuccessOrQuit(ncpBuffer.OutFrameBegin());
648     VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
649     ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
650     WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityHigh);
651     VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
652     ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText));
653     SuccessOrQuit(ncpBuffer.OutFrameBegin());
654     VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
655     ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
656     ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText));
657     ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
658     ReadAndVerifyContent(ncpBuffer, sHelloText, sizeof(sHelloText));
659     VerifyOrQuit(ncpBuffer.OutFrameHasEnded(), "Frame longer than expected.");
660     WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityHigh);
661     WriteTestFrame4(ncpBuffer, Spinel::Buffer::kPriorityLow);
662     VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
663     VerifyAndRemoveFrame1(ncpBuffer);
664     VerifyAndRemoveFrame2(ncpBuffer);
665     VerifyAndRemoveFrame3(ncpBuffer);
666     VerifyAndRemoveFrame4(ncpBuffer);
667     // Repeat test reversing frame priority orders.
668     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
669     VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
670     SuccessOrQuit(ncpBuffer.OutFrameBegin());
671     VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
672     ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
673     WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
674     VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
675     ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText));
676     SuccessOrQuit(ncpBuffer.OutFrameBegin());
677     VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
678     ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
679     ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText));
680     ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText));
681     ReadAndVerifyContent(ncpBuffer, sHelloText, sizeof(sHelloText));
682     VerifyOrQuit(ncpBuffer.OutFrameHasEnded(), "Frame longer than expected.");
683     WriteTestFrame3(ncpBuffer, Spinel::Buffer::kPriorityHigh);
684     WriteTestFrame4(ncpBuffer, Spinel::Buffer::kPriorityLow);
685     VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size);
686     VerifyAndRemoveFrame1(ncpBuffer);
687     VerifyAndRemoveFrame3(ncpBuffer);
688     VerifyAndRemoveFrame2(ncpBuffer);
689     VerifyAndRemoveFrame4(ncpBuffer);
690     printf(" -- PASS\n");
691 
692     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
693     printf("\n Test 11: Read and remove in middle of an active input frame write");
694     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
695     ncpBuffer.InFrameBegin(Spinel::Buffer::kPriorityHigh);
696     SuccessOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)));
697     VerifyAndRemoveFrame1(ncpBuffer);
698     VerifyOrQuit(ncpBuffer.IsEmpty());
699     SuccessOrQuit(ncpBuffer.InFrameEnd());
700     VerifyAndRemoveFrame4(ncpBuffer);
701     // Repeat the test reversing priorities
702     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
703     ncpBuffer.InFrameBegin(Spinel::Buffer::kPriorityLow);
704     SuccessOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)));
705     VerifyAndRemoveFrame1(ncpBuffer);
706     VerifyOrQuit(ncpBuffer.IsEmpty());
707     SuccessOrQuit(ncpBuffer.InFrameEnd());
708     VerifyAndRemoveFrame4(ncpBuffer);
709     // Repeat the test with same priorities
710     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
711     ncpBuffer.InFrameBegin(Spinel::Buffer::kPriorityHigh);
712     SuccessOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)));
713     VerifyAndRemoveFrame1(ncpBuffer);
714     VerifyOrQuit(ncpBuffer.IsEmpty());
715     SuccessOrQuit(ncpBuffer.InFrameEnd());
716     VerifyAndRemoveFrame4(ncpBuffer);
717     printf(" -- PASS\n");
718 
719     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
720     printf("\n Test 12: Check returned error status");
721     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
722     ncpBuffer.InFrameBegin(Spinel::Buffer::kPriorityHigh);
723     VerifyOrQuit(ncpBuffer.InFrameFeedData(buffer, sizeof(buffer)) == OT_ERROR_NO_BUFS);
724     VerifyAndRemoveFrame1(ncpBuffer);
725     VerifyOrQuit(ncpBuffer.IsEmpty());
726 
727     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityLow);
728     WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityHigh);
729     // Ensure writes with starting `InFrameBegin()` fail
730     VerifyOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, 1) == OT_ERROR_INVALID_STATE);
731     VerifyOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, 0) == OT_ERROR_INVALID_STATE);
732     VerifyOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, 0) == OT_ERROR_INVALID_STATE);
733     VerifyOrQuit(ncpBuffer.InFrameEnd() == OT_ERROR_INVALID_STATE);
734     message = sMessagePool->Allocate(Message::kTypeIp6);
735     VerifyOrQuit(message != nullptr, "Null Message");
736     SuccessOrQuit(message->SetLength(sizeof(sMysteryText)));
737     message->Write(0, sMysteryText);
738     VerifyOrQuit(ncpBuffer.InFrameFeedMessage(message) == OT_ERROR_INVALID_STATE);
739     message->Free();
740     VerifyOrQuit(ncpBuffer.InFrameEnd() == OT_ERROR_INVALID_STATE);
741     VerifyAndRemoveFrame2(ncpBuffer);
742     VerifyAndRemoveFrame1(ncpBuffer);
743     VerifyOrQuit(ncpBuffer.IsEmpty());
744     VerifyOrQuit(ncpBuffer.OutFrameBegin() == OT_ERROR_NOT_FOUND, "OutFrameBegin() failed on empty queue");
745     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
746     VerifyAndRemoveFrame1(ncpBuffer);
747     VerifyOrQuit(ncpBuffer.IsEmpty());
748     printf(" -- PASS\n");
749 
750     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
751     printf("\n Test 13: Ensure we can utilize the full buffer size when frames removed during write");
752     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
753     WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
754     ncpBuffer.InFrameBegin(Spinel::Buffer::kPriorityHigh);
755     VerifyAndRemoveFrame1(ncpBuffer);
756     VerifyAndRemoveFrame2(ncpBuffer);
757     SuccessOrQuit(ncpBuffer.InFrameFeedData(buffer, sizeof(buffer) - 4));
758     SuccessOrQuit(ncpBuffer.InFrameEnd());
759     SuccessOrQuit(ncpBuffer.OutFrameRemove());
760     // Repeat the test with a low priority buffer write
761     WriteTestFrame1(ncpBuffer, Spinel::Buffer::kPriorityHigh);
762     WriteTestFrame2(ncpBuffer, Spinel::Buffer::kPriorityLow);
763     ncpBuffer.InFrameBegin(Spinel::Buffer::kPriorityLow);
764     VerifyAndRemoveFrame1(ncpBuffer);
765     VerifyAndRemoveFrame2(ncpBuffer);
766     SuccessOrQuit(ncpBuffer.InFrameFeedData(buffer, sizeof(buffer) - 4));
767     SuccessOrQuit(ncpBuffer.InFrameEnd());
768     SuccessOrQuit(ncpBuffer.OutFrameRemove());
769     printf(" -- PASS\n");
770 
771     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
772     printf("\n Test 14: Test InFrameOverwrite ");
773     printf("\nIterations: ");
774 
775     for (j = 0; j < kTestIterationAttemps; j++)
776     {
777         uint16_t                 index;
778         bool                     addExtra = ((j % 7) != 0);
779         Spinel::Buffer::Priority priority;
780 
781         printf("*");
782         priority = ((j % 3) == 0) ? Spinel::Buffer::kPriorityHigh : Spinel::Buffer::kPriorityLow;
783         index    = static_cast<uint16_t>(j % sizeof(sHexText));
784         ncpBuffer.InFrameBegin(priority);
785         SuccessOrQuit(ncpBuffer.InFrameFeedData(sHexText, index));
786         SuccessOrQuit(ncpBuffer.InFrameGetPosition(pos1));
787         SuccessOrQuit(ncpBuffer.InFrameFeedData(sMysteryText, sizeof(sHexText) - index));
788         VerifyOrQuit(ncpBuffer.InFrameGetDistance(pos1) == sizeof(sHexText) - index);
789 
790         if (addExtra)
791         {
792             SuccessOrQuit(ncpBuffer.InFrameFeedData(sHelloText, sizeof(sHelloText)));
793         }
794 
795         SuccessOrQuit(ncpBuffer.InFrameOverwrite(pos1, sHexText + index, sizeof(sHexText) - index),
796                       "InFrameOverwrite() failed.");
797         VerifyOrQuit(ncpBuffer.InFrameGetDistance(pos1) ==
798                          sizeof(sHexText) - index + (addExtra ? sizeof(sHelloText) : 0),
799                      "InFrameGetDistance() failed");
800         SuccessOrQuit(ncpBuffer.InFrameEnd());
801         VerifyOrQuit(ncpBuffer.InFrameGetPosition(pos2) == OT_ERROR_INVALID_STATE);
802         VerifyOrQuit(ncpBuffer.InFrameOverwrite(pos1, sHexText, 0) != OT_ERROR_NONE);
803         SuccessOrQuit(ncpBuffer.OutFrameBegin());
804         ReadAndVerifyContent(ncpBuffer, sHexText, sizeof(sHexText));
805 
806         if (addExtra)
807         {
808             ReadAndVerifyContent(ncpBuffer, sHelloText, sizeof(sHelloText));
809         }
810 
811         SuccessOrQuit(ncpBuffer.OutFrameRemove());
812         VerifyOrQuit(ncpBuffer.InFrameGetPosition(pos2) == OT_ERROR_INVALID_STATE);
813     }
814 
815     printf(" -- PASS\n");
816 
817     printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
818     printf("\n Test 15: Test InFrameReset()");
819     printf("\nIterations: ");
820 
821     for (j = 0; j < kTestIterationAttemps; j++)
822     {
823         uint16_t                 index;
824         bool                     addExtra = ((j % 7) != 0);
825         Spinel::Buffer::Priority priority;
826 
827         printf("*");
828         priority = ((j % 3) == 0) ? Spinel::Buffer::kPriorityHigh : Spinel::Buffer::kPriorityLow;
829         index    = static_cast<uint16_t>(j % sizeof(sHexText));
830         ncpBuffer.InFrameBegin(priority);
831         SuccessOrQuit(ncpBuffer.InFrameFeedData(sHexText, index));
832         SuccessOrQuit(ncpBuffer.InFrameGetPosition(pos1));
833         SuccessOrQuit(ncpBuffer.InFrameFeedData(sMysteryText, sizeof(sHexText) - index));
834         VerifyOrQuit(ncpBuffer.InFrameGetDistance(pos1) == sizeof(sHexText) - index);
835 
836         if (addExtra)
837         {
838             SuccessOrQuit(ncpBuffer.InFrameFeedData(sHelloText, sizeof(sHelloText)));
839         }
840 
841         SuccessOrQuit(ncpBuffer.InFrameReset(pos1));
842         SuccessOrQuit(ncpBuffer.InFrameFeedData(sHexText + index, sizeof(sHexText) - index));
843 
844         if (addExtra)
845         {
846             SuccessOrQuit(ncpBuffer.InFrameReset(pos1));
847             SuccessOrQuit(ncpBuffer.InFrameFeedData(sHexText + index, sizeof(sHexText) - index));
848         }
849 
850         VerifyOrQuit(ncpBuffer.InFrameGetDistance(pos1) == sizeof(sHexText) - index);
851         SuccessOrQuit(ncpBuffer.InFrameEnd());
852         SuccessOrQuit(ncpBuffer.OutFrameBegin());
853         ReadAndVerifyContent(ncpBuffer, sHexText, sizeof(sHexText));
854         SuccessOrQuit(ncpBuffer.OutFrameRemove());
855     }
856 
857     printf(" -- PASS\n");
858 
859     testFreeInstance(sInstance);
860 }
861 
862 /**
863  * NCP Buffer Fuzz testing
864  *
865  * Randomly decide if to read or write a frame to the NCP buffer (use `kReadProbability` in percent to control the
866  * behavior).
867  *
868  * When writing a frame, use a random length (1 up to `kMaxFrameLen`) and generate random byte sequences.
869  * When reading a frame ensure the length and the content matches what was written earlier.
870  * Handle the cases where buffer gets full or empty.
871  */
872 
873 enum
874 {
875     kFuzTestBufferSize            = 2000,   // Size of the buffer used during fuzz testing
876     kFuzTestIterationAttempts     = 500000, // Number of iterations  to run
877     kLensArraySize                = 500,    // Size of "Lengths" array.
878     kMaxFrameLen                  = 400,    // Maximum frame length
879     kReadProbability              = 50,     // Probability (in percent) to randomly choose to read vs write frame
880     kHighPriorityProbability      = 20,     // Probability (in percent) to write a high priority frame
881     kUseTrueRandomNumberGenerator = 1,      // To use true random number generator or not.
882 };
883 
884 uint8_t  sFrameBuffer[kNumPrios][kFuzTestBufferSize];
885 uint32_t sFrameBufferTailIndex[kNumPrios] = {0};
886 
GetRandom(uint32_t max)887 uint32_t GetRandom(uint32_t max)
888 {
889     uint32_t value;
890 
891     if (kUseTrueRandomNumberGenerator)
892     {
893         SuccessOrQuit(Random::Crypto::Fill(value));
894     }
895     else
896     {
897         value = Random::NonCrypto::GetUint32();
898     }
899 
900     return value % max;
901 }
902 
WriteRandomFrame(uint32_t aLength,Spinel::Buffer & aNcpBuffer,Spinel::Buffer::Priority aPriority)903 otError WriteRandomFrame(uint32_t aLength, Spinel::Buffer &aNcpBuffer, Spinel::Buffer::Priority aPriority)
904 {
905     otError         error;
906     uint8_t         byte;
907     uint8_t         priority   = static_cast<uint8_t>(aPriority);
908     CallbackContext oldContext = sContext;
909     uint32_t        tail       = sFrameBufferTailIndex[priority];
910 
911     aNcpBuffer.InFrameBegin(aPriority);
912 
913     while (aLength--)
914     {
915         byte = static_cast<uint8_t>(GetRandom(256));
916         SuccessOrExit(error = aNcpBuffer.InFrameFeedData(&byte, sizeof(byte)));
917         sFrameBuffer[priority][tail++] = byte;
918     }
919 
920     SuccessOrExit(error = aNcpBuffer.InFrameEnd());
921 
922     sFrameBufferTailIndex[priority] = tail;
923 
924     // check the callbacks
925     VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
926     VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
927 
928 exit:
929     return error;
930 }
931 
ReadRandomFrame(uint32_t aLength,Spinel::Buffer & aNcpBuffer,uint8_t priority)932 otError ReadRandomFrame(uint32_t aLength, Spinel::Buffer &aNcpBuffer, uint8_t priority)
933 {
934     CallbackContext oldContext = sContext;
935 
936     SuccessOrQuit(aNcpBuffer.OutFrameBegin());
937     VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == aLength);
938 
939     // Read and verify that the content is same as sFrameBuffer values...
940     ReadAndVerifyContent(aNcpBuffer, sFrameBuffer[priority], static_cast<uint16_t>(aLength));
941     sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
942 
943     SuccessOrQuit(aNcpBuffer.OutFrameRemove());
944 
945     sFrameBufferTailIndex[priority] -= aLength;
946     memmove(sFrameBuffer[priority], sFrameBuffer[priority] + aLength, sFrameBufferTailIndex[priority]);
947 
948     // If successful check the callbacks
949     VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
950     VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
951 
952     return OT_ERROR_NONE;
953 }
954 
955 // This runs a fuzz test of NCP buffer
TestFuzzBuffer(void)956 void TestFuzzBuffer(void)
957 {
958     uint8_t        buffer[kFuzTestBufferSize];
959     Spinel::Buffer ncpBuffer(buffer, kFuzTestBufferSize);
960 
961     uint32_t lensArray[kNumPrios][kLensArraySize]; // Keeps track of length of written frames so far
962     uint32_t lensArrayStart[kNumPrios];
963     uint32_t lensArrayCount[kNumPrios];
964 
965     sInstance    = testInitInstance();
966     sMessagePool = &sInstance->Get<MessagePool>();
967 
968     memset(buffer, 0, sizeof(buffer));
969 
970     memset(lensArray, 0, sizeof(lensArray));
971     memset(lensArrayStart, 0, sizeof(lensArrayStart));
972     memset(lensArrayCount, 0, sizeof(lensArrayCount));
973 
974     sContext.mFrameAddedCount   = 0;
975     sContext.mFrameRemovedCount = 0;
976     ClearTagHistory();
977 
978     ncpBuffer.SetFrameAddedCallback(FrameAddedCallback, &sContext);
979     ncpBuffer.SetFrameRemovedCallback(FrameRemovedCallback, &sContext);
980 
981     for (uint32_t iter = 0; iter < kFuzTestIterationAttempts; iter++)
982     {
983         bool shouldRead;
984 
985         if (lensArrayCount[0] == 0 && lensArrayCount[1] == 0)
986         {
987             shouldRead = false;
988         }
989         else if (lensArrayCount[0] == kLensArraySize - 1 || lensArrayCount[1] == kLensArraySize - 1)
990         {
991             shouldRead = true;
992         }
993         else
994         {
995             // Randomly decide to read or write.
996             shouldRead = (GetRandom(100) < kReadProbability);
997         }
998 
999         if (shouldRead)
1000         {
1001             uint32_t len;
1002             uint8_t  priority;
1003 
1004             priority = (lensArrayCount[Spinel::Buffer::kPriorityHigh] != 0) ? Spinel::Buffer::kPriorityHigh
1005                                                                             : Spinel::Buffer::kPriorityLow;
1006 
1007             len                      = lensArray[priority][lensArrayStart[priority]];
1008             lensArrayStart[priority] = (lensArrayStart[priority] + 1) % kLensArraySize;
1009             lensArrayCount[priority]--;
1010 
1011             printf("R%c%d ", priority == Spinel::Buffer::kPriorityHigh ? 'H' : 'L', len);
1012 
1013             SuccessOrQuit(ReadRandomFrame(len, ncpBuffer, priority), "Failed to read random frame.");
1014         }
1015         else
1016         {
1017             uint32_t                 len = GetRandom(kMaxFrameLen) + 1;
1018             Spinel::Buffer::Priority priority;
1019 
1020             if (GetRandom(100) < kHighPriorityProbability)
1021             {
1022                 priority = Spinel::Buffer::kPriorityHigh;
1023             }
1024             else
1025             {
1026                 priority = Spinel::Buffer::kPriorityLow;
1027             }
1028 
1029             if (WriteRandomFrame(len, ncpBuffer, priority) == OT_ERROR_NONE)
1030             {
1031                 lensArray[priority][(lensArrayStart[priority] + lensArrayCount[priority]) % kLensArraySize] = len;
1032                 lensArrayCount[priority]++;
1033 
1034                 printf("W%c%d ", priority == Spinel::Buffer::kPriorityHigh ? 'H' : 'L', len);
1035             }
1036             else
1037             {
1038                 printf("FULL ");
1039             }
1040         }
1041 
1042         if (lensArrayCount[0] == 0 && lensArrayCount[1] == 0)
1043         {
1044             VerifyOrQuit(ncpBuffer.IsEmpty());
1045             printf("EMPTY ");
1046         }
1047     }
1048 
1049     printf("\n -- PASS\n");
1050 
1051     testFreeInstance(sInstance);
1052 }
1053 
1054 } // namespace Spinel
1055 } // namespace ot
1056 
main(void)1057 int main(void)
1058 {
1059     ot::Spinel::TestBuffer();
1060     ot::Spinel::TestFuzzBuffer();
1061     printf("\nAll tests passed.\n");
1062     return 0;
1063 }
1064