Fix for x86_64 build fail
[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/secure_channel/Constants.h>
11 #include <protocols/secure_channel/StatusReport.h>
12 #include <support/BufferReader.h>
13 #include <support/CHIPMem.h>
14 #include <support/CodeUtils.h>
15 #include <support/UnitTestRegistration.h>
16 #include <system/SystemPacketBuffer.h>
17
18 using namespace ::chip;
19 using namespace ::chip::bdx;
20 using namespace ::chip::Protocols;
21
22 namespace {
23 // Use this as a timestamp if not needing to test BDX timeouts.
24 constexpr uint64_t kNoAdvanceTime = 0;
25
26 const uint64_t tlvStrTag  = TLV::ContextTag(4);
27 const uint64_t tlvListTag = TLV::ProfileTag(7777, 8888);
28 } // anonymous namespace
29
30 // Helper method for generating a complete TLV structure with a list containing a single tag and string
31 CHIP_ERROR WriteChipTLVString(uint8_t * buf, uint32_t bufLen, const char * data, uint32_t & written)
32 {
33     CHIP_ERROR err = CHIP_NO_ERROR;
34     written        = 0;
35     TLV::TLVWriter writer;
36     writer.Init(buf, bufLen);
37
38     {
39         TLV::TLVWriter listWriter;
40         err = writer.OpenContainer(tlvListTag, TLV::kTLVType_List, listWriter);
41         SuccessOrExit(err);
42         err = listWriter.PutString(tlvStrTag, data);
43         SuccessOrExit(err);
44         err = writer.CloseContainer(listWriter);
45         SuccessOrExit(err);
46     }
47
48     err = writer.Finalize();
49     SuccessOrExit(err);
50     written = writer.GetLengthWritten();
51
52 exit:
53     return err;
54 }
55
56 // Helper method: read a TLV structure with a single tag and string and verify it matches expected string.
57 CHIP_ERROR ReadAndVerifyTLVString(nlTestSuite * inSuite, void * inContext, const uint8_t * dataStart, uint32_t len,
58                                   const char * expected, uint16_t expectedLen)
59 {
60     CHIP_ERROR err = CHIP_NO_ERROR;
61     TLV::TLVReader reader;
62     char tmp[64]        = { 0 };
63     uint32_t readLength = 0;
64     VerifyOrExit(sizeof(tmp) > len, err = CHIP_ERROR_INTERNAL);
65
66     reader.Init(dataStart, len);
67     err = reader.Next();
68
69     VerifyOrExit(reader.GetTag() == tlvListTag, err = CHIP_ERROR_INTERNAL);
70
71     // Metadata must have a top-level list
72     {
73         TLV::TLVReader listReader;
74         err = reader.OpenContainer(listReader);
75         SuccessOrExit(err);
76
77         err = listReader.Next();
78         SuccessOrExit(err);
79
80         VerifyOrExit(listReader.GetTag() == tlvStrTag, err = CHIP_ERROR_INTERNAL);
81         readLength = listReader.GetLength();
82         VerifyOrExit(readLength == expectedLen, err = CHIP_ERROR_INTERNAL);
83         err = listReader.GetString(tmp, sizeof(tmp));
84         SuccessOrExit(err);
85         VerifyOrExit(!memcmp(expected, tmp, readLength), err = CHIP_ERROR_INTERNAL);
86
87         err = reader.CloseContainer(listReader);
88         SuccessOrExit(err);
89     }
90
91 exit:
92     return err;
93 }
94
95 // Helper method for verifying that a PacketBufferHandle contains a valid BDX header and message type matches expected.
96 void VerifyBdxMessageType(nlTestSuite * inSuite, void * inContext, const System::PacketBufferHandle & msg, MessageType expected)
97 {
98     CHIP_ERROR err      = CHIP_NO_ERROR;
99     uint16_t headerSize = 0;
100     PayloadHeader payloadHeader;
101
102     if (msg.IsNull())
103     {
104         NL_TEST_ASSERT(inSuite, false);
105         return;
106     }
107
108     err = payloadHeader.Decode(msg->Start(), msg->DataLength(), &headerSize);
109     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
110     NL_TEST_ASSERT(inSuite, payloadHeader.HasMessageType(expected));
111 }
112
113 // Helper method for verifying that a PacketBufferHandle contains a valid StatusReport message and contains a specific StatusCode.
114 void VerifyStatusReport(nlTestSuite * inSuite, void * inContext, const System::PacketBufferHandle & msg, StatusCode code)
115 {
116     CHIP_ERROR err = CHIP_NO_ERROR;
117     PayloadHeader payloadHeader;
118
119     if (msg.IsNull())
120     {
121         NL_TEST_ASSERT(inSuite, false);
122         return;
123     }
124
125     System::PacketBufferHandle msgCopy = msg.CloneData();
126     if (msgCopy.IsNull())
127     {
128         NL_TEST_ASSERT(inSuite, false);
129         return;
130     }
131
132     err = payloadHeader.DecodeAndConsume(msgCopy);
133     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
134     NL_TEST_ASSERT(inSuite, payloadHeader.HasMessageType(SecureChannel::MsgType::StatusReport));
135
136     SecureChannel::StatusReport report;
137     err = report.Parse(std::move(msgCopy));
138     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
139     NL_TEST_ASSERT(inSuite, report.GetGeneralCode() == SecureChannel::GeneralStatusCode::kFailure);
140     NL_TEST_ASSERT(inSuite, report.GetProtocolId() == Protocols::BDX::Id.ToFullyQualifiedSpecForm());
141     NL_TEST_ASSERT(inSuite, report.GetProtocolCode() == static_cast<uint16_t>(code));
142 }
143
144 void VerifyNoMoreOutput(nlTestSuite * inSuite, void * inContext, TransferSession & transferSession)
145 {
146     TransferSession::OutputEvent event;
147     transferSession.PollOutput(event, kNoAdvanceTime);
148     NL_TEST_ASSERT(inSuite, event.EventType == TransferSession::OutputEventType::kNone);
149 }
150
151 // Helper method for initializing two TransferSession objects, generating a TransferInit message, and passing it to a responding
152 // TransferSession.
153 void SendAndVerifyTransferInit(nlTestSuite * inSuite, void * inContext, TransferSession::OutputEvent & outEvent, uint32_t timeoutMs,
154                                TransferSession & initiator, TransferRole initiatorRole, TransferSession::TransferInitData initData,
155                                TransferSession & responder, BitFlags<TransferControlFlags> & responderControlOpts,
156                                uint16_t responderMaxBlock)
157 {
158     CHIP_ERROR err              = CHIP_NO_ERROR;
159     TransferRole responderRole  = (initiatorRole == TransferRole::kSender) ? TransferRole::kReceiver : TransferRole::kSender;
160     MessageType expectedInitMsg = (initiatorRole == TransferRole::kSender) ? MessageType::SendInit : MessageType::ReceiveInit;
161
162     // Initializer responder to wait for transfer
163     err = responder.WaitForTransfer(responderRole, responderControlOpts, responderMaxBlock, timeoutMs);
164     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
165     VerifyNoMoreOutput(inSuite, inContext, responder);
166
167     // Verify initiator outputs respective Init message (depending on role) after StartTransfer()
168     err = initiator.StartTransfer(initiatorRole, initData, timeoutMs);
169     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
170     initiator.PollOutput(outEvent, kNoAdvanceTime);
171     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
172     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expectedInitMsg);
173     VerifyNoMoreOutput(inSuite, inContext, initiator);
174
175     // Verify that all parsed TransferInit fields match what was sent by the initiator
176     err = responder.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
177     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
178     responder.PollOutput(outEvent, kNoAdvanceTime);
179     VerifyNoMoreOutput(inSuite, inContext, responder);
180     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kInitReceived);
181     NL_TEST_ASSERT(inSuite, outEvent.transferInitData.TransferCtlFlags == initData.TransferCtlFlags);
182     NL_TEST_ASSERT(inSuite, outEvent.transferInitData.MaxBlockSize == initData.MaxBlockSize);
183     NL_TEST_ASSERT(inSuite, outEvent.transferInitData.StartOffset == initData.StartOffset);
184     NL_TEST_ASSERT(inSuite, outEvent.transferInitData.Length == initData.Length);
185     NL_TEST_ASSERT(inSuite, outEvent.transferInitData.FileDesignator != nullptr);
186     NL_TEST_ASSERT(inSuite, outEvent.transferInitData.FileDesLength == initData.FileDesLength);
187     if (outEvent.EventType == TransferSession::OutputEventType::kInitReceived &&
188         outEvent.transferInitData.FileDesignator != nullptr)
189     {
190         NL_TEST_ASSERT(
191             inSuite,
192             !memcmp(initData.FileDesignator, outEvent.transferInitData.FileDesignator, outEvent.transferInitData.FileDesLength));
193     }
194     if (outEvent.transferInitData.Metadata != nullptr)
195     {
196         NL_TEST_ASSERT(inSuite, outEvent.transferInitData.MetadataLength == initData.MetadataLength);
197         if (outEvent.transferInitData.MetadataLength == initData.MetadataLength)
198         {
199             // Only check that metadata buffers match. The OutputEvent can still be inspected when this function returns to parse
200             // the metadata and verify that it matches.
201             NL_TEST_ASSERT(
202                 inSuite, !memcmp(initData.Metadata, outEvent.transferInitData.Metadata, outEvent.transferInitData.MetadataLength));
203         }
204         else
205         {
206             NL_TEST_ASSERT(inSuite, false); // Metadata length mismatch
207         }
208     }
209 }
210
211 // Helper method for sending an Accept message and verifying that the received parameters match what was sent.
212 // This function assumes that the acceptData struct contains transfer parameters that are valid responses to the original
213 // TransferInit message (for example, MaxBlockSize should be <= the TransferInit MaxBlockSize). If such parameters are invalid, the
214 // receiver should emit a StatusCode event instead.
215 //
216 // The acceptSender is the node that is sending the Accept message (not necessarily the same node that will send Blocks).
217 void SendAndVerifyAcceptMsg(nlTestSuite * inSuite, void * inContext, TransferSession::OutputEvent & outEvent,
218                             TransferSession & acceptSender, TransferRole acceptSenderRole,
219                             TransferSession::TransferAcceptData acceptData, TransferSession & acceptReceiver,
220                             TransferSession::TransferInitData initData)
221 {
222     CHIP_ERROR err = CHIP_NO_ERROR;
223
224     // If the node sending the Accept message is also the one that will send Blocks, then this should be a ReceiveAccept message.
225     MessageType expectedMsg = (acceptSenderRole == TransferRole::kSender) ? MessageType::ReceiveAccept : MessageType::SendAccept;
226
227     err = acceptSender.AcceptTransfer(acceptData);
228     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
229
230     // Verify Sender emits ReceiveAccept message for sending
231     acceptSender.PollOutput(outEvent, kNoAdvanceTime);
232     VerifyNoMoreOutput(inSuite, inContext, acceptSender);
233     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
234     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expectedMsg);
235
236     // Pass Accept message to acceptReceiver
237     err = acceptReceiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
238     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
239
240     // Verify received ReceiveAccept.
241     // Client may want to inspect TransferControl, MaxBlockSize, StartOffset, Length, and Metadata, and may choose to reject the
242     // Transfer at this point.
243     acceptReceiver.PollOutput(outEvent, kNoAdvanceTime);
244     VerifyNoMoreOutput(inSuite, inContext, acceptReceiver);
245     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kAcceptReceived);
246     NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.ControlMode == acceptData.ControlMode);
247     NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.MaxBlockSize == acceptData.MaxBlockSize);
248     NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.StartOffset == acceptData.StartOffset);
249     NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.Length == acceptData.Length);
250     if (outEvent.transferAcceptData.Metadata != nullptr)
251     {
252         NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.MetadataLength == acceptData.MetadataLength);
253         if (outEvent.transferAcceptData.MetadataLength == acceptData.MetadataLength)
254         {
255             // Only check that metadata buffers match. The OutputEvent can still be inspected when this function returns to parse
256             // the metadata and verify that it matches.
257             NL_TEST_ASSERT(
258                 inSuite,
259                 !memcmp(acceptData.Metadata, outEvent.transferAcceptData.Metadata, outEvent.transferAcceptData.MetadataLength));
260         }
261         else
262         {
263             NL_TEST_ASSERT(inSuite, false); // Metadata length mismatch
264         }
265     }
266
267     // Verify that MaxBlockSize was set appropriately
268     NL_TEST_ASSERT(inSuite, acceptReceiver.GetTransferBlockSize() <= initData.MaxBlockSize);
269 }
270
271 // Helper method for preparing a sending a BlockQuery message between two TransferSession objects.
272 void SendAndVerifyQuery(nlTestSuite * inSuite, void * inContext, TransferSession & queryReceiver, TransferSession & querySender,
273                         TransferSession::OutputEvent & outEvent)
274 {
275     // Verify that querySender emits BlockQuery message
276     CHIP_ERROR err = querySender.PrepareBlockQuery();
277     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
278     querySender.PollOutput(outEvent, kNoAdvanceTime);
279     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
280     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, MessageType::BlockQuery);
281     VerifyNoMoreOutput(inSuite, inContext, querySender);
282
283     // Pass BlockQuery to queryReceiver and verify queryReceiver emits QueryReceived event
284     err = queryReceiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
285     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
286     queryReceiver.PollOutput(outEvent, kNoAdvanceTime);
287     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kQueryReceived);
288     VerifyNoMoreOutput(inSuite, inContext, queryReceiver);
289 }
290
291 // Helper method for preparing a sending a Block message between two TransferSession objects. The sender refers to the node that is
292 // sending Blocks. Uses a static counter incremented with each call. Also verifies that block data received matches what was sent.
293 void SendAndVerifyArbitraryBlock(nlTestSuite * inSuite, void * inContext, TransferSession & sender, TransferSession & receiver,
294                                  TransferSession::OutputEvent & outEvent, bool isEof)
295 {
296     CHIP_ERROR err           = CHIP_NO_ERROR;
297     static uint8_t dataCount = 0;
298     uint16_t maxBlockSize    = sender.GetTransferBlockSize();
299
300     NL_TEST_ASSERT(inSuite, maxBlockSize > 0);
301     System::PacketBufferHandle fakeDataBuf = System::PacketBufferHandle::New(maxBlockSize);
302     if (fakeDataBuf.IsNull())
303     {
304         NL_TEST_ASSERT(inSuite, false);
305         return;
306     }
307
308     uint8_t * fakeBlockData = fakeDataBuf->Start();
309     fakeBlockData[0]        = dataCount++;
310
311     TransferSession::BlockData blockData;
312     blockData.Data   = fakeBlockData;
313     blockData.Length = maxBlockSize;
314     blockData.IsEof  = isEof;
315
316     MessageType expected = isEof ? MessageType::BlockEOF : MessageType::Block;
317
318     // Provide Block data and verify sender emits Block message
319     err = sender.PrepareBlock(blockData);
320     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
321     sender.PollOutput(outEvent, kNoAdvanceTime);
322     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
323     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expected);
324     VerifyNoMoreOutput(inSuite, inContext, sender);
325
326     // Pass Block message to receiver and verify matching Block is received
327     err = receiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
328     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
329     receiver.PollOutput(outEvent, kNoAdvanceTime);
330     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kBlockReceived);
331     NL_TEST_ASSERT(inSuite, outEvent.blockdata.Data != nullptr);
332     if (outEvent.EventType == TransferSession::OutputEventType::kBlockReceived && outEvent.blockdata.Data != nullptr)
333     {
334         NL_TEST_ASSERT(inSuite, !memcmp(fakeBlockData, outEvent.blockdata.Data, outEvent.blockdata.Length));
335     }
336     VerifyNoMoreOutput(inSuite, inContext, receiver);
337 }
338
339 // Helper method for sending a BlockAck or BlockAckEOF, depending on the state of the receiver.
340 void SendAndVerifyBlockAck(nlTestSuite * inSuite, void * inContext, TransferSession & ackReceiver, TransferSession & ackSender,
341                            TransferSession::OutputEvent & outEvent, bool expectEOF)
342 {
343     TransferSession::OutputEventType expectedEventType =
344         expectEOF ? TransferSession::OutputEventType::kAckEOFReceived : TransferSession::OutputEventType::kAckReceived;
345     MessageType expectedMsgType = expectEOF ? MessageType::BlockAckEOF : MessageType::BlockAck;
346
347     // Verify PrepareBlockAck() outputs message to send
348     CHIP_ERROR err = ackSender.PrepareBlockAck();
349     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
350     ackSender.PollOutput(outEvent, kNoAdvanceTime);
351     NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
352     VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expectedMsgType);
353     VerifyNoMoreOutput(inSuite, inContext, ackSender);
354
355     // Pass BlockAck to ackReceiver and verify it was received
356     err = ackReceiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
357     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
358     ackReceiver.PollOutput(outEvent, kNoAdvanceTime);
359     NL_TEST_ASSERT(inSuite, outEvent.EventType == expectedEventType);
360     VerifyNoMoreOutput(inSuite, inContext, ackReceiver);
361 }
362
363 // Test a full transfer using a responding receiver and an initiating sender, receiver drive.
364 void TestInitiatingReceiverReceiverDrive(nlTestSuite * inSuite, void * inContext)
365 {
366     CHIP_ERROR err = CHIP_NO_ERROR;
367     TransferSession::OutputEvent outEvent;
368     TransferSession initiatingReceiver;
369     TransferSession respondingSender;
370     uint32_t numBlocksSent = 0;
371
372     // Chosen arbitrarily for this test
373     uint32_t numBlockSends        = 10;
374     uint16_t proposedBlockSize    = 128;
375     uint16_t testSmallerBlockSize = 64;
376     uint64_t proposedOffset       = 64;
377     uint64_t proposedLength       = 0;
378     uint32_t timeoutMs            = 1000 * 24;
379
380     // Chosen specifically for this test
381     TransferControlFlags driveMode = TransferControlFlags::kReceiverDrive;
382
383     // ReceiveInit parameters
384     TransferSession::TransferInitData initOptions;
385     initOptions.TransferCtlFlags = driveMode;
386     initOptions.MaxBlockSize     = proposedBlockSize;
387     char testFileDes[9]          = { "test.txt" };
388     initOptions.FileDesLength    = static_cast<uint16_t>(strlen(testFileDes));
389     initOptions.FileDesignator   = reinterpret_cast<uint8_t *>(testFileDes);
390
391     // Initialize respondingSender and pass ReceiveInit message
392     BitFlags<TransferControlFlags> senderOpts;
393     senderOpts.Set(driveMode);
394
395     SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingReceiver, TransferRole::kReceiver, initOptions,
396                               respondingSender, senderOpts, proposedBlockSize);
397
398     // Test metadata for Accept message
399     uint8_t tlvBuf[64]    = { 0 };
400     char metadataStr[11]  = { "hi_dad.txt" };
401     uint32_t bytesWritten = 0;
402     err                   = WriteChipTLVString(tlvBuf, sizeof(tlvBuf), metadataStr, bytesWritten);
403     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
404     uint16_t metadataSize = static_cast<uint16_t>(bytesWritten & 0x0000FFFF);
405
406     // Compose ReceiveAccept parameters struct and give to respondingSender
407     TransferSession::TransferAcceptData acceptData;
408     acceptData.ControlMode    = respondingSender.GetControlMode();
409     acceptData.StartOffset    = proposedOffset;
410     acceptData.Length         = proposedLength;
411     acceptData.MaxBlockSize   = testSmallerBlockSize;
412     acceptData.Metadata       = tlvBuf;
413     acceptData.MetadataLength = metadataSize;
414
415     SendAndVerifyAcceptMsg(inSuite, inContext, outEvent, respondingSender, TransferRole::kSender, acceptData, initiatingReceiver,
416                            initOptions);
417
418     // Verify that MaxBlockSize was chosen correctly
419     NL_TEST_ASSERT(inSuite, respondingSender.GetTransferBlockSize() == testSmallerBlockSize);
420     NL_TEST_ASSERT(inSuite, respondingSender.GetTransferBlockSize() == initiatingReceiver.GetTransferBlockSize());
421
422     // Verify parsed TLV metadata matches the original
423     err =
424         ReadAndVerifyTLVString(inSuite, inContext, outEvent.transferAcceptData.Metadata, outEvent.transferAcceptData.MetadataLength,
425                                metadataStr, static_cast<uint16_t>(strlen(metadataStr)));
426     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
427
428     // Test BlockQuery -> Block -> BlockAck
429     SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
430     SendAndVerifyArbitraryBlock(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, false);
431     numBlocksSent++;
432
433     // Test only one block can be prepared at a time, without receiving a response to the first
434     System::PacketBufferHandle fakeBuf = System::PacketBufferHandle::New(testSmallerBlockSize);
435     TransferSession::BlockData prematureBlock;
436     if (fakeBuf.IsNull())
437     {
438         NL_TEST_ASSERT(inSuite, false);
439         return;
440     }
441     prematureBlock.Data   = fakeBuf->Start();
442     prematureBlock.Length = testSmallerBlockSize;
443     prematureBlock.IsEof  = false;
444     err                   = respondingSender.PrepareBlock(prematureBlock);
445     NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR);
446     VerifyNoMoreOutput(inSuite, inContext, respondingSender);
447
448     // Test Ack -> Query -> Block
449     SendAndVerifyBlockAck(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, false);
450
451     // Test multiple Blocks sent and received (last Block is BlockEOF)
452     while (numBlocksSent < numBlockSends)
453     {
454         bool isEof = (numBlocksSent == numBlockSends - 1);
455
456         SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
457         SendAndVerifyArbitraryBlock(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, isEof);
458
459         numBlocksSent++;
460     }
461
462     // Verify last block was BlockEOF, then verify response BlockAckEOF message
463     NL_TEST_ASSERT(inSuite, outEvent.blockdata.IsEof == true);
464     SendAndVerifyBlockAck(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, true);
465 }
466
467 // Partial transfer test using Sender Drive to specifically test Block -> BlockAck -> Block sequence
468 void TestInitiatingSenderSenderDrive(nlTestSuite * inSuite, void * inContext)
469 {
470     CHIP_ERROR err = CHIP_NO_ERROR;
471     TransferSession::OutputEvent outEvent;
472     TransferSession initiatingSender;
473     TransferSession respondingReceiver;
474
475     TransferControlFlags driveMode = TransferControlFlags::kSenderDrive;
476
477     // Chosen arbitrarily for this test
478     uint16_t transferBlockSize = 10;
479     uint32_t timeoutMs         = 1000 * 24;
480
481     // Initialize respondingReceiver
482     BitFlags<TransferControlFlags> receiverOpts;
483     receiverOpts.Set(driveMode);
484
485     // Test metadata for TransferInit message
486     uint8_t tlvBuf[64]    = { 0 };
487     char metadataStr[11]  = { "hi_dad.txt" };
488     uint32_t bytesWritten = 0;
489     err                   = WriteChipTLVString(tlvBuf, sizeof(tlvBuf), metadataStr, bytesWritten);
490     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
491     uint16_t metadataSize = static_cast<uint16_t>(bytesWritten & 0x0000FFFF);
492
493     // Initialize struct with TransferInit parameters
494     TransferSession::TransferInitData initOptions;
495     initOptions.TransferCtlFlags = driveMode;
496     initOptions.MaxBlockSize     = transferBlockSize;
497     char testFileDes[9]          = { "test.txt" };
498     initOptions.FileDesLength    = static_cast<uint16_t>(strlen(testFileDes));
499     initOptions.FileDesignator   = reinterpret_cast<uint8_t *>(testFileDes);
500     initOptions.Metadata         = tlvBuf;
501     initOptions.MetadataLength   = metadataSize;
502
503     SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingSender, TransferRole::kSender, initOptions,
504                               respondingReceiver, receiverOpts, transferBlockSize);
505
506     // Verify parsed TLV metadata matches the original
507     err = ReadAndVerifyTLVString(inSuite, inContext, outEvent.transferInitData.Metadata, outEvent.transferInitData.MetadataLength,
508                                  metadataStr, static_cast<uint16_t>(strlen(metadataStr)));
509     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
510
511     // Compose SendAccept parameters struct and give to respondingSender
512     uint16_t proposedBlockSize = transferBlockSize;
513     TransferSession::TransferAcceptData acceptData;
514     acceptData.ControlMode    = respondingReceiver.GetControlMode();
515     acceptData.MaxBlockSize   = proposedBlockSize;
516     acceptData.StartOffset    = 0; // not used in SendAccept
517     acceptData.Length         = 0; // not used in SendAccept
518     acceptData.Metadata       = nullptr;
519     acceptData.MetadataLength = 0;
520
521     SendAndVerifyAcceptMsg(inSuite, inContext, outEvent, respondingReceiver, TransferRole::kReceiver, acceptData, initiatingSender,
522                            initOptions);
523
524     // Test multiple Block -> BlockAck -> Block
525     for (int i = 0; i < 3; i++)
526     {
527         SendAndVerifyArbitraryBlock(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, false);
528         SendAndVerifyBlockAck(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, false);
529     }
530
531     SendAndVerifyArbitraryBlock(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, true);
532     SendAndVerifyBlockAck(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, true);
533 }
534
535 // Test that calls to AcceptTransfer() with bad parameters result in an error.
536 void TestBadAcceptMessageFields(nlTestSuite * inSuite, void * inContext)
537 {
538     CHIP_ERROR err = CHIP_NO_ERROR;
539     TransferSession::OutputEvent outEvent;
540     TransferSession initiatingReceiver;
541     TransferSession respondingSender;
542
543     uint16_t maxBlockSize          = 64;
544     TransferControlFlags driveMode = TransferControlFlags::kReceiverDrive;
545     uint64_t commonLength          = 0;
546     uint64_t commonOffset          = 0;
547     uint32_t timeoutMs             = 1000 * 24;
548
549     // Initialize struct with TransferInit parameters
550     TransferSession::TransferInitData initOptions;
551     initOptions.TransferCtlFlags = driveMode;
552     initOptions.MaxBlockSize     = maxBlockSize;
553     initOptions.StartOffset      = commonOffset;
554     initOptions.Length           = commonLength;
555     char testFileDes[9]          = { "test.txt" }; // arbitrary file designator
556     initOptions.FileDesLength    = static_cast<uint16_t>(strlen(testFileDes));
557     initOptions.FileDesignator   = reinterpret_cast<uint8_t *>(testFileDes);
558     initOptions.Metadata         = nullptr;
559     initOptions.MetadataLength   = 0;
560
561     // Responder parameters
562     BitFlags<TransferControlFlags> responderControl;
563     responderControl.Set(driveMode);
564
565     SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingReceiver, TransferRole::kReceiver, initOptions,
566                               respondingSender, responderControl, maxBlockSize);
567
568     // Verify AcceptTransfer() returns error for choosing larger max block size
569     TransferSession::TransferAcceptData acceptData;
570     acceptData.ControlMode  = driveMode;
571     acceptData.MaxBlockSize = static_cast<uint16_t>(maxBlockSize + 1); // invalid if larger than proposed
572     acceptData.StartOffset  = commonOffset;
573     acceptData.Length       = commonLength;
574     err                     = respondingSender.AcceptTransfer(acceptData);
575     NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR);
576
577     // Verify AcceptTransfer() returns error for choosing unsupported transfer control mode
578     TransferSession::TransferAcceptData acceptData2;
579     acceptData2.ControlMode = (driveMode == TransferControlFlags::kReceiverDrive) ? TransferControlFlags::kSenderDrive
580                                                                                   : TransferControlFlags::kReceiverDrive;
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.TransferCtlFlags = TransferControlFlags::kReceiverDrive;
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 = TransferRole::kReceiver;
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::OutputEventType::kMsgToSend);
620     MessageType expectedInitMsg = (role == TransferRole::kSender) ? 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::OutputEventType::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 = TransferControlFlags::kReceiverDrive;
649
650     // ReceiveInit parameters
651     TransferSession::TransferInitData initOptions;
652     initOptions.TransferCtlFlags = 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<TransferControlFlags> senderOpts;
660     senderOpts.Set(driveMode);
661
662     SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingReceiver, TransferRole::kReceiver, 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, TransferRole::kSender, 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::OutputEventType::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::OutputEventType::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::OutputEventType::kMsgToSend);
709     System::PacketBufferHandle statusReportMsg = outEvent.MsgData.Retain();
710     VerifyStatusReport(inSuite, inContext, std::move(outEvent.MsgData), StatusCode::kBadBlockCounter);
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::OutputEventType::kInternalError);
717         NL_TEST_ASSERT(inSuite, outEvent.statusData.statusCode == StatusCode::kBadBlockCounter);
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::OutputEventType::kStatusReceived);
724     NL_TEST_ASSERT(inSuite, outEvent.statusData.statusCode == StatusCode::kBadBlockCounter);
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::OutputEventType::kInternalError);
731         NL_TEST_ASSERT(inSuite, outEvent.statusData.statusCode == StatusCode::kBadBlockCounter);
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)