1 #include <protocols/Protocols.h>
2 #include <protocols/bdx/BdxMessages.h>
3 #include <protocols/bdx/BdxTransferSession.h>
7 #include <nlunit-test.h>
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>
18 using namespace ::chip;
19 using namespace ::chip::bdx;
22 // Use this as a timestamp if not needing to test BDX timeouts.
23 constexpr uint64_t kNoAdvanceTime = 0;
25 const uint64_t tlvStrTag = TLV::ContextTag(4);
26 const uint64_t tlvListTag = TLV::ProfileTag(7777, 8888);
27 } // anonymous namespace
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)
32 CHIP_ERROR err = CHIP_NO_ERROR;
34 TLV::TLVWriter writer;
35 writer.Init(buf, bufLen);
38 TLV::TLVWriter listWriter;
39 err = writer.OpenContainer(tlvListTag, TLV::kTLVType_List, listWriter);
41 err = listWriter.PutString(tlvStrTag, data);
43 err = writer.CloseContainer(listWriter);
47 err = writer.Finalize();
49 written = writer.GetLengthWritten();
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)
59 CHIP_ERROR err = CHIP_NO_ERROR;
60 TLV::TLVReader reader;
62 uint32_t readLength = 0;
63 VerifyOrExit(sizeof(tmp) > len, err = CHIP_ERROR_INTERNAL);
65 reader.Init(dataStart, len);
68 VerifyOrExit(reader.GetTag() == tlvListTag, err = CHIP_ERROR_INTERNAL);
70 // Metadata must have a top-level list
72 TLV::TLVReader listReader;
73 err = reader.OpenContainer(listReader);
76 err = listReader.Next();
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));
84 VerifyOrExit(!memcmp(expected, tmp, readLength), err = CHIP_ERROR_INTERNAL);
86 err = reader.CloseContainer(listReader);
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)
97 CHIP_ERROR err = CHIP_NO_ERROR;
98 uint16_t headerSize = 0;
99 PayloadHeader payloadHeader;
103 NL_TEST_ASSERT(inSuite, false);
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));
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)
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;
124 NL_TEST_ASSERT(inSuite, false);
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())
134 NL_TEST_ASSERT(inSuite, false);
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);
146 void VerifyNoMoreOutput(nlTestSuite * inSuite, void * inContext, TransferSession & transferSession)
148 TransferSession::OutputEvent event;
149 transferSession.PollOutput(event, kNoAdvanceTime);
150 NL_TEST_ASSERT(inSuite, event.EventType == TransferSession::kNone);
153 // Helper method for initializing two TransferSession objects, generating a TransferInit message, and passing it to a responding
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)
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;
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);
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);
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)
193 !memcmp(initData.FileDesignator, outEvent.transferInitData.FileDesignator, outEvent.transferInitData.FileDesLength));
195 if (outEvent.transferInitData.Metadata != nullptr)
197 NL_TEST_ASSERT(inSuite, outEvent.transferInitData.MetadataLength == initData.MetadataLength);
198 if (outEvent.transferInitData.MetadataLength == initData.MetadataLength)
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.
203 inSuite, !memcmp(initData.Metadata, outEvent.transferInitData.Metadata, outEvent.transferInitData.MetadataLength));
207 NL_TEST_ASSERT(inSuite, false); // Metadata length mismatch
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.
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)
223 CHIP_ERROR err = CHIP_NO_ERROR;
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;
228 err = acceptSender.AcceptTransfer(acceptData);
229 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
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);
237 // Pass Accept message to acceptReceiver
238 err = acceptReceiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
239 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
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)
253 NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.MetadataLength == acceptData.MetadataLength);
254 if (outEvent.transferAcceptData.MetadataLength == acceptData.MetadataLength)
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.
260 !memcmp(acceptData.Metadata, outEvent.transferAcceptData.Metadata, outEvent.transferAcceptData.MetadataLength));
264 NL_TEST_ASSERT(inSuite, false); // Metadata length mismatch
268 // Verify that MaxBlockSize was set appropriately
269 NL_TEST_ASSERT(inSuite, acceptReceiver.GetTransferBlockSize() <= initData.MaxBlockSize);
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)
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);
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);
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)
297 CHIP_ERROR err = CHIP_NO_ERROR;
298 static uint8_t dataCount = 0;
299 uint16_t maxBlockSize = sender.GetTransferBlockSize();
301 NL_TEST_ASSERT(inSuite, maxBlockSize > 0);
302 System::PacketBufferHandle fakeDataBuf = System::PacketBufferHandle::New(maxBlockSize);
303 if (fakeDataBuf.IsNull())
305 NL_TEST_ASSERT(inSuite, false);
309 uint8_t * fakeBlockData = fakeDataBuf->Start();
310 fakeBlockData[0] = dataCount++;
312 TransferSession::BlockData blockData;
313 blockData.Data = fakeBlockData;
314 blockData.Length = maxBlockSize;
315 blockData.IsEof = isEof;
317 MessageType expected = isEof ? MessageType::BlockEOF : MessageType::Block;
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);
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)
335 NL_TEST_ASSERT(inSuite, !memcmp(fakeBlockData, outEvent.blockdata.Data, outEvent.blockdata.Length));
337 VerifyNoMoreOutput(inSuite, inContext, receiver);
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)
344 TransferSession::OutputEventType expectedEventType =
345 expectEOF ? TransferSession::kAckEOFReceived : TransferSession::kAckReceived;
346 MessageType expectedMsgType = expectEOF ? MessageType::BlockAckEOF : MessageType::BlockAck;
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);
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);
364 // Test a full transfer using a responding receiver and an initiating sender, receiver drive.
365 void TestInitiatingReceiverReceiverDrive(nlTestSuite * inSuite, void * inContext)
367 CHIP_ERROR err = CHIP_NO_ERROR;
368 TransferSession::OutputEvent outEvent;
369 TransferSession initiatingReceiver;
370 TransferSession respondingSender;
371 uint32_t numBlocksSent = 0;
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;
381 // Chosen specifically for this test
382 TransferControlFlags driveMode = kControl_ReceiverDrive;
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);
392 // Initialize respondingSender and pass ReceiveInit message
393 BitFlags<uint8_t, TransferControlFlags> senderOpts;
394 senderOpts.Set(driveMode);
396 SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingReceiver, kRole_Receiver, initOptions,
397 respondingSender, senderOpts, proposedBlockSize);
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);
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;
416 SendAndVerifyAcceptMsg(inSuite, inContext, outEvent, respondingSender, kRole_Sender, acceptData, initiatingReceiver,
419 // Verify that MaxBlockSize was chosen correctly
420 NL_TEST_ASSERT(inSuite, respondingSender.GetTransferBlockSize() == testSmallerBlockSize);
421 NL_TEST_ASSERT(inSuite, respondingSender.GetTransferBlockSize() == initiatingReceiver.GetTransferBlockSize());
423 // Verify parsed TLV metadata matches the original
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);
429 // Test BlockQuery -> Block -> BlockAck
430 SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
431 SendAndVerifyArbitraryBlock(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, false);
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())
439 NL_TEST_ASSERT(inSuite, false);
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);
449 // Test Ack -> Query -> Block
450 SendAndVerifyBlockAck(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, false);
452 // Test multiple Blocks sent and received (last Block is BlockEOF)
453 while (numBlocksSent < numBlockSends)
455 bool isEof = (numBlocksSent == numBlockSends - 1);
457 SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
458 SendAndVerifyArbitraryBlock(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, isEof);
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);
468 // Partial transfer test using Sender Drive to specifically test Block -> BlockAck -> Block sequence
469 void TestInitiatingSenderSenderDrive(nlTestSuite * inSuite, void * inContext)
471 CHIP_ERROR err = CHIP_NO_ERROR;
472 TransferSession::OutputEvent outEvent;
473 TransferSession initiatingSender;
474 TransferSession respondingReceiver;
476 TransferControlFlags driveMode = kControl_SenderDrive;
478 // Chosen arbitrarily for this test
479 uint16_t transferBlockSize = 10;
480 uint32_t timeoutMs = 1000 * 24;
482 // Initialize respondingReceiver
483 BitFlags<uint8_t, TransferControlFlags> receiverOpts;
484 receiverOpts.Set(driveMode);
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);
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;
504 SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingSender, kRole_Sender, initOptions,
505 respondingReceiver, receiverOpts, transferBlockSize);
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);
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;
522 SendAndVerifyAcceptMsg(inSuite, inContext, outEvent, respondingReceiver, kRole_Receiver, acceptData, initiatingSender,
525 // Test multiple Block -> BlockAck -> Block
526 for (int i = 0; i < 3; i++)
528 SendAndVerifyArbitraryBlock(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, false);
529 SendAndVerifyBlockAck(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, false);
532 SendAndVerifyArbitraryBlock(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, true);
533 SendAndVerifyBlockAck(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, true);
536 // Test that calls to AcceptTransfer() with bad parameters result in an error.
537 void TestBadAcceptMessageFields(nlTestSuite * inSuite, void * inContext)
539 CHIP_ERROR err = CHIP_NO_ERROR;
540 TransferSession::OutputEvent outEvent;
541 TransferSession initiatingReceiver;
542 TransferSession respondingSender;
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;
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;
562 // Responder parameters
563 BitFlags<uint8_t, TransferControlFlags> responderControl;
564 responderControl.Set(driveMode);
566 SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingReceiver, kRole_Receiver, initOptions,
567 respondingSender, responderControl, maxBlockSize);
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);
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);
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)
591 CHIP_ERROR err = CHIP_NO_ERROR;
592 TransferSession initiator;
593 TransferSession::OutputEvent outEvent;
595 uint32_t timeoutMs = 24;
596 uint64_t startTimeMs = 100;
597 uint64_t endTimeMs = 124;
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;
611 TransferRole role = kRole_Receiver;
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);
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);
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);
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)
632 CHIP_ERROR err = CHIP_NO_ERROR;
633 TransferSession::OutputEvent outEvent;
634 TransferSession::OutputEvent eventWithBlock;
635 TransferSession initiatingReceiver;
636 TransferSession respondingSender;
638 uint8_t fakeData[64] = { 0 };
639 uint8_t fakeDataLen = sizeof(fakeData);
640 uint16_t blockSize = sizeof(fakeData);
642 // Chosen arbitrarily for this test
643 uint64_t proposedOffset = 64;
644 uint64_t proposedLength = 0;
645 uint32_t timeoutMs = 1000 * 24;
647 // Chosen specifically for this test
648 TransferControlFlags driveMode = kControl_ReceiverDrive;
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);
658 // Initialize respondingSender and pass ReceiveInit message
659 BitFlags<uint8_t, TransferControlFlags> senderOpts;
660 senderOpts.Set(driveMode);
662 SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingReceiver, kRole_Receiver, initOptions,
663 respondingSender, senderOpts, blockSize);
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;
674 SendAndVerifyAcceptMsg(inSuite, inContext, outEvent, respondingSender, kRole_Sender, acceptData, initiatingReceiver,
677 SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
679 TransferSession::BlockData blockData;
680 blockData.Data = fakeData;
681 blockData.Length = fakeDataLen;
682 blockData.IsEof = false;
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());
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);
702 SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
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);
712 // All subsequent PollOutput() calls should return kInternalError
713 for (int i = 0; i < 5; ++i)
715 initiatingReceiver.PollOutput(outEvent, kNoAdvanceTime);
716 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kInternalError);
717 NL_TEST_ASSERT(inSuite, outEvent.statusData.StatusCode == kStatus_BadBlockCounter);
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);
726 // All subsequent PollOutput() calls should return kInternalError
727 for (int i = 0; i < 5; ++i)
729 respondingSender.PollOutput(outEvent, kNoAdvanceTime);
730 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::kInternalError);
731 NL_TEST_ASSERT(inSuite, outEvent.statusData.StatusCode == kStatus_BadBlockCounter);
738 * Test Suite that lists all the test functions.
741 static const nlTest sTests[] =
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),
752 int TestBdxTransferSession_Setup(void * inContext)
754 CHIP_ERROR error = chip::Platform::MemoryInit();
755 if (error != CHIP_NO_ERROR)
760 int TestBdxTransferSession_Teardown(void * inContext)
762 chip::Platform::MemoryShutdown();
767 static nlTestSuite sSuite =
769 "Test-CHIP-TransferSession",
771 TestBdxTransferSession_Setup,
772 TestBdxTransferSession_Teardown
779 int TestBdxTransferSession()
781 // Run test suit against one context
782 nlTestRunner(&sSuite, nullptr);
784 return (nlTestRunnerStats(&sSuite));
787 CHIP_REGISTER_TEST_SUITE(TestBdxTransferSession)