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