Apply Upstream code (2021-03-15)
[platform/upstream/connectedhomeip.git] / src / protocols / bdx / tests / TestBdxTransferSession.cpp
1 #include <protocols/Protocols.h>
2 #include <protocols/bdx/BdxMessages.h>
3 #include <protocols/bdx/BdxTransferSession.h>
4
5 #include <string.h>
6
7 #include <nlunit-test.h>
8
9 #include <core/CHIPTLV.h>
10 #include <protocols/common/Constants.h>
11 #include <support/BufferReader.h>
12 #include <support/CHIPMem.h>
13 #include <support/CodeUtils.h>
14 #include <support/ReturnMacros.h>
15 #include <support/UnitTestRegistration.h>
16 #include <system/SystemPacketBuffer.h>
17
18 using namespace ::chip;
19 using namespace ::chip::bdx;
20
21 namespace {
22 // Use this as a timestamp if not needing to test BDX timeouts.
23 constexpr uint64_t kNoAdvanceTime = 0;
24
25 const uint64_t tlvStrTag  = TLV::ContextTag(4);
26 const uint64_t tlvListTag = TLV::ProfileTag(7777, 8888);
27 } // anonymous namespace
28
29 // Helper method for generating a complete TLV structure with a list containing a single tag and string
30 CHIP_ERROR WriteChipTLVString(uint8_t * buf, uint32_t bufLen, const char * data, uint32_t & written)
31 {
32     CHIP_ERROR err = CHIP_NO_ERROR;
33     written        = 0;
34     TLV::TLVWriter writer;
35     writer.Init(buf, bufLen);
36
37     {
38         TLV::TLVWriter listWriter;
39         err = writer.OpenContainer(tlvListTag, TLV::kTLVType_List, listWriter);
40         SuccessOrExit(err);
41         err = listWriter.PutString(tlvStrTag, data);
42         SuccessOrExit(err);
43         err = writer.CloseContainer(listWriter);
44         SuccessOrExit(err);
45     }
46
47     err = writer.Finalize();
48     SuccessOrExit(err);
49     written = writer.GetLengthWritten();
50
51 exit:
52     return err;
53 }
54
55 // Helper method: read a TLV structure with a single tag and string and verify it matches expected string.
56 CHIP_ERROR ReadAndVerifyTLVString(nlTestSuite * inSuite, void * inContext, const uint8_t * dataStart, uint32_t len,
57                                   const char * expected, uint16_t expectedLen)
58 {
59     CHIP_ERROR err = CHIP_NO_ERROR;
60     TLV::TLVReader reader;
61     char tmp[64]        = { 0 };
62     uint32_t readLength = 0;
63     VerifyOrExit(sizeof(tmp) > len, err = CHIP_ERROR_INTERNAL);
64
65     reader.Init(dataStart, len);
66     err = reader.Next();
67
68     VerifyOrExit(reader.GetTag() == tlvListTag, err = CHIP_ERROR_INTERNAL);
69
70     // Metadata must have a top-level list
71     {
72         TLV::TLVReader listReader;
73         err = reader.OpenContainer(listReader);
74         SuccessOrExit(err);
75
76         err = listReader.Next();
77         SuccessOrExit(err);
78
79         VerifyOrExit(listReader.GetTag() == tlvStrTag, err = CHIP_ERROR_INTERNAL);
80         readLength = listReader.GetLength();
81         VerifyOrExit(readLength == expectedLen, err = CHIP_ERROR_INTERNAL);
82         err = listReader.GetString(tmp, sizeof(tmp));
83         SuccessOrExit(err);
84         VerifyOrExit(!memcmp(expected, tmp, readLength), err = CHIP_ERROR_INTERNAL);
85
86         err = reader.CloseContainer(listReader);
87         SuccessOrExit(err);
88     }
89
90 exit:
91     return err;
92 }
93
94 // Helper method for verifying that a PacketBufferHandle contains a valid BDX header and message type matches expected.
95 void VerifyBdxMessageType(nlTestSuite * inSuite, void * inContext, const System::PacketBufferHandle & msg, MessageType expected)
96 {
97     CHIP_ERROR err      = CHIP_NO_ERROR;
98     uint16_t headerSize = 0;
99     PayloadHeader payloadHeader;
100
101     if (msg.IsNull())
102     {
103         NL_TEST_ASSERT(inSuite, false);
104         return;
105     }
106
107     err = payloadHeader.Decode(msg->Start(), msg->DataLength(), &headerSize);
108     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
109     NL_TEST_ASSERT(inSuite, payloadHeader.HasMessageType(expected));
110 }
111
112 // Helper method for verifying that a PacketBufferHandle contains a valid StatusReport message and contains a specific StatusCode.
113 void VerifyStatusReport(nlTestSuite * inSuite, void * inContext, const System::PacketBufferHandle & msg, StatusCode code)
114 {
115     CHIP_ERROR err      = CHIP_NO_ERROR;
116     uint16_t headerSize = 0;
117     PayloadHeader payloadHeader;
118     uint16_t generalCode = 0;
119     uint32_t protocolId  = 0;
120     BitFlags<StatusCode> protocolCode;
121
122     if (msg.IsNull())
123     {
124         NL_TEST_ASSERT(inSuite, false);
125         return;
126     }
127
128     err = payloadHeader.Decode(msg->Start(), msg->DataLength(), &headerSize);
129     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
130     NL_TEST_ASSERT(inSuite, payloadHeader.GetProtocolID() == Protocols::kProtocol_Protocol_Common);
131     NL_TEST_ASSERT(inSuite, payloadHeader.GetMessageType() == static_cast<uint8_t>(Protocols::Common::MsgType::StatusReport));
132     if (headerSize > msg->DataLength())
133     {
134         NL_TEST_ASSERT(inSuite, false);
135         return;
136     }
137
138     Encoding::LittleEndian::Reader reader(msg->Start(), msg->DataLength());
139     err = reader.Skip(headerSize).Read16(&generalCode).Read32(&protocolId).Read16(protocolCode.RawStorage()).StatusCode();
140     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
141     NL_TEST_ASSERT(inSuite, generalCode == static_cast<uint16_t>(Protocols::Common::StatusCode::Failure));
142     NL_TEST_ASSERT(inSuite, protocolId == Protocols::kProtocol_BDX);
143     NL_TEST_ASSERT(inSuite, protocolCode == code);
144 }
145
146 void VerifyNoMoreOutput(nlTestSuite * inSuite, void * inContext, TransferSession & transferSession)
147 {
148     TransferSession::OutputEvent event;
149     transferSession.PollOutput(event, kNoAdvanceTime);
150     NL_TEST_ASSERT(inSuite, event.EventType == TransferSession::OutputEventType::kNone);
151 }
152
153 // Helper method for initializing two TransferSession objects, generating a TransferInit message, and passing it to a responding
154 // TransferSession.
155 void SendAndVerifyTransferInit(nlTestSuite * inSuite, void * inContext, TransferSession::OutputEvent & outEvent, uint32_t timeoutMs,
156                                TransferSession & initiator, TransferRole initiatorRole, TransferSession::TransferInitData initData,
157                                TransferSession & responder, BitFlags<TransferControlFlags> & responderControlOpts,
158                                uint16_t responderMaxBlock)
159 {
160     CHIP_ERROR err              = CHIP_NO_ERROR;
161     TransferRole responderRole  = (initiatorRole == TransferRole::kSender) ? TransferRole::kReceiver : TransferRole::kSender;
162     MessageType expectedInitMsg = (initiatorRole == TransferRole::kSender) ? MessageType::SendInit : MessageType::ReceiveInit;
163
164     // Initializer responder to wait for transfer
165     err = responder.WaitForTransfer(responderRole, responderControlOpts, responderMaxBlock, timeoutMs);
166     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
167     VerifyNoMoreOutput(inSuite, inContext, responder);
168
169     // Verify initiator outputs respective Init message (depending on role) after StartTransfer()
170     err = initiator.StartTransfer(initiatorRole, initData, timeoutMs);
171     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
172     initiator.PollOutput(outEvent, kNoAdvanceTime);
173     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
174     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expectedInitMsg);
175     VerifyNoMoreOutput(inSuite, inContext, initiator);
176
177     // Verify that all parsed TransferInit fields match what was sent by the initiator
178     err = responder.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
179     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
180     responder.PollOutput(outEvent, kNoAdvanceTime);
181     VerifyNoMoreOutput(inSuite, inContext, responder);
182     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kInitReceived);
183     NL_TEST_ASSERT(inSuite, outEvent.transferInitData.TransferCtlFlags == initData.TransferCtlFlags);
184     NL_TEST_ASSERT(inSuite, outEvent.transferInitData.MaxBlockSize == initData.MaxBlockSize);
185     NL_TEST_ASSERT(inSuite, outEvent.transferInitData.StartOffset == initData.StartOffset);
186     NL_TEST_ASSERT(inSuite, outEvent.transferInitData.Length == initData.Length);
187     NL_TEST_ASSERT(inSuite, outEvent.transferInitData.FileDesignator != nullptr);
188     NL_TEST_ASSERT(inSuite, outEvent.transferInitData.FileDesLength == initData.FileDesLength);
189     if (outEvent.EventType == TransferSession::OutputEventType::kInitReceived &&
190         outEvent.transferInitData.FileDesignator != nullptr)
191     {
192         NL_TEST_ASSERT(
193             inSuite,
194             !memcmp(initData.FileDesignator, outEvent.transferInitData.FileDesignator, outEvent.transferInitData.FileDesLength));
195     }
196     if (outEvent.transferInitData.Metadata != nullptr)
197     {
198         NL_TEST_ASSERT(inSuite, outEvent.transferInitData.MetadataLength == initData.MetadataLength);
199         if (outEvent.transferInitData.MetadataLength == initData.MetadataLength)
200         {
201             // Only check that metadata buffers match. The OutputEvent can still be inspected when this function returns to parse
202             // the metadata and verify that it matches.
203             NL_TEST_ASSERT(
204                 inSuite, !memcmp(initData.Metadata, outEvent.transferInitData.Metadata, outEvent.transferInitData.MetadataLength));
205         }
206         else
207         {
208             NL_TEST_ASSERT(inSuite, false); // Metadata length mismatch
209         }
210     }
211 }
212
213 // Helper method for sending an Accept message and verifying that the received parameters match what was sent.
214 // This function assumes that the acceptData struct contains transfer parameters that are valid responses to the original
215 // TransferInit message (for example, MaxBlockSize should be <= the TransferInit MaxBlockSize). If such parameters are invalid, the
216 // receiver should emit a StatusCode event instead.
217 //
218 // The acceptSender is the node that is sending the Accept message (not necessarily the same node that will send Blocks).
219 void SendAndVerifyAcceptMsg(nlTestSuite * inSuite, void * inContext, TransferSession::OutputEvent & outEvent,
220                             TransferSession & acceptSender, TransferRole acceptSenderRole,
221                             TransferSession::TransferAcceptData acceptData, TransferSession & acceptReceiver,
222                             TransferSession::TransferInitData initData)
223 {
224     CHIP_ERROR err = CHIP_NO_ERROR;
225
226     // If the node sending the Accept message is also the one that will send Blocks, then this should be a ReceiveAccept message.
227     MessageType expectedMsg = (acceptSenderRole == TransferRole::kSender) ? MessageType::ReceiveAccept : MessageType::SendAccept;
228
229     err = acceptSender.AcceptTransfer(acceptData);
230     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
231
232     // Verify Sender emits ReceiveAccept message for sending
233     acceptSender.PollOutput(outEvent, kNoAdvanceTime);
234     VerifyNoMoreOutput(inSuite, inContext, acceptSender);
235     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
236     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expectedMsg);
237
238     // Pass Accept message to acceptReceiver
239     err = acceptReceiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
240     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
241
242     // Verify received ReceiveAccept.
243     // Client may want to inspect TransferControl, MaxBlockSize, StartOffset, Length, and Metadata, and may choose to reject the
244     // Transfer at this point.
245     acceptReceiver.PollOutput(outEvent, kNoAdvanceTime);
246     VerifyNoMoreOutput(inSuite, inContext, acceptReceiver);
247     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kAcceptReceived);
248     NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.ControlMode == acceptData.ControlMode);
249     NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.MaxBlockSize == acceptData.MaxBlockSize);
250     NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.StartOffset == acceptData.StartOffset);
251     NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.Length == acceptData.Length);
252     if (outEvent.transferAcceptData.Metadata != nullptr)
253     {
254         NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.MetadataLength == acceptData.MetadataLength);
255         if (outEvent.transferAcceptData.MetadataLength == acceptData.MetadataLength)
256         {
257             // Only check that metadata buffers match. The OutputEvent can still be inspected when this function returns to parse
258             // the metadata and verify that it matches.
259             NL_TEST_ASSERT(
260                 inSuite,
261                 !memcmp(acceptData.Metadata, outEvent.transferAcceptData.Metadata, outEvent.transferAcceptData.MetadataLength));
262         }
263         else
264         {
265             NL_TEST_ASSERT(inSuite, false); // Metadata length mismatch
266         }
267     }
268
269     // Verify that MaxBlockSize was set appropriately
270     NL_TEST_ASSERT(inSuite, acceptReceiver.GetTransferBlockSize() <= initData.MaxBlockSize);
271 }
272
273 // Helper method for preparing a sending a BlockQuery message between two TransferSession objects.
274 void SendAndVerifyQuery(nlTestSuite * inSuite, void * inContext, TransferSession & queryReceiver, TransferSession & querySender,
275                         TransferSession::OutputEvent & outEvent)
276 {
277     // Verify that querySender emits BlockQuery message
278     CHIP_ERROR err = querySender.PrepareBlockQuery();
279     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
280     querySender.PollOutput(outEvent, kNoAdvanceTime);
281     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
282     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, MessageType::BlockQuery);
283     VerifyNoMoreOutput(inSuite, inContext, querySender);
284
285     // Pass BlockQuery to queryReceiver and verify queryReceiver emits QueryReceived event
286     err = queryReceiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
287     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
288     queryReceiver.PollOutput(outEvent, kNoAdvanceTime);
289     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kQueryReceived);
290     VerifyNoMoreOutput(inSuite, inContext, queryReceiver);
291 }
292
293 // Helper method for preparing a sending a Block message between two TransferSession objects. The sender refers to the node that is
294 // sending Blocks. Uses a static counter incremented with each call. Also verifies that block data received matches what was sent.
295 void SendAndVerifyArbitraryBlock(nlTestSuite * inSuite, void * inContext, TransferSession & sender, TransferSession & receiver,
296                                  TransferSession::OutputEvent & outEvent, bool isEof)
297 {
298     CHIP_ERROR err           = CHIP_NO_ERROR;
299     static uint8_t dataCount = 0;
300     uint16_t maxBlockSize    = sender.GetTransferBlockSize();
301
302     NL_TEST_ASSERT(inSuite, maxBlockSize > 0);
303     System::PacketBufferHandle fakeDataBuf = System::PacketBufferHandle::New(maxBlockSize);
304     if (fakeDataBuf.IsNull())
305     {
306         NL_TEST_ASSERT(inSuite, false);
307         return;
308     }
309
310     uint8_t * fakeBlockData = fakeDataBuf->Start();
311     fakeBlockData[0]        = dataCount++;
312
313     TransferSession::BlockData blockData;
314     blockData.Data   = fakeBlockData;
315     blockData.Length = maxBlockSize;
316     blockData.IsEof  = isEof;
317
318     MessageType expected = isEof ? MessageType::BlockEOF : MessageType::Block;
319
320     // Provide Block data and verify sender emits Block message
321     err = sender.PrepareBlock(blockData);
322     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
323     sender.PollOutput(outEvent, kNoAdvanceTime);
324     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
325     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expected);
326     VerifyNoMoreOutput(inSuite, inContext, sender);
327
328     // Pass Block message to receiver and verify matching Block is received
329     err = receiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
330     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
331     receiver.PollOutput(outEvent, kNoAdvanceTime);
332     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kBlockReceived);
333     NL_TEST_ASSERT(inSuite, outEvent.blockdata.Data != nullptr);
334     if (outEvent.EventType == TransferSession::OutputEventType::kBlockReceived && outEvent.blockdata.Data != nullptr)
335     {
336         NL_TEST_ASSERT(inSuite, !memcmp(fakeBlockData, outEvent.blockdata.Data, outEvent.blockdata.Length));
337     }
338     VerifyNoMoreOutput(inSuite, inContext, receiver);
339 }
340
341 // Helper method for sending a BlockAck or BlockAckEOF, depending on the state of the receiver.
342 void SendAndVerifyBlockAck(nlTestSuite * inSuite, void * inContext, TransferSession & ackReceiver, TransferSession & ackSender,
343                            TransferSession::OutputEvent & outEvent, bool expectEOF)
344 {
345     TransferSession::OutputEventType expectedEventType =
346         expectEOF ? TransferSession::OutputEventType::kAckEOFReceived : TransferSession::OutputEventType::kAckReceived;
347     MessageType expectedMsgType = expectEOF ? MessageType::BlockAckEOF : MessageType::BlockAck;
348
349     // Verify PrepareBlockAck() outputs message to send
350     CHIP_ERROR err = ackSender.PrepareBlockAck();
351     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
352     ackSender.PollOutput(outEvent, kNoAdvanceTime);
353     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
354     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expectedMsgType);
355     VerifyNoMoreOutput(inSuite, inContext, ackSender);
356
357     // Pass BlockAck to ackReceiver and verify it was received
358     err = ackReceiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
359     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
360     ackReceiver.PollOutput(outEvent, kNoAdvanceTime);
361     NL_TEST_ASSERT(inSuite, outEvent.EventType == expectedEventType);
362     VerifyNoMoreOutput(inSuite, inContext, ackReceiver);
363 }
364
365 // Test a full transfer using a responding receiver and an initiating sender, receiver drive.
366 void TestInitiatingReceiverReceiverDrive(nlTestSuite * inSuite, void * inContext)
367 {
368     CHIP_ERROR err = CHIP_NO_ERROR;
369     TransferSession::OutputEvent outEvent;
370     TransferSession initiatingReceiver;
371     TransferSession respondingSender;
372     uint32_t numBlocksSent = 0;
373
374     // Chosen arbitrarily for this test
375     uint32_t numBlockSends        = 10;
376     uint16_t proposedBlockSize    = 128;
377     uint16_t testSmallerBlockSize = 64;
378     uint64_t proposedOffset       = 64;
379     uint64_t proposedLength       = 0;
380     uint32_t timeoutMs            = 1000 * 24;
381
382     // Chosen specifically for this test
383     TransferControlFlags driveMode = TransferControlFlags::kReceiverDrive;
384
385     // ReceiveInit parameters
386     TransferSession::TransferInitData initOptions;
387     initOptions.TransferCtlFlags = driveMode;
388     initOptions.MaxBlockSize     = proposedBlockSize;
389     char testFileDes[9]          = { "test.txt" };
390     initOptions.FileDesLength    = static_cast<uint16_t>(strlen(testFileDes));
391     initOptions.FileDesignator   = reinterpret_cast<uint8_t *>(testFileDes);
392
393     // Initialize respondingSender and pass ReceiveInit message
394     BitFlags<TransferControlFlags> senderOpts;
395     senderOpts.Set(driveMode);
396
397     SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingReceiver, TransferRole::kReceiver, initOptions,
398                               respondingSender, senderOpts, proposedBlockSize);
399
400     // Test metadata for Accept message
401     uint8_t tlvBuf[64]    = { 0 };
402     char metadataStr[11]  = { "hi_dad.txt" };
403     uint32_t bytesWritten = 0;
404     err                   = WriteChipTLVString(tlvBuf, sizeof(tlvBuf), metadataStr, bytesWritten);
405     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
406     uint16_t metadataSize = static_cast<uint16_t>(bytesWritten & 0x0000FFFF);
407
408     // Compose ReceiveAccept parameters struct and give to respondingSender
409     TransferSession::TransferAcceptData acceptData;
410     acceptData.ControlMode    = respondingSender.GetControlMode();
411     acceptData.StartOffset    = proposedOffset;
412     acceptData.Length         = proposedLength;
413     acceptData.MaxBlockSize   = testSmallerBlockSize;
414     acceptData.Metadata       = tlvBuf;
415     acceptData.MetadataLength = metadataSize;
416
417     SendAndVerifyAcceptMsg(inSuite, inContext, outEvent, respondingSender, TransferRole::kSender, acceptData, initiatingReceiver,
418                            initOptions);
419
420     // Verify that MaxBlockSize was chosen correctly
421     NL_TEST_ASSERT(inSuite, respondingSender.GetTransferBlockSize() == testSmallerBlockSize);
422     NL_TEST_ASSERT(inSuite, respondingSender.GetTransferBlockSize() == initiatingReceiver.GetTransferBlockSize());
423
424     // Verify parsed TLV metadata matches the original
425     err =
426         ReadAndVerifyTLVString(inSuite, inContext, outEvent.transferAcceptData.Metadata, outEvent.transferAcceptData.MetadataLength,
427                                metadataStr, static_cast<uint16_t>(strlen(metadataStr)));
428     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
429
430     // Test BlockQuery -> Block -> BlockAck
431     SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
432     SendAndVerifyArbitraryBlock(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, false);
433     numBlocksSent++;
434
435     // Test only one block can be prepared at a time, without receiving a response to the first
436     System::PacketBufferHandle fakeBuf = System::PacketBufferHandle::New(testSmallerBlockSize);
437     TransferSession::BlockData prematureBlock;
438     if (fakeBuf.IsNull())
439     {
440         NL_TEST_ASSERT(inSuite, false);
441         return;
442     }
443     prematureBlock.Data   = fakeBuf->Start();
444     prematureBlock.Length = testSmallerBlockSize;
445     prematureBlock.IsEof  = false;
446     err                   = respondingSender.PrepareBlock(prematureBlock);
447     NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR);
448     VerifyNoMoreOutput(inSuite, inContext, respondingSender);
449
450     // Test Ack -> Query -> Block
451     SendAndVerifyBlockAck(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, false);
452
453     // Test multiple Blocks sent and received (last Block is BlockEOF)
454     while (numBlocksSent < numBlockSends)
455     {
456         bool isEof = (numBlocksSent == numBlockSends - 1);
457
458         SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
459         SendAndVerifyArbitraryBlock(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, isEof);
460
461         numBlocksSent++;
462     }
463
464     // Verify last block was BlockEOF, then verify response BlockAckEOF message
465     NL_TEST_ASSERT(inSuite, outEvent.blockdata.IsEof == true);
466     SendAndVerifyBlockAck(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, true);
467 }
468
469 // Partial transfer test using Sender Drive to specifically test Block -> BlockAck -> Block sequence
470 void TestInitiatingSenderSenderDrive(nlTestSuite * inSuite, void * inContext)
471 {
472     CHIP_ERROR err = CHIP_NO_ERROR;
473     TransferSession::OutputEvent outEvent;
474     TransferSession initiatingSender;
475     TransferSession respondingReceiver;
476
477     TransferControlFlags driveMode = TransferControlFlags::kSenderDrive;
478
479     // Chosen arbitrarily for this test
480     uint16_t transferBlockSize = 10;
481     uint32_t timeoutMs         = 1000 * 24;
482
483     // Initialize respondingReceiver
484     BitFlags<TransferControlFlags> receiverOpts;
485     receiverOpts.Set(driveMode);
486
487     // Test metadata for TransferInit message
488     uint8_t tlvBuf[64]    = { 0 };
489     char metadataStr[11]  = { "hi_dad.txt" };
490     uint32_t bytesWritten = 0;
491     err                   = WriteChipTLVString(tlvBuf, sizeof(tlvBuf), metadataStr, bytesWritten);
492     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
493     uint16_t metadataSize = static_cast<uint16_t>(bytesWritten & 0x0000FFFF);
494
495     // Initialize struct with TransferInit parameters
496     TransferSession::TransferInitData initOptions;
497     initOptions.TransferCtlFlags = driveMode;
498     initOptions.MaxBlockSize     = transferBlockSize;
499     char testFileDes[9]          = { "test.txt" };
500     initOptions.FileDesLength    = static_cast<uint16_t>(strlen(testFileDes));
501     initOptions.FileDesignator   = reinterpret_cast<uint8_t *>(testFileDes);
502     initOptions.Metadata         = tlvBuf;
503     initOptions.MetadataLength   = metadataSize;
504
505     SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingSender, TransferRole::kSender, initOptions,
506                               respondingReceiver, receiverOpts, transferBlockSize);
507
508     // Verify parsed TLV metadata matches the original
509     err = ReadAndVerifyTLVString(inSuite, inContext, outEvent.transferInitData.Metadata, outEvent.transferInitData.MetadataLength,
510                                  metadataStr, static_cast<uint16_t>(strlen(metadataStr)));
511     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
512
513     // Compose SendAccept parameters struct and give to respondingSender
514     uint16_t proposedBlockSize = transferBlockSize;
515     TransferSession::TransferAcceptData acceptData;
516     acceptData.ControlMode    = respondingReceiver.GetControlMode();
517     acceptData.MaxBlockSize   = proposedBlockSize;
518     acceptData.StartOffset    = 0; // not used in SendAccept
519     acceptData.Length         = 0; // not used in SendAccept
520     acceptData.Metadata       = nullptr;
521     acceptData.MetadataLength = 0;
522
523     SendAndVerifyAcceptMsg(inSuite, inContext, outEvent, respondingReceiver, TransferRole::kReceiver, acceptData, initiatingSender,
524                            initOptions);
525
526     // Test multiple Block -> BlockAck -> Block
527     for (int i = 0; i < 3; i++)
528     {
529         SendAndVerifyArbitraryBlock(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, false);
530         SendAndVerifyBlockAck(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, false);
531     }
532
533     SendAndVerifyArbitraryBlock(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, true);
534     SendAndVerifyBlockAck(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, true);
535 }
536
537 // Test that calls to AcceptTransfer() with bad parameters result in an error.
538 void TestBadAcceptMessageFields(nlTestSuite * inSuite, void * inContext)
539 {
540     CHIP_ERROR err = CHIP_NO_ERROR;
541     TransferSession::OutputEvent outEvent;
542     TransferSession initiatingReceiver;
543     TransferSession respondingSender;
544
545     uint16_t maxBlockSize          = 64;
546     TransferControlFlags driveMode = TransferControlFlags::kReceiverDrive;
547     uint64_t commonLength          = 0;
548     uint64_t commonOffset          = 0;
549     uint32_t timeoutMs             = 1000 * 24;
550
551     // Initialize struct with TransferInit parameters
552     TransferSession::TransferInitData initOptions;
553     initOptions.TransferCtlFlags = driveMode;
554     initOptions.MaxBlockSize     = maxBlockSize;
555     initOptions.StartOffset      = commonOffset;
556     initOptions.Length           = commonLength;
557     char testFileDes[9]          = { "test.txt" }; // arbitrary file designator
558     initOptions.FileDesLength    = static_cast<uint16_t>(strlen(testFileDes));
559     initOptions.FileDesignator   = reinterpret_cast<uint8_t *>(testFileDes);
560     initOptions.Metadata         = nullptr;
561     initOptions.MetadataLength   = 0;
562
563     // Responder parameters
564     BitFlags<TransferControlFlags> responderControl;
565     responderControl.Set(driveMode);
566
567     SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingReceiver, TransferRole::kReceiver, initOptions,
568                               respondingSender, responderControl, maxBlockSize);
569
570     // Verify AcceptTransfer() returns error for choosing larger max block size
571     TransferSession::TransferAcceptData acceptData;
572     acceptData.ControlMode  = driveMode;
573     acceptData.MaxBlockSize = static_cast<uint16_t>(maxBlockSize + 1); // invalid if larger than proposed
574     acceptData.StartOffset  = commonOffset;
575     acceptData.Length       = commonLength;
576     err                     = respondingSender.AcceptTransfer(acceptData);
577     NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR);
578
579     // Verify AcceptTransfer() returns error for choosing unsupported transfer control mode
580     TransferSession::TransferAcceptData acceptData2;
581     acceptData2.ControlMode = (driveMode == TransferControlFlags::kReceiverDrive) ? TransferControlFlags::kSenderDrive
582                                                                                   : TransferControlFlags::kReceiverDrive;
583     acceptData2.MaxBlockSize = maxBlockSize;
584     acceptData2.StartOffset  = commonOffset;
585     acceptData2.Length       = commonLength;
586     err                      = respondingSender.AcceptTransfer(acceptData2);
587     NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR);
588 }
589
590 // Test that a TransferSession will emit kTransferTimeout if the specified timeout is exceeded while waiting for a response.
591 void TestTimeout(nlTestSuite * inSuite, void * inContext)
592 {
593     CHIP_ERROR err = CHIP_NO_ERROR;
594     TransferSession initiator;
595     TransferSession::OutputEvent outEvent;
596
597     uint32_t timeoutMs   = 24;
598     uint64_t startTimeMs = 100;
599     uint64_t endTimeMs   = 124;
600
601     // Initialize struct with arbitrary TransferInit parameters
602     TransferSession::TransferInitData initOptions;
603     initOptions.TransferCtlFlags = TransferControlFlags::kReceiverDrive;
604     initOptions.MaxBlockSize     = 64;
605     initOptions.StartOffset      = 0;
606     initOptions.Length           = 0;
607     char testFileDes[9]          = { "test.txt" }; // arbitrary file designator
608     initOptions.FileDesLength    = static_cast<uint16_t>(strlen(testFileDes));
609     initOptions.FileDesignator   = reinterpret_cast<uint8_t *>(testFileDes);
610     initOptions.Metadata         = nullptr;
611     initOptions.MetadataLength   = 0;
612
613     TransferRole role = TransferRole::kReceiver;
614
615     // Verify initiator outputs respective Init message (depending on role) after StartTransfer()
616     err = initiator.StartTransfer(role, initOptions, timeoutMs);
617     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
618
619     // First PollOutput() should output the TransferInit message
620     initiator.PollOutput(outEvent, startTimeMs);
621     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
622     MessageType expectedInitMsg = (role == TransferRole::kSender) ? MessageType::SendInit : MessageType::ReceiveInit;
623     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expectedInitMsg);
624
625     // Second PollOutput() with no call to HandleMessageReceived() should result in a timeout.
626     initiator.PollOutput(outEvent, endTimeMs);
627     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kTransferTimeout);
628 }
629
630 // Test that sending the same block twice (with same block counter) results in a StatusReport message with BadBlockCounter. Also
631 // test that receiving the StatusReport ends the transfer on the other node.
632 void TestDuplicateBlockError(nlTestSuite * inSuite, void * inContext)
633 {
634     CHIP_ERROR err = CHIP_NO_ERROR;
635     TransferSession::OutputEvent outEvent;
636     TransferSession::OutputEvent eventWithBlock;
637     TransferSession initiatingReceiver;
638     TransferSession respondingSender;
639
640     uint8_t fakeData[64] = { 0 };
641     uint8_t fakeDataLen  = sizeof(fakeData);
642     uint16_t blockSize   = sizeof(fakeData);
643
644     // Chosen arbitrarily for this test
645     uint64_t proposedOffset = 64;
646     uint64_t proposedLength = 0;
647     uint32_t timeoutMs      = 1000 * 24;
648
649     // Chosen specifically for this test
650     TransferControlFlags driveMode = TransferControlFlags::kReceiverDrive;
651
652     // ReceiveInit parameters
653     TransferSession::TransferInitData initOptions;
654     initOptions.TransferCtlFlags = driveMode;
655     initOptions.MaxBlockSize     = blockSize;
656     char testFileDes[9]          = { "test.txt" };
657     initOptions.FileDesLength    = static_cast<uint16_t>(strlen(testFileDes));
658     initOptions.FileDesignator   = reinterpret_cast<uint8_t *>(testFileDes);
659
660     // Initialize respondingSender and pass ReceiveInit message
661     BitFlags<TransferControlFlags> senderOpts;
662     senderOpts.Set(driveMode);
663
664     SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingReceiver, TransferRole::kReceiver, initOptions,
665                               respondingSender, senderOpts, blockSize);
666
667     // Compose ReceiveAccept parameters struct and give to respondingSender
668     TransferSession::TransferAcceptData acceptData;
669     acceptData.ControlMode    = respondingSender.GetControlMode();
670     acceptData.StartOffset    = proposedOffset;
671     acceptData.Length         = proposedLength;
672     acceptData.MaxBlockSize   = blockSize;
673     acceptData.Metadata       = nullptr;
674     acceptData.MetadataLength = 0;
675
676     SendAndVerifyAcceptMsg(inSuite, inContext, outEvent, respondingSender, TransferRole::kSender, acceptData, initiatingReceiver,
677                            initOptions);
678
679     SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
680
681     TransferSession::BlockData blockData;
682     blockData.Data   = fakeData;
683     blockData.Length = fakeDataLen;
684     blockData.IsEof  = false;
685
686     // Provide Block data and verify sender emits Block message
687     err = respondingSender.PrepareBlock(blockData);
688     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
689     respondingSender.PollOutput(eventWithBlock, kNoAdvanceTime);
690     NL_TEST_ASSERT(inSuite, eventWithBlock.EventType == TransferSession::OutputEventType::kMsgToSend);
691     VerifyBdxMessageType(inSuite, inContext, eventWithBlock.MsgData, MessageType::Block);
692     VerifyNoMoreOutput(inSuite, inContext, respondingSender);
693     System::PacketBufferHandle blockCopy =
694         System::PacketBufferHandle::NewWithData(eventWithBlock.MsgData->Start(), eventWithBlock.MsgData->DataLength());
695
696     // Pass Block message to receiver and verify matching Block is received
697     err = initiatingReceiver.HandleMessageReceived(std::move(eventWithBlock.MsgData), kNoAdvanceTime);
698     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
699     initiatingReceiver.PollOutput(outEvent, kNoAdvanceTime);
700     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kBlockReceived);
701     NL_TEST_ASSERT(inSuite, outEvent.blockdata.Data != nullptr);
702     VerifyNoMoreOutput(inSuite, inContext, initiatingReceiver);
703
704     SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
705
706     // Verify receiving same Block twice fails and results in StatusReport event, and then InternalError event
707     err = initiatingReceiver.HandleMessageReceived(std::move(blockCopy), kNoAdvanceTime);
708     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
709     initiatingReceiver.PollOutput(outEvent, kNoAdvanceTime);
710     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
711     System::PacketBufferHandle statusReportMsg = outEvent.MsgData.Retain();
712     VerifyStatusReport(inSuite, inContext, std::move(outEvent.MsgData), StatusCode::kBadBlockCounter);
713
714     // All subsequent PollOutput() calls should return kInternalError
715     for (int i = 0; i < 5; ++i)
716     {
717         initiatingReceiver.PollOutput(outEvent, kNoAdvanceTime);
718         NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kInternalError);
719         NL_TEST_ASSERT(inSuite, outEvent.statusData.statusCode == StatusCode::kBadBlockCounter);
720     }
721
722     err = respondingSender.HandleMessageReceived(std::move(statusReportMsg), kNoAdvanceTime);
723     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
724     respondingSender.PollOutput(outEvent, kNoAdvanceTime);
725     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kStatusReceived);
726     NL_TEST_ASSERT(inSuite, outEvent.statusData.statusCode == StatusCode::kBadBlockCounter);
727
728     // All subsequent PollOutput() calls should return kInternalError
729     for (int i = 0; i < 5; ++i)
730     {
731         respondingSender.PollOutput(outEvent, kNoAdvanceTime);
732         NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kInternalError);
733         NL_TEST_ASSERT(inSuite, outEvent.statusData.statusCode == StatusCode::kBadBlockCounter);
734     }
735 }
736
737 // Test Suite
738
739 /**
740  *  Test Suite that lists all the test functions.
741  */
742 // clang-format off
743 static const nlTest sTests[] =
744 {
745     NL_TEST_DEF("TestInitiatingReceiverReceiverDrive", TestInitiatingReceiverReceiverDrive),
746     NL_TEST_DEF("TestInitiatingSenderSenderDrive", TestInitiatingSenderSenderDrive),
747     NL_TEST_DEF("TestBadAcceptMessageFields", TestBadAcceptMessageFields),
748     NL_TEST_DEF("TestTimeout", TestTimeout),
749     NL_TEST_DEF("TestDuplicateBlockError", TestDuplicateBlockError),
750     NL_TEST_SENTINEL()
751 };
752 // clang-format on
753
754 int TestBdxTransferSession_Setup(void * inContext)
755 {
756     CHIP_ERROR error = chip::Platform::MemoryInit();
757     if (error != CHIP_NO_ERROR)
758         return FAILURE;
759     return SUCCESS;
760 }
761
762 int TestBdxTransferSession_Teardown(void * inContext)
763 {
764     chip::Platform::MemoryShutdown();
765     return SUCCESS;
766 }
767
768 // clang-format off
769 static nlTestSuite sSuite =
770 {
771     "Test-CHIP-TransferSession",
772     &sTests[0],
773     TestBdxTransferSession_Setup,
774     TestBdxTransferSession_Teardown
775 };
776 // clang-format on
777
778 /**
779  *  Main
780  */
781 int TestBdxTransferSession()
782 {
783     // Run test suit against one context
784     nlTestRunner(&sSuite, nullptr);
785
786     return (nlTestRunnerStats(&sSuite));
787 }
788
789 CHIP_REGISTER_TEST_SUITE(TestBdxTransferSession)