ab362a5e59a2eb6f47db1f2e56f69e2b7980704c
[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     uint16_t protocolCode = 0;
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).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::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<uint8_t, TransferControlFlags> & responderControlOpts,
158                                uint16_t responderMaxBlock)
159 {
160     CHIP_ERROR err              = CHIP_NO_ERROR;
161     TransferRole responderRole  = (initiatorRole == kRole_Sender) ? kRole_Receiver : kRole_Sender;
162     MessageType expectedInitMsg = (initiatorRole == kRole_Sender) ? 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::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::kInitReceived);
183     NL_TEST_ASSERT(inSuite, outEvent.transferInitData.TransferCtlFlagsRaw == initData.TransferCtlFlagsRaw);
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::kInitReceived && outEvent.transferInitData.FileDesignator != nullptr)
190     {
191         NL_TEST_ASSERT(
192             inSuite,
193             !memcmp(initData.FileDesignator, outEvent.transferInitData.FileDesignator, outEvent.transferInitData.FileDesLength));
194     }
195     if (outEvent.transferInitData.Metadata != nullptr)
196     {
197         NL_TEST_ASSERT(inSuite, outEvent.transferInitData.MetadataLength == initData.MetadataLength);
198         if (outEvent.transferInitData.MetadataLength == initData.MetadataLength)
199         {
200             // Only check that metadata buffers match. The OutputEvent can still be inspected when this function returns to parse
201             // the metadata and verify that it matches.
202             NL_TEST_ASSERT(
203                 inSuite, !memcmp(initData.Metadata, outEvent.transferInitData.Metadata, outEvent.transferInitData.MetadataLength));
204         }
205         else
206         {
207             NL_TEST_ASSERT(inSuite, false); // Metadata length mismatch
208         }
209     }
210 }
211
212 // Helper method for sending an Accept message and verifying that the received parameters match what was sent.
213 // This function assumes that the acceptData struct contains transfer parameters that are valid responses to the original
214 // TransferInit message (for example, MaxBlockSize should be <= the TransferInit MaxBlockSize). If such parameters are invalid, the
215 // receiver should emit a StatusCode event instead.
216 //
217 // The acceptSender is the node that is sending the Accept message (not necessarily the same node that will send Blocks).
218 void SendAndVerifyAcceptMsg(nlTestSuite * inSuite, void * inContext, TransferSession::OutputEvent & outEvent,
219                             TransferSession & acceptSender, TransferRole acceptSenderRole,
220                             TransferSession::TransferAcceptData acceptData, TransferSession & acceptReceiver,
221                             TransferSession::TransferInitData initData)
222 {
223     CHIP_ERROR err = CHIP_NO_ERROR;
224
225     // If the node sending the Accept message is also the one that will send Blocks, then this should be a ReceiveAccept message.
226     MessageType expectedMsg = (acceptSenderRole == kRole_Sender) ? MessageType::ReceiveAccept : MessageType::SendAccept;
227
228     err = acceptSender.AcceptTransfer(acceptData);
229     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
230
231     // Verify Sender emits ReceiveAccept message for sending
232     acceptSender.PollOutput(outEvent, kNoAdvanceTime);
233     VerifyNoMoreOutput(inSuite, inContext, acceptSender);
234     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kMsgToSend);
235     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expectedMsg);
236
237     // Pass Accept message to acceptReceiver
238     err = acceptReceiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
239     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
240
241     // Verify received ReceiveAccept.
242     // Client may want to inspect TransferControl, MaxBlockSize, StartOffset, Length, and Metadata, and may choose to reject the
243     // Transfer at this point.
244     acceptReceiver.PollOutput(outEvent, kNoAdvanceTime);
245     VerifyNoMoreOutput(inSuite, inContext, acceptReceiver);
246     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kAcceptReceived);
247     NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.ControlMode == acceptData.ControlMode);
248     NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.MaxBlockSize == acceptData.MaxBlockSize);
249     NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.StartOffset == acceptData.StartOffset);
250     NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.Length == acceptData.Length);
251     if (outEvent.transferAcceptData.Metadata != nullptr)
252     {
253         NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.MetadataLength == acceptData.MetadataLength);
254         if (outEvent.transferAcceptData.MetadataLength == acceptData.MetadataLength)
255         {
256             // Only check that metadata buffers match. The OutputEvent can still be inspected when this function returns to parse
257             // the metadata and verify that it matches.
258             NL_TEST_ASSERT(
259                 inSuite,
260                 !memcmp(acceptData.Metadata, outEvent.transferAcceptData.Metadata, outEvent.transferAcceptData.MetadataLength));
261         }
262         else
263         {
264             NL_TEST_ASSERT(inSuite, false); // Metadata length mismatch
265         }
266     }
267
268     // Verify that MaxBlockSize was set appropriately
269     NL_TEST_ASSERT(inSuite, acceptReceiver.GetTransferBlockSize() <= initData.MaxBlockSize);
270 }
271
272 // Helper method for preparing a sending a BlockQuery message between two TransferSession objects.
273 void SendAndVerifyQuery(nlTestSuite * inSuite, void * inContext, TransferSession & queryReceiver, TransferSession & querySender,
274                         TransferSession::OutputEvent & outEvent)
275 {
276     // Verify that querySender emits BlockQuery message
277     CHIP_ERROR err = querySender.PrepareBlockQuery();
278     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
279     querySender.PollOutput(outEvent, kNoAdvanceTime);
280     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kMsgToSend);
281     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, MessageType::BlockQuery);
282     VerifyNoMoreOutput(inSuite, inContext, querySender);
283
284     // Pass BlockQuery to queryReceiver and verify queryReceiver emits QueryReceived event
285     err = queryReceiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
286     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
287     queryReceiver.PollOutput(outEvent, kNoAdvanceTime);
288     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kQueryReceived);
289     VerifyNoMoreOutput(inSuite, inContext, queryReceiver);
290 }
291
292 // Helper method for preparing a sending a Block message between two TransferSession objects. The sender refers to the node that is
293 // sending Blocks. Uses a static counter incremented with each call. Also verifies that block data received matches what was sent.
294 void SendAndVerifyArbitraryBlock(nlTestSuite * inSuite, void * inContext, TransferSession & sender, TransferSession & receiver,
295                                  TransferSession::OutputEvent & outEvent, bool isEof)
296 {
297     CHIP_ERROR err           = CHIP_NO_ERROR;
298     static uint8_t dataCount = 0;
299     uint16_t maxBlockSize    = sender.GetTransferBlockSize();
300
301     NL_TEST_ASSERT(inSuite, maxBlockSize > 0);
302     System::PacketBufferHandle fakeDataBuf = System::PacketBufferHandle::New(maxBlockSize);
303     if (fakeDataBuf.IsNull())
304     {
305         NL_TEST_ASSERT(inSuite, false);
306         return;
307     }
308
309     uint8_t * fakeBlockData = fakeDataBuf->Start();
310     fakeBlockData[0]        = dataCount++;
311
312     TransferSession::BlockData blockData;
313     blockData.Data   = fakeBlockData;
314     blockData.Length = maxBlockSize;
315     blockData.IsEof  = isEof;
316
317     MessageType expected = isEof ? MessageType::BlockEOF : MessageType::Block;
318
319     // Provide Block data and verify sender emits Block message
320     err = sender.PrepareBlock(blockData);
321     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
322     sender.PollOutput(outEvent, kNoAdvanceTime);
323     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kMsgToSend);
324     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expected);
325     VerifyNoMoreOutput(inSuite, inContext, sender);
326
327     // Pass Block message to receiver and verify matching Block is received
328     err = receiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
329     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
330     receiver.PollOutput(outEvent, kNoAdvanceTime);
331     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kBlockReceived);
332     NL_TEST_ASSERT(inSuite, outEvent.blockdata.Data != nullptr);
333     if (outEvent.EventType == TransferSession::kBlockReceived && outEvent.blockdata.Data != nullptr)
334     {
335         NL_TEST_ASSERT(inSuite, !memcmp(fakeBlockData, outEvent.blockdata.Data, outEvent.blockdata.Length));
336     }
337     VerifyNoMoreOutput(inSuite, inContext, receiver);
338 }
339
340 // Helper method for sending a BlockAck or BlockAckEOF, depending on the state of the receiver.
341 void SendAndVerifyBlockAck(nlTestSuite * inSuite, void * inContext, TransferSession & ackReceiver, TransferSession & ackSender,
342                            TransferSession::OutputEvent & outEvent, bool expectEOF)
343 {
344     TransferSession::OutputEventType expectedEventType =
345         expectEOF ? TransferSession::kAckEOFReceived : TransferSession::kAckReceived;
346     MessageType expectedMsgType = expectEOF ? MessageType::BlockAckEOF : MessageType::BlockAck;
347
348     // Verify PrepareBlockAck() outputs message to send
349     CHIP_ERROR err = ackSender.PrepareBlockAck();
350     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
351     ackSender.PollOutput(outEvent, kNoAdvanceTime);
352     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kMsgToSend);
353     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expectedMsgType);
354     VerifyNoMoreOutput(inSuite, inContext, ackSender);
355
356     // Pass BlockAck to ackReceiver and verify it was received
357     err = ackReceiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
358     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
359     ackReceiver.PollOutput(outEvent, kNoAdvanceTime);
360     NL_TEST_ASSERT(inSuite, outEvent.EventType == expectedEventType);
361     VerifyNoMoreOutput(inSuite, inContext, ackReceiver);
362 }
363
364 // Test a full transfer using a responding receiver and an initiating sender, receiver drive.
365 void TestInitiatingReceiverReceiverDrive(nlTestSuite * inSuite, void * inContext)
366 {
367     CHIP_ERROR err = CHIP_NO_ERROR;
368     TransferSession::OutputEvent outEvent;
369     TransferSession initiatingReceiver;
370     TransferSession respondingSender;
371     uint32_t numBlocksSent = 0;
372
373     // Chosen arbitrarily for this test
374     uint32_t numBlockSends        = 10;
375     uint16_t proposedBlockSize    = 128;
376     uint16_t testSmallerBlockSize = 64;
377     uint64_t proposedOffset       = 64;
378     uint64_t proposedLength       = 0;
379     uint32_t timeoutMs            = 1000 * 24;
380
381     // Chosen specifically for this test
382     TransferControlFlags driveMode = kControl_ReceiverDrive;
383
384     // ReceiveInit parameters
385     TransferSession::TransferInitData initOptions;
386     initOptions.TransferCtlFlagsRaw = driveMode;
387     initOptions.MaxBlockSize        = proposedBlockSize;
388     char testFileDes[9]             = { "test.txt" };
389     initOptions.FileDesLength       = static_cast<uint16_t>(strlen(testFileDes));
390     initOptions.FileDesignator      = reinterpret_cast<uint8_t *>(testFileDes);
391
392     // Initialize respondingSender and pass ReceiveInit message
393     BitFlags<uint8_t, TransferControlFlags> senderOpts;
394     senderOpts.Set(driveMode);
395
396     SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingReceiver, kRole_Receiver, initOptions,
397                               respondingSender, senderOpts, proposedBlockSize);
398
399     // Test metadata for Accept message
400     uint8_t tlvBuf[64]    = { 0 };
401     char metadataStr[11]  = { "hi_dad.txt" };
402     uint32_t bytesWritten = 0;
403     err                   = WriteChipTLVString(tlvBuf, sizeof(tlvBuf), metadataStr, bytesWritten);
404     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
405     uint16_t metadataSize = static_cast<uint16_t>(bytesWritten & 0x0000FFFF);
406
407     // Compose ReceiveAccept parameters struct and give to respondingSender
408     TransferSession::TransferAcceptData acceptData;
409     acceptData.ControlMode    = respondingSender.GetControlMode();
410     acceptData.StartOffset    = proposedOffset;
411     acceptData.Length         = proposedLength;
412     acceptData.MaxBlockSize   = testSmallerBlockSize;
413     acceptData.Metadata       = tlvBuf;
414     acceptData.MetadataLength = metadataSize;
415
416     SendAndVerifyAcceptMsg(inSuite, inContext, outEvent, respondingSender, kRole_Sender, acceptData, initiatingReceiver,
417                            initOptions);
418
419     // Verify that MaxBlockSize was chosen correctly
420     NL_TEST_ASSERT(inSuite, respondingSender.GetTransferBlockSize() == testSmallerBlockSize);
421     NL_TEST_ASSERT(inSuite, respondingSender.GetTransferBlockSize() == initiatingReceiver.GetTransferBlockSize());
422
423     // Verify parsed TLV metadata matches the original
424     err =
425         ReadAndVerifyTLVString(inSuite, inContext, outEvent.transferAcceptData.Metadata, outEvent.transferAcceptData.MetadataLength,
426                                metadataStr, static_cast<uint16_t>(strlen(metadataStr)));
427     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
428
429     // Test BlockQuery -> Block -> BlockAck
430     SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
431     SendAndVerifyArbitraryBlock(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, false);
432     numBlocksSent++;
433
434     // Test only one block can be prepared at a time, without receiving a response to the first
435     System::PacketBufferHandle fakeBuf = System::PacketBufferHandle::New(testSmallerBlockSize);
436     TransferSession::BlockData prematureBlock;
437     if (fakeBuf.IsNull())
438     {
439         NL_TEST_ASSERT(inSuite, false);
440         return;
441     }
442     prematureBlock.Data   = fakeBuf->Start();
443     prematureBlock.Length = testSmallerBlockSize;
444     prematureBlock.IsEof  = false;
445     err                   = respondingSender.PrepareBlock(prematureBlock);
446     NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR);
447     VerifyNoMoreOutput(inSuite, inContext, respondingSender);
448
449     // Test Ack -> Query -> Block
450     SendAndVerifyBlockAck(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, false);
451
452     // Test multiple Blocks sent and received (last Block is BlockEOF)
453     while (numBlocksSent < numBlockSends)
454     {
455         bool isEof = (numBlocksSent == numBlockSends - 1);
456
457         SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
458         SendAndVerifyArbitraryBlock(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, isEof);
459
460         numBlocksSent++;
461     }
462
463     // Verify last block was BlockEOF, then verify response BlockAckEOF message
464     NL_TEST_ASSERT(inSuite, outEvent.blockdata.IsEof == true);
465     SendAndVerifyBlockAck(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, true);
466 }
467
468 // Partial transfer test using Sender Drive to specifically test Block -> BlockAck -> Block sequence
469 void TestInitiatingSenderSenderDrive(nlTestSuite * inSuite, void * inContext)
470 {
471     CHIP_ERROR err = CHIP_NO_ERROR;
472     TransferSession::OutputEvent outEvent;
473     TransferSession initiatingSender;
474     TransferSession respondingReceiver;
475
476     TransferControlFlags driveMode = kControl_SenderDrive;
477
478     // Chosen arbitrarily for this test
479     uint16_t transferBlockSize = 10;
480     uint32_t timeoutMs         = 1000 * 24;
481
482     // Initialize respondingReceiver
483     BitFlags<uint8_t, TransferControlFlags> receiverOpts;
484     receiverOpts.Set(driveMode);
485
486     // Test metadata for TransferInit message
487     uint8_t tlvBuf[64]    = { 0 };
488     char metadataStr[11]  = { "hi_dad.txt" };
489     uint32_t bytesWritten = 0;
490     err                   = WriteChipTLVString(tlvBuf, sizeof(tlvBuf), metadataStr, bytesWritten);
491     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
492     uint16_t metadataSize = static_cast<uint16_t>(bytesWritten & 0x0000FFFF);
493
494     // Initialize struct with TransferInit parameters
495     TransferSession::TransferInitData initOptions;
496     initOptions.TransferCtlFlagsRaw = driveMode;
497     initOptions.MaxBlockSize        = transferBlockSize;
498     char testFileDes[9]             = { "test.txt" };
499     initOptions.FileDesLength       = static_cast<uint16_t>(strlen(testFileDes));
500     initOptions.FileDesignator      = reinterpret_cast<uint8_t *>(testFileDes);
501     initOptions.Metadata            = tlvBuf;
502     initOptions.MetadataLength      = metadataSize;
503
504     SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingSender, kRole_Sender, initOptions,
505                               respondingReceiver, receiverOpts, transferBlockSize);
506
507     // Verify parsed TLV metadata matches the original
508     err = ReadAndVerifyTLVString(inSuite, inContext, outEvent.transferInitData.Metadata, outEvent.transferInitData.MetadataLength,
509                                  metadataStr, static_cast<uint16_t>(strlen(metadataStr)));
510     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
511
512     // Compose SendAccept parameters struct and give to respondingSender
513     uint16_t proposedBlockSize = transferBlockSize;
514     TransferSession::TransferAcceptData acceptData;
515     acceptData.ControlMode    = respondingReceiver.GetControlMode();
516     acceptData.MaxBlockSize   = proposedBlockSize;
517     acceptData.StartOffset    = 0; // not used in SendAccept
518     acceptData.Length         = 0; // not used in SendAccept
519     acceptData.Metadata       = nullptr;
520     acceptData.MetadataLength = 0;
521
522     SendAndVerifyAcceptMsg(inSuite, inContext, outEvent, respondingReceiver, kRole_Receiver, acceptData, initiatingSender,
523                            initOptions);
524
525     // Test multiple Block -> BlockAck -> Block
526     for (int i = 0; i < 3; i++)
527     {
528         SendAndVerifyArbitraryBlock(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, false);
529         SendAndVerifyBlockAck(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, false);
530     }
531
532     SendAndVerifyArbitraryBlock(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, true);
533     SendAndVerifyBlockAck(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, true);
534 }
535
536 // Test that calls to AcceptTransfer() with bad parameters result in an error.
537 void TestBadAcceptMessageFields(nlTestSuite * inSuite, void * inContext)
538 {
539     CHIP_ERROR err = CHIP_NO_ERROR;
540     TransferSession::OutputEvent outEvent;
541     TransferSession initiatingReceiver;
542     TransferSession respondingSender;
543
544     uint16_t maxBlockSize          = 64;
545     TransferControlFlags driveMode = kControl_ReceiverDrive;
546     uint64_t commonLength          = 0;
547     uint64_t commonOffset          = 0;
548     uint32_t timeoutMs             = 1000 * 24;
549
550     // Initialize struct with TransferInit parameters
551     TransferSession::TransferInitData initOptions;
552     initOptions.TransferCtlFlagsRaw = driveMode;
553     initOptions.MaxBlockSize        = maxBlockSize;
554     initOptions.StartOffset         = commonOffset;
555     initOptions.Length              = commonLength;
556     char testFileDes[9]             = { "test.txt" }; // arbitrary file designator
557     initOptions.FileDesLength       = static_cast<uint16_t>(strlen(testFileDes));
558     initOptions.FileDesignator      = reinterpret_cast<uint8_t *>(testFileDes);
559     initOptions.Metadata            = nullptr;
560     initOptions.MetadataLength      = 0;
561
562     // Responder parameters
563     BitFlags<uint8_t, TransferControlFlags> responderControl;
564     responderControl.Set(driveMode);
565
566     SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingReceiver, kRole_Receiver, initOptions,
567                               respondingSender, responderControl, maxBlockSize);
568
569     // Verify AcceptTransfer() returns error for choosing larger max block size
570     TransferSession::TransferAcceptData acceptData;
571     acceptData.ControlMode  = driveMode;
572     acceptData.MaxBlockSize = static_cast<uint16_t>(maxBlockSize + 1); // invalid if larger than proposed
573     acceptData.StartOffset  = commonOffset;
574     acceptData.Length       = commonLength;
575     err                     = respondingSender.AcceptTransfer(acceptData);
576     NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR);
577
578     // Verify AcceptTransfer() returns error for choosing unsupported transfer control mode
579     TransferSession::TransferAcceptData acceptData2;
580     acceptData2.ControlMode  = (driveMode == kControl_ReceiverDrive) ? kControl_SenderDrive : kControl_ReceiverDrive;
581     acceptData2.MaxBlockSize = maxBlockSize;
582     acceptData2.StartOffset  = commonOffset;
583     acceptData2.Length       = commonLength;
584     err                      = respondingSender.AcceptTransfer(acceptData2);
585     NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR);
586 }
587
588 // Test that a TransferSession will emit kTransferTimeout if the specified timeout is exceeded while waiting for a response.
589 void TestTimeout(nlTestSuite * inSuite, void * inContext)
590 {
591     CHIP_ERROR err = CHIP_NO_ERROR;
592     TransferSession initiator;
593     TransferSession::OutputEvent outEvent;
594
595     uint32_t timeoutMs   = 24;
596     uint64_t startTimeMs = 100;
597     uint64_t endTimeMs   = 124;
598
599     // Initialize struct with arbitrary TransferInit parameters
600     TransferSession::TransferInitData initOptions;
601     initOptions.TransferCtlFlagsRaw = kControl_ReceiverDrive;
602     initOptions.MaxBlockSize        = 64;
603     initOptions.StartOffset         = 0;
604     initOptions.Length              = 0;
605     char testFileDes[9]             = { "test.txt" }; // arbitrary file designator
606     initOptions.FileDesLength       = static_cast<uint16_t>(strlen(testFileDes));
607     initOptions.FileDesignator      = reinterpret_cast<uint8_t *>(testFileDes);
608     initOptions.Metadata            = nullptr;
609     initOptions.MetadataLength      = 0;
610
611     TransferRole role = kRole_Receiver;
612
613     // Verify initiator outputs respective Init message (depending on role) after StartTransfer()
614     err = initiator.StartTransfer(role, initOptions, timeoutMs);
615     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
616
617     // First PollOutput() should output the TransferInit message
618     initiator.PollOutput(outEvent, startTimeMs);
619     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kMsgToSend);
620     MessageType expectedInitMsg = (role == kRole_Sender) ? MessageType::SendInit : MessageType::ReceiveInit;
621     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expectedInitMsg);
622
623     // Second PollOutput() with no call to HandleMessageReceived() should result in a timeout.
624     initiator.PollOutput(outEvent, endTimeMs);
625     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kTransferTimeout);
626 }
627
628 // Test that sending the same block twice (with same block counter) results in a StatusReport message with BadBlockCounter. Also
629 // test that receiving the StatusReport ends the transfer on the other node.
630 void TestDuplicateBlockError(nlTestSuite * inSuite, void * inContext)
631 {
632     CHIP_ERROR err = CHIP_NO_ERROR;
633     TransferSession::OutputEvent outEvent;
634     TransferSession::OutputEvent eventWithBlock;
635     TransferSession initiatingReceiver;
636     TransferSession respondingSender;
637
638     uint8_t fakeData[64] = { 0 };
639     uint8_t fakeDataLen  = sizeof(fakeData);
640     uint16_t blockSize   = sizeof(fakeData);
641
642     // Chosen arbitrarily for this test
643     uint64_t proposedOffset = 64;
644     uint64_t proposedLength = 0;
645     uint32_t timeoutMs      = 1000 * 24;
646
647     // Chosen specifically for this test
648     TransferControlFlags driveMode = kControl_ReceiverDrive;
649
650     // ReceiveInit parameters
651     TransferSession::TransferInitData initOptions;
652     initOptions.TransferCtlFlagsRaw = driveMode;
653     initOptions.MaxBlockSize        = blockSize;
654     char testFileDes[9]             = { "test.txt" };
655     initOptions.FileDesLength       = static_cast<uint16_t>(strlen(testFileDes));
656     initOptions.FileDesignator      = reinterpret_cast<uint8_t *>(testFileDes);
657
658     // Initialize respondingSender and pass ReceiveInit message
659     BitFlags<uint8_t, TransferControlFlags> senderOpts;
660     senderOpts.Set(driveMode);
661
662     SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingReceiver, kRole_Receiver, initOptions,
663                               respondingSender, senderOpts, blockSize);
664
665     // Compose ReceiveAccept parameters struct and give to respondingSender
666     TransferSession::TransferAcceptData acceptData;
667     acceptData.ControlMode    = respondingSender.GetControlMode();
668     acceptData.StartOffset    = proposedOffset;
669     acceptData.Length         = proposedLength;
670     acceptData.MaxBlockSize   = blockSize;
671     acceptData.Metadata       = nullptr;
672     acceptData.MetadataLength = 0;
673
674     SendAndVerifyAcceptMsg(inSuite, inContext, outEvent, respondingSender, kRole_Sender, acceptData, initiatingReceiver,
675                            initOptions);
676
677     SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
678
679     TransferSession::BlockData blockData;
680     blockData.Data   = fakeData;
681     blockData.Length = fakeDataLen;
682     blockData.IsEof  = false;
683
684     // Provide Block data and verify sender emits Block message
685     err = respondingSender.PrepareBlock(blockData);
686     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
687     respondingSender.PollOutput(eventWithBlock, kNoAdvanceTime);
688     NL_TEST_ASSERT(inSuite, eventWithBlock.EventType == TransferSession::kMsgToSend);
689     VerifyBdxMessageType(inSuite, inContext, eventWithBlock.MsgData, MessageType::Block);
690     VerifyNoMoreOutput(inSuite, inContext, respondingSender);
691     System::PacketBufferHandle blockCopy =
692         System::PacketBufferHandle::NewWithData(eventWithBlock.MsgData->Start(), eventWithBlock.MsgData->DataLength());
693
694     // Pass Block message to receiver and verify matching Block is received
695     err = initiatingReceiver.HandleMessageReceived(std::move(eventWithBlock.MsgData), kNoAdvanceTime);
696     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
697     initiatingReceiver.PollOutput(outEvent, kNoAdvanceTime);
698     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kBlockReceived);
699     NL_TEST_ASSERT(inSuite, outEvent.blockdata.Data != nullptr);
700     VerifyNoMoreOutput(inSuite, inContext, initiatingReceiver);
701
702     SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
703
704     // Verify receiving same Block twice fails and results in StatusReport event, and then InternalError event
705     err = initiatingReceiver.HandleMessageReceived(std::move(blockCopy), kNoAdvanceTime);
706     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
707     initiatingReceiver.PollOutput(outEvent, kNoAdvanceTime);
708     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kMsgToSend);
709     System::PacketBufferHandle statusReportMsg = outEvent.MsgData.Retain();
710     VerifyStatusReport(inSuite, inContext, std::move(outEvent.MsgData), kStatus_BadBlockCounter);
711
712     // All subsequent PollOutput() calls should return kInternalError
713     for (int i = 0; i < 5; ++i)
714     {
715         initiatingReceiver.PollOutput(outEvent, kNoAdvanceTime);
716         NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kInternalError);
717         NL_TEST_ASSERT(inSuite, outEvent.statusData.StatusCode == kStatus_BadBlockCounter);
718     }
719
720     err = respondingSender.HandleMessageReceived(std::move(statusReportMsg), kNoAdvanceTime);
721     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
722     respondingSender.PollOutput(outEvent, kNoAdvanceTime);
723     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kStatusReceived);
724     NL_TEST_ASSERT(inSuite, outEvent.statusData.StatusCode == kStatus_BadBlockCounter);
725
726     // All subsequent PollOutput() calls should return kInternalError
727     for (int i = 0; i < 5; ++i)
728     {
729         respondingSender.PollOutput(outEvent, kNoAdvanceTime);
730         NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kInternalError);
731         NL_TEST_ASSERT(inSuite, outEvent.statusData.StatusCode == kStatus_BadBlockCounter);
732     }
733 }
734
735 // Test Suite
736
737 /**
738  *  Test Suite that lists all the test functions.
739  */
740 // clang-format off
741 static const nlTest sTests[] =
742 {
743     NL_TEST_DEF("TestInitiatingReceiverReceiverDrive", TestInitiatingReceiverReceiverDrive),
744     NL_TEST_DEF("TestInitiatingSenderSenderDrive", TestInitiatingSenderSenderDrive),
745     NL_TEST_DEF("TestBadAcceptMessageFields", TestBadAcceptMessageFields),
746     NL_TEST_DEF("TestTimeout", TestTimeout),
747     NL_TEST_DEF("TestDuplicateBlockError", TestDuplicateBlockError),
748     NL_TEST_SENTINEL()
749 };
750 // clang-format on
751
752 int TestBdxTransferSession_Setup(void * inContext)
753 {
754     CHIP_ERROR error = chip::Platform::MemoryInit();
755     if (error != CHIP_NO_ERROR)
756         return FAILURE;
757     return SUCCESS;
758 }
759
760 int TestBdxTransferSession_Teardown(void * inContext)
761 {
762     chip::Platform::MemoryShutdown();
763     return SUCCESS;
764 }
765
766 // clang-format off
767 static nlTestSuite sSuite =
768 {
769     "Test-CHIP-TransferSession",
770     &sTests[0],
771     TestBdxTransferSession_Setup,
772     TestBdxTransferSession_Teardown
773 };
774 // clang-format on
775
776 /**
777  *  Main
778  */
779 int TestBdxTransferSession()
780 {
781     // Run test suit against one context
782     nlTestRunner(&sSuite, nullptr);
783
784     return (nlTestRunnerStats(&sSuite));
785 }
786
787 CHIP_REGISTER_TEST_SUITE(TestBdxTransferSession)