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 
874 enum
875 {
876     kFuzTestBufferSize            = 2000,   // Size of the buffer used during fuzz testing
877     kFuzTestIterationAttempts     = 500000, // Number of iterations  to run
878     kLensArraySize                = 500,    // Size of "Lengths" array.
879     kMaxFrameLen                  = 400,    // Maximum frame length
880     kReadProbability              = 50,     // Probability (in percent) to randomly choose to read vs write frame
881     kHighPriorityProbability      = 20,     // Probability (in percent) to write a high priority frame
882     kUseTrueRandomNumberGenerator = 1,      // To use true random number generator or not.
883 };
884 
885 uint8_t  sFrameBuffer[kNumPrios][kFuzTestBufferSize];
886 uint32_t sFrameBufferTailIndex[kNumPrios] = {0};
887 
GetRandom(uint32_t max)888 uint32_t GetRandom(uint32_t max)
889 {
890     uint32_t value;
891 
892     if (kUseTrueRandomNumberGenerator)
893     {
894         SuccessOrQuit(Random::Crypto::Fill(value));
895     }
896     else
897     {
898         value = Random::NonCrypto::GetUint32();
899     }
900 
901     return value % max;
902 }
903 
WriteRandomFrame(uint32_t aLength,Spinel::Buffer & aNcpBuffer,Spinel::Buffer::Priority aPriority)904 otError WriteRandomFrame(uint32_t aLength, Spinel::Buffer &aNcpBuffer, Spinel::Buffer::Priority aPriority)
905 {
906     otError         error;
907     uint8_t         byte;
908     uint8_t         priority   = static_cast<uint8_t>(aPriority);
909     CallbackContext oldContext = sContext;
910     uint32_t        tail       = sFrameBufferTailIndex[priority];
911 
912     aNcpBuffer.InFrameBegin(aPriority);
913 
914     while (aLength--)
915     {
916         byte = static_cast<uint8_t>(GetRandom(256));
917         SuccessOrExit(error = aNcpBuffer.InFrameFeedData(&byte, sizeof(byte)));
918         sFrameBuffer[priority][tail++] = byte;
919     }
920 
921     SuccessOrExit(error = aNcpBuffer.InFrameEnd());
922 
923     sFrameBufferTailIndex[priority] = tail;
924 
925     // check the callbacks
926     VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
927     VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
928 
929 exit:
930     return error;
931 }
932 
ReadRandomFrame(uint32_t aLength,Spinel::Buffer & aNcpBuffer,uint8_t priority)933 otError ReadRandomFrame(uint32_t aLength, Spinel::Buffer &aNcpBuffer, uint8_t priority)
934 {
935     CallbackContext oldContext = sContext;
936 
937     SuccessOrQuit(aNcpBuffer.OutFrameBegin());
938     VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == aLength);
939 
940     // Read and verify that the content is same as sFrameBuffer values...
941     ReadAndVerifyContent(aNcpBuffer, sFrameBuffer[priority], static_cast<uint16_t>(aLength));
942     sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag();
943 
944     SuccessOrQuit(aNcpBuffer.OutFrameRemove());
945 
946     sFrameBufferTailIndex[priority] -= aLength;
947     memmove(sFrameBuffer[priority], sFrameBuffer[priority] + aLength, sFrameBufferTailIndex[priority]);
948 
949     // If successful check the callbacks
950     VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed.");
951     VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed.");
952 
953     return OT_ERROR_NONE;
954 }
955 
956 // This runs a fuzz test of NCP buffer
TestFuzzBuffer(void)957 void TestFuzzBuffer(void)
958 {
959     uint8_t        buffer[kFuzTestBufferSize];
960     Spinel::Buffer ncpBuffer(buffer, kFuzTestBufferSize);
961 
962     uint32_t lensArray[kNumPrios][kLensArraySize]; // Keeps track of length of written frames so far
963     uint32_t lensArrayStart[kNumPrios];
964     uint32_t lensArrayCount[kNumPrios];
965 
966     sInstance    = testInitInstance();
967     sMessagePool = &sInstance->Get<MessagePool>();
968 
969     memset(buffer, 0, sizeof(buffer));
970 
971     memset(lensArray, 0, sizeof(lensArray));
972     memset(lensArrayStart, 0, sizeof(lensArrayStart));
973     memset(lensArrayCount, 0, sizeof(lensArrayCount));
974 
975     sContext.mFrameAddedCount   = 0;
976     sContext.mFrameRemovedCount = 0;
977     ClearTagHistory();
978 
979     ncpBuffer.SetFrameAddedCallback(FrameAddedCallback, &sContext);
980     ncpBuffer.SetFrameRemovedCallback(FrameRemovedCallback, &sContext);
981 
982     for (uint32_t iter = 0; iter < kFuzTestIterationAttempts; iter++)
983     {
984         bool shouldRead;
985 
986         if (lensArrayCount[0] == 0 && lensArrayCount[1] == 0)
987         {
988             shouldRead = false;
989         }
990         else if (lensArrayCount[0] == kLensArraySize - 1 || lensArrayCount[1] == kLensArraySize - 1)
991         {
992             shouldRead = true;
993         }
994         else
995         {
996             // Randomly decide to read or write.
997             shouldRead = (GetRandom(100) < kReadProbability);
998         }
999 
1000         if (shouldRead)
1001         {
1002             uint32_t len;
1003             uint8_t  priority;
1004 
1005             priority = (lensArrayCount[Spinel::Buffer::kPriorityHigh] != 0) ? Spinel::Buffer::kPriorityHigh
1006                                                                             : Spinel::Buffer::kPriorityLow;
1007 
1008             len                      = lensArray[priority][lensArrayStart[priority]];
1009             lensArrayStart[priority] = (lensArrayStart[priority] + 1) % kLensArraySize;
1010             lensArrayCount[priority]--;
1011 
1012             printf("R%c%d ", priority == Spinel::Buffer::kPriorityHigh ? 'H' : 'L', len);
1013 
1014             SuccessOrQuit(ReadRandomFrame(len, ncpBuffer, priority), "Failed to read random frame.");
1015         }
1016         else
1017         {
1018             uint32_t                 len = GetRandom(kMaxFrameLen) + 1;
1019             Spinel::Buffer::Priority priority;
1020 
1021             if (GetRandom(100) < kHighPriorityProbability)
1022             {
1023                 priority = Spinel::Buffer::kPriorityHigh;
1024             }
1025             else
1026             {
1027                 priority = Spinel::Buffer::kPriorityLow;
1028             }
1029 
1030             if (WriteRandomFrame(len, ncpBuffer, priority) == OT_ERROR_NONE)
1031             {
1032                 lensArray[priority][(lensArrayStart[priority] + lensArrayCount[priority]) % kLensArraySize] = len;
1033                 lensArrayCount[priority]++;
1034 
1035                 printf("W%c%d ", priority == Spinel::Buffer::kPriorityHigh ? 'H' : 'L', len);
1036             }
1037             else
1038             {
1039                 printf("FULL ");
1040             }
1041         }
1042 
1043         if (lensArrayCount[0] == 0 && lensArrayCount[1] == 0)
1044         {
1045             VerifyOrQuit(ncpBuffer.IsEmpty());
1046             printf("EMPTY ");
1047         }
1048     }
1049 
1050     printf("\n -- PASS\n");
1051 
1052     testFreeInstance(sInstance);
1053 }
1054 
1055 } // namespace Spinel
1056 } // namespace ot
1057 
main(void)1058 int main(void)
1059 {
1060     ot::Spinel::TestBuffer();
1061     ot::Spinel::TestFuzzBuffer();
1062     printf("\nAll tests passed.\n");
1063     return 0;
1064 }
1065