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 BitFlags<StatusCode> protocolCode;
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.RawStorage()).StatusCode();
140 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
141 NL_TEST_ASSERT(inSuite, generalCode == static_cast<uint16_t>(Protocols::Common::StatusCode::Failure));
142 NL_TEST_ASSERT(inSuite, protocolId == Protocols::kProtocol_BDX);
143 NL_TEST_ASSERT(inSuite, protocolCode == code);
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::OutputEventType::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<TransferControlFlags> & responderControlOpts,
158 uint16_t responderMaxBlock)
160 CHIP_ERROR err = CHIP_NO_ERROR;
161 TransferRole responderRole = (initiatorRole == TransferRole::kSender) ? TransferRole::kReceiver : TransferRole::kSender;
162 MessageType expectedInitMsg = (initiatorRole == TransferRole::kSender) ? MessageType::SendInit : MessageType::ReceiveInit;
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::OutputEventType::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::OutputEventType::kInitReceived);
183 NL_TEST_ASSERT(inSuite, outEvent.transferInitData.TransferCtlFlags == initData.TransferCtlFlags);
184 NL_TEST_ASSERT(inSuite, outEvent.transferInitData.MaxBlockSize == initData.MaxBlockSize);
185 NL_TEST_ASSERT(inSuite, outEvent.transferInitData.StartOffset == initData.StartOffset);
186 NL_TEST_ASSERT(inSuite, outEvent.transferInitData.Length == initData.Length);
187 NL_TEST_ASSERT(inSuite, outEvent.transferInitData.FileDesignator != nullptr);
188 NL_TEST_ASSERT(inSuite, outEvent.transferInitData.FileDesLength == initData.FileDesLength);
189 if (outEvent.EventType == TransferSession::OutputEventType::kInitReceived &&
190 outEvent.transferInitData.FileDesignator != nullptr)
194 !memcmp(initData.FileDesignator, outEvent.transferInitData.FileDesignator, outEvent.transferInitData.FileDesLength));
196 if (outEvent.transferInitData.Metadata != nullptr)
198 NL_TEST_ASSERT(inSuite, outEvent.transferInitData.MetadataLength == initData.MetadataLength);
199 if (outEvent.transferInitData.MetadataLength == initData.MetadataLength)
201 // Only check that metadata buffers match. The OutputEvent can still be inspected when this function returns to parse
202 // the metadata and verify that it matches.
204 inSuite, !memcmp(initData.Metadata, outEvent.transferInitData.Metadata, outEvent.transferInitData.MetadataLength));
208 NL_TEST_ASSERT(inSuite, false); // Metadata length mismatch
213 // Helper method for sending an Accept message and verifying that the received parameters match what was sent.
214 // This function assumes that the acceptData struct contains transfer parameters that are valid responses to the original
215 // TransferInit message (for example, MaxBlockSize should be <= the TransferInit MaxBlockSize). If such parameters are invalid, the
216 // receiver should emit a StatusCode event instead.
218 // The acceptSender is the node that is sending the Accept message (not necessarily the same node that will send Blocks).
219 void SendAndVerifyAcceptMsg(nlTestSuite * inSuite, void * inContext, TransferSession::OutputEvent & outEvent,
220 TransferSession & acceptSender, TransferRole acceptSenderRole,
221 TransferSession::TransferAcceptData acceptData, TransferSession & acceptReceiver,
222 TransferSession::TransferInitData initData)
224 CHIP_ERROR err = CHIP_NO_ERROR;
226 // If the node sending the Accept message is also the one that will send Blocks, then this should be a ReceiveAccept message.
227 MessageType expectedMsg = (acceptSenderRole == TransferRole::kSender) ? MessageType::ReceiveAccept : MessageType::SendAccept;
229 err = acceptSender.AcceptTransfer(acceptData);
230 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
232 // Verify Sender emits ReceiveAccept message for sending
233 acceptSender.PollOutput(outEvent, kNoAdvanceTime);
234 VerifyNoMoreOutput(inSuite, inContext, acceptSender);
235 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
236 VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expectedMsg);
238 // Pass Accept message to acceptReceiver
239 err = acceptReceiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
240 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
242 // Verify received ReceiveAccept.
243 // Client may want to inspect TransferControl, MaxBlockSize, StartOffset, Length, and Metadata, and may choose to reject the
244 // Transfer at this point.
245 acceptReceiver.PollOutput(outEvent, kNoAdvanceTime);
246 VerifyNoMoreOutput(inSuite, inContext, acceptReceiver);
247 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kAcceptReceived);
248 NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.ControlMode == acceptData.ControlMode);
249 NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.MaxBlockSize == acceptData.MaxBlockSize);
250 NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.StartOffset == acceptData.StartOffset);
251 NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.Length == acceptData.Length);
252 if (outEvent.transferAcceptData.Metadata != nullptr)
254 NL_TEST_ASSERT(inSuite, outEvent.transferAcceptData.MetadataLength == acceptData.MetadataLength);
255 if (outEvent.transferAcceptData.MetadataLength == acceptData.MetadataLength)
257 // Only check that metadata buffers match. The OutputEvent can still be inspected when this function returns to parse
258 // the metadata and verify that it matches.
261 !memcmp(acceptData.Metadata, outEvent.transferAcceptData.Metadata, outEvent.transferAcceptData.MetadataLength));
265 NL_TEST_ASSERT(inSuite, false); // Metadata length mismatch
269 // Verify that MaxBlockSize was set appropriately
270 NL_TEST_ASSERT(inSuite, acceptReceiver.GetTransferBlockSize() <= initData.MaxBlockSize);
273 // Helper method for preparing a sending a BlockQuery message between two TransferSession objects.
274 void SendAndVerifyQuery(nlTestSuite * inSuite, void * inContext, TransferSession & queryReceiver, TransferSession & querySender,
275 TransferSession::OutputEvent & outEvent)
277 // Verify that querySender emits BlockQuery message
278 CHIP_ERROR err = querySender.PrepareBlockQuery();
279 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
280 querySender.PollOutput(outEvent, kNoAdvanceTime);
281 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
282 VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, MessageType::BlockQuery);
283 VerifyNoMoreOutput(inSuite, inContext, querySender);
285 // Pass BlockQuery to queryReceiver and verify queryReceiver emits QueryReceived event
286 err = queryReceiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
287 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
288 queryReceiver.PollOutput(outEvent, kNoAdvanceTime);
289 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kQueryReceived);
290 VerifyNoMoreOutput(inSuite, inContext, queryReceiver);
293 // Helper method for preparing a sending a Block message between two TransferSession objects. The sender refers to the node that is
294 // sending Blocks. Uses a static counter incremented with each call. Also verifies that block data received matches what was sent.
295 void SendAndVerifyArbitraryBlock(nlTestSuite * inSuite, void * inContext, TransferSession & sender, TransferSession & receiver,
296 TransferSession::OutputEvent & outEvent, bool isEof)
298 CHIP_ERROR err = CHIP_NO_ERROR;
299 static uint8_t dataCount = 0;
300 uint16_t maxBlockSize = sender.GetTransferBlockSize();
302 NL_TEST_ASSERT(inSuite, maxBlockSize > 0);
303 System::PacketBufferHandle fakeDataBuf = System::PacketBufferHandle::New(maxBlockSize);
304 if (fakeDataBuf.IsNull())
306 NL_TEST_ASSERT(inSuite, false);
310 uint8_t * fakeBlockData = fakeDataBuf->Start();
311 fakeBlockData[0] = dataCount++;
313 TransferSession::BlockData blockData;
314 blockData.Data = fakeBlockData;
315 blockData.Length = maxBlockSize;
316 blockData.IsEof = isEof;
318 MessageType expected = isEof ? MessageType::BlockEOF : MessageType::Block;
320 // Provide Block data and verify sender emits Block message
321 err = sender.PrepareBlock(blockData);
322 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
323 sender.PollOutput(outEvent, kNoAdvanceTime);
324 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
325 VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expected);
326 VerifyNoMoreOutput(inSuite, inContext, sender);
328 // Pass Block message to receiver and verify matching Block is received
329 err = receiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
330 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
331 receiver.PollOutput(outEvent, kNoAdvanceTime);
332 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kBlockReceived);
333 NL_TEST_ASSERT(inSuite, outEvent.blockdata.Data != nullptr);
334 if (outEvent.EventType == TransferSession::OutputEventType::kBlockReceived && outEvent.blockdata.Data != nullptr)
336 NL_TEST_ASSERT(inSuite, !memcmp(fakeBlockData, outEvent.blockdata.Data, outEvent.blockdata.Length));
338 VerifyNoMoreOutput(inSuite, inContext, receiver);
341 // Helper method for sending a BlockAck or BlockAckEOF, depending on the state of the receiver.
342 void SendAndVerifyBlockAck(nlTestSuite * inSuite, void * inContext, TransferSession & ackReceiver, TransferSession & ackSender,
343 TransferSession::OutputEvent & outEvent, bool expectEOF)
345 TransferSession::OutputEventType expectedEventType =
346 expectEOF ? TransferSession::OutputEventType::kAckEOFReceived : TransferSession::OutputEventType::kAckReceived;
347 MessageType expectedMsgType = expectEOF ? MessageType::BlockAckEOF : MessageType::BlockAck;
349 // Verify PrepareBlockAck() outputs message to send
350 CHIP_ERROR err = ackSender.PrepareBlockAck();
351 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
352 ackSender.PollOutput(outEvent, kNoAdvanceTime);
353 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
354 VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expectedMsgType);
355 VerifyNoMoreOutput(inSuite, inContext, ackSender);
357 // Pass BlockAck to ackReceiver and verify it was received
358 err = ackReceiver.HandleMessageReceived(std::move(outEvent.MsgData), kNoAdvanceTime);
359 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
360 ackReceiver.PollOutput(outEvent, kNoAdvanceTime);
361 NL_TEST_ASSERT(inSuite, outEvent.EventType == expectedEventType);
362 VerifyNoMoreOutput(inSuite, inContext, ackReceiver);
365 // Test a full transfer using a responding receiver and an initiating sender, receiver drive.
366 void TestInitiatingReceiverReceiverDrive(nlTestSuite * inSuite, void * inContext)
368 CHIP_ERROR err = CHIP_NO_ERROR;
369 TransferSession::OutputEvent outEvent;
370 TransferSession initiatingReceiver;
371 TransferSession respondingSender;
372 uint32_t numBlocksSent = 0;
374 // Chosen arbitrarily for this test
375 uint32_t numBlockSends = 10;
376 uint16_t proposedBlockSize = 128;
377 uint16_t testSmallerBlockSize = 64;
378 uint64_t proposedOffset = 64;
379 uint64_t proposedLength = 0;
380 uint32_t timeoutMs = 1000 * 24;
382 // Chosen specifically for this test
383 TransferControlFlags driveMode = TransferControlFlags::kReceiverDrive;
385 // ReceiveInit parameters
386 TransferSession::TransferInitData initOptions;
387 initOptions.TransferCtlFlags = driveMode;
388 initOptions.MaxBlockSize = proposedBlockSize;
389 char testFileDes[9] = { "test.txt" };
390 initOptions.FileDesLength = static_cast<uint16_t>(strlen(testFileDes));
391 initOptions.FileDesignator = reinterpret_cast<uint8_t *>(testFileDes);
393 // Initialize respondingSender and pass ReceiveInit message
394 BitFlags<TransferControlFlags> senderOpts;
395 senderOpts.Set(driveMode);
397 SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingReceiver, TransferRole::kReceiver, initOptions,
398 respondingSender, senderOpts, proposedBlockSize);
400 // Test metadata for Accept message
401 uint8_t tlvBuf[64] = { 0 };
402 char metadataStr[11] = { "hi_dad.txt" };
403 uint32_t bytesWritten = 0;
404 err = WriteChipTLVString(tlvBuf, sizeof(tlvBuf), metadataStr, bytesWritten);
405 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
406 uint16_t metadataSize = static_cast<uint16_t>(bytesWritten & 0x0000FFFF);
408 // Compose ReceiveAccept parameters struct and give to respondingSender
409 TransferSession::TransferAcceptData acceptData;
410 acceptData.ControlMode = respondingSender.GetControlMode();
411 acceptData.StartOffset = proposedOffset;
412 acceptData.Length = proposedLength;
413 acceptData.MaxBlockSize = testSmallerBlockSize;
414 acceptData.Metadata = tlvBuf;
415 acceptData.MetadataLength = metadataSize;
417 SendAndVerifyAcceptMsg(inSuite, inContext, outEvent, respondingSender, TransferRole::kSender, acceptData, initiatingReceiver,
420 // Verify that MaxBlockSize was chosen correctly
421 NL_TEST_ASSERT(inSuite, respondingSender.GetTransferBlockSize() == testSmallerBlockSize);
422 NL_TEST_ASSERT(inSuite, respondingSender.GetTransferBlockSize() == initiatingReceiver.GetTransferBlockSize());
424 // Verify parsed TLV metadata matches the original
426 ReadAndVerifyTLVString(inSuite, inContext, outEvent.transferAcceptData.Metadata, outEvent.transferAcceptData.MetadataLength,
427 metadataStr, static_cast<uint16_t>(strlen(metadataStr)));
428 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
430 // Test BlockQuery -> Block -> BlockAck
431 SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
432 SendAndVerifyArbitraryBlock(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, false);
435 // Test only one block can be prepared at a time, without receiving a response to the first
436 System::PacketBufferHandle fakeBuf = System::PacketBufferHandle::New(testSmallerBlockSize);
437 TransferSession::BlockData prematureBlock;
438 if (fakeBuf.IsNull())
440 NL_TEST_ASSERT(inSuite, false);
443 prematureBlock.Data = fakeBuf->Start();
444 prematureBlock.Length = testSmallerBlockSize;
445 prematureBlock.IsEof = false;
446 err = respondingSender.PrepareBlock(prematureBlock);
447 NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR);
448 VerifyNoMoreOutput(inSuite, inContext, respondingSender);
450 // Test Ack -> Query -> Block
451 SendAndVerifyBlockAck(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, false);
453 // Test multiple Blocks sent and received (last Block is BlockEOF)
454 while (numBlocksSent < numBlockSends)
456 bool isEof = (numBlocksSent == numBlockSends - 1);
458 SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
459 SendAndVerifyArbitraryBlock(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, isEof);
464 // Verify last block was BlockEOF, then verify response BlockAckEOF message
465 NL_TEST_ASSERT(inSuite, outEvent.blockdata.IsEof == true);
466 SendAndVerifyBlockAck(inSuite, inContext, respondingSender, initiatingReceiver, outEvent, true);
469 // Partial transfer test using Sender Drive to specifically test Block -> BlockAck -> Block sequence
470 void TestInitiatingSenderSenderDrive(nlTestSuite * inSuite, void * inContext)
472 CHIP_ERROR err = CHIP_NO_ERROR;
473 TransferSession::OutputEvent outEvent;
474 TransferSession initiatingSender;
475 TransferSession respondingReceiver;
477 TransferControlFlags driveMode = TransferControlFlags::kSenderDrive;
479 // Chosen arbitrarily for this test
480 uint16_t transferBlockSize = 10;
481 uint32_t timeoutMs = 1000 * 24;
483 // Initialize respondingReceiver
484 BitFlags<TransferControlFlags> receiverOpts;
485 receiverOpts.Set(driveMode);
487 // Test metadata for TransferInit message
488 uint8_t tlvBuf[64] = { 0 };
489 char metadataStr[11] = { "hi_dad.txt" };
490 uint32_t bytesWritten = 0;
491 err = WriteChipTLVString(tlvBuf, sizeof(tlvBuf), metadataStr, bytesWritten);
492 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
493 uint16_t metadataSize = static_cast<uint16_t>(bytesWritten & 0x0000FFFF);
495 // Initialize struct with TransferInit parameters
496 TransferSession::TransferInitData initOptions;
497 initOptions.TransferCtlFlags = driveMode;
498 initOptions.MaxBlockSize = transferBlockSize;
499 char testFileDes[9] = { "test.txt" };
500 initOptions.FileDesLength = static_cast<uint16_t>(strlen(testFileDes));
501 initOptions.FileDesignator = reinterpret_cast<uint8_t *>(testFileDes);
502 initOptions.Metadata = tlvBuf;
503 initOptions.MetadataLength = metadataSize;
505 SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingSender, TransferRole::kSender, initOptions,
506 respondingReceiver, receiverOpts, transferBlockSize);
508 // Verify parsed TLV metadata matches the original
509 err = ReadAndVerifyTLVString(inSuite, inContext, outEvent.transferInitData.Metadata, outEvent.transferInitData.MetadataLength,
510 metadataStr, static_cast<uint16_t>(strlen(metadataStr)));
511 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
513 // Compose SendAccept parameters struct and give to respondingSender
514 uint16_t proposedBlockSize = transferBlockSize;
515 TransferSession::TransferAcceptData acceptData;
516 acceptData.ControlMode = respondingReceiver.GetControlMode();
517 acceptData.MaxBlockSize = proposedBlockSize;
518 acceptData.StartOffset = 0; // not used in SendAccept
519 acceptData.Length = 0; // not used in SendAccept
520 acceptData.Metadata = nullptr;
521 acceptData.MetadataLength = 0;
523 SendAndVerifyAcceptMsg(inSuite, inContext, outEvent, respondingReceiver, TransferRole::kReceiver, acceptData, initiatingSender,
526 // Test multiple Block -> BlockAck -> Block
527 for (int i = 0; i < 3; i++)
529 SendAndVerifyArbitraryBlock(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, false);
530 SendAndVerifyBlockAck(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, false);
533 SendAndVerifyArbitraryBlock(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, true);
534 SendAndVerifyBlockAck(inSuite, inContext, initiatingSender, respondingReceiver, outEvent, true);
537 // Test that calls to AcceptTransfer() with bad parameters result in an error.
538 void TestBadAcceptMessageFields(nlTestSuite * inSuite, void * inContext)
540 CHIP_ERROR err = CHIP_NO_ERROR;
541 TransferSession::OutputEvent outEvent;
542 TransferSession initiatingReceiver;
543 TransferSession respondingSender;
545 uint16_t maxBlockSize = 64;
546 TransferControlFlags driveMode = TransferControlFlags::kReceiverDrive;
547 uint64_t commonLength = 0;
548 uint64_t commonOffset = 0;
549 uint32_t timeoutMs = 1000 * 24;
551 // Initialize struct with TransferInit parameters
552 TransferSession::TransferInitData initOptions;
553 initOptions.TransferCtlFlags = driveMode;
554 initOptions.MaxBlockSize = maxBlockSize;
555 initOptions.StartOffset = commonOffset;
556 initOptions.Length = commonLength;
557 char testFileDes[9] = { "test.txt" }; // arbitrary file designator
558 initOptions.FileDesLength = static_cast<uint16_t>(strlen(testFileDes));
559 initOptions.FileDesignator = reinterpret_cast<uint8_t *>(testFileDes);
560 initOptions.Metadata = nullptr;
561 initOptions.MetadataLength = 0;
563 // Responder parameters
564 BitFlags<TransferControlFlags> responderControl;
565 responderControl.Set(driveMode);
567 SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingReceiver, TransferRole::kReceiver, initOptions,
568 respondingSender, responderControl, maxBlockSize);
570 // Verify AcceptTransfer() returns error for choosing larger max block size
571 TransferSession::TransferAcceptData acceptData;
572 acceptData.ControlMode = driveMode;
573 acceptData.MaxBlockSize = static_cast<uint16_t>(maxBlockSize + 1); // invalid if larger than proposed
574 acceptData.StartOffset = commonOffset;
575 acceptData.Length = commonLength;
576 err = respondingSender.AcceptTransfer(acceptData);
577 NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR);
579 // Verify AcceptTransfer() returns error for choosing unsupported transfer control mode
580 TransferSession::TransferAcceptData acceptData2;
581 acceptData2.ControlMode = (driveMode == TransferControlFlags::kReceiverDrive) ? TransferControlFlags::kSenderDrive
582 : TransferControlFlags::kReceiverDrive;
583 acceptData2.MaxBlockSize = maxBlockSize;
584 acceptData2.StartOffset = commonOffset;
585 acceptData2.Length = commonLength;
586 err = respondingSender.AcceptTransfer(acceptData2);
587 NL_TEST_ASSERT(inSuite, err != CHIP_NO_ERROR);
590 // Test that a TransferSession will emit kTransferTimeout if the specified timeout is exceeded while waiting for a response.
591 void TestTimeout(nlTestSuite * inSuite, void * inContext)
593 CHIP_ERROR err = CHIP_NO_ERROR;
594 TransferSession initiator;
595 TransferSession::OutputEvent outEvent;
597 uint32_t timeoutMs = 24;
598 uint64_t startTimeMs = 100;
599 uint64_t endTimeMs = 124;
601 // Initialize struct with arbitrary TransferInit parameters
602 TransferSession::TransferInitData initOptions;
603 initOptions.TransferCtlFlags = TransferControlFlags::kReceiverDrive;
604 initOptions.MaxBlockSize = 64;
605 initOptions.StartOffset = 0;
606 initOptions.Length = 0;
607 char testFileDes[9] = { "test.txt" }; // arbitrary file designator
608 initOptions.FileDesLength = static_cast<uint16_t>(strlen(testFileDes));
609 initOptions.FileDesignator = reinterpret_cast<uint8_t *>(testFileDes);
610 initOptions.Metadata = nullptr;
611 initOptions.MetadataLength = 0;
613 TransferRole role = TransferRole::kReceiver;
615 // Verify initiator outputs respective Init message (depending on role) after StartTransfer()
616 err = initiator.StartTransfer(role, initOptions, timeoutMs);
617 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
619 // First PollOutput() should output the TransferInit message
620 initiator.PollOutput(outEvent, startTimeMs);
621 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
622 MessageType expectedInitMsg = (role == TransferRole::kSender) ? MessageType::SendInit : MessageType::ReceiveInit;
623 VerifyBdxMessageType(inSuite, inContext, outEvent.MsgData, expectedInitMsg);
625 // Second PollOutput() with no call to HandleMessageReceived() should result in a timeout.
626 initiator.PollOutput(outEvent, endTimeMs);
627 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kTransferTimeout);
630 // Test that sending the same block twice (with same block counter) results in a StatusReport message with BadBlockCounter. Also
631 // test that receiving the StatusReport ends the transfer on the other node.
632 void TestDuplicateBlockError(nlTestSuite * inSuite, void * inContext)
634 CHIP_ERROR err = CHIP_NO_ERROR;
635 TransferSession::OutputEvent outEvent;
636 TransferSession::OutputEvent eventWithBlock;
637 TransferSession initiatingReceiver;
638 TransferSession respondingSender;
640 uint8_t fakeData[64] = { 0 };
641 uint8_t fakeDataLen = sizeof(fakeData);
642 uint16_t blockSize = sizeof(fakeData);
644 // Chosen arbitrarily for this test
645 uint64_t proposedOffset = 64;
646 uint64_t proposedLength = 0;
647 uint32_t timeoutMs = 1000 * 24;
649 // Chosen specifically for this test
650 TransferControlFlags driveMode = TransferControlFlags::kReceiverDrive;
652 // ReceiveInit parameters
653 TransferSession::TransferInitData initOptions;
654 initOptions.TransferCtlFlags = driveMode;
655 initOptions.MaxBlockSize = blockSize;
656 char testFileDes[9] = { "test.txt" };
657 initOptions.FileDesLength = static_cast<uint16_t>(strlen(testFileDes));
658 initOptions.FileDesignator = reinterpret_cast<uint8_t *>(testFileDes);
660 // Initialize respondingSender and pass ReceiveInit message
661 BitFlags<TransferControlFlags> senderOpts;
662 senderOpts.Set(driveMode);
664 SendAndVerifyTransferInit(inSuite, inContext, outEvent, timeoutMs, initiatingReceiver, TransferRole::kReceiver, initOptions,
665 respondingSender, senderOpts, blockSize);
667 // Compose ReceiveAccept parameters struct and give to respondingSender
668 TransferSession::TransferAcceptData acceptData;
669 acceptData.ControlMode = respondingSender.GetControlMode();
670 acceptData.StartOffset = proposedOffset;
671 acceptData.Length = proposedLength;
672 acceptData.MaxBlockSize = blockSize;
673 acceptData.Metadata = nullptr;
674 acceptData.MetadataLength = 0;
676 SendAndVerifyAcceptMsg(inSuite, inContext, outEvent, respondingSender, TransferRole::kSender, acceptData, initiatingReceiver,
679 SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
681 TransferSession::BlockData blockData;
682 blockData.Data = fakeData;
683 blockData.Length = fakeDataLen;
684 blockData.IsEof = false;
686 // Provide Block data and verify sender emits Block message
687 err = respondingSender.PrepareBlock(blockData);
688 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
689 respondingSender.PollOutput(eventWithBlock, kNoAdvanceTime);
690 NL_TEST_ASSERT(inSuite, eventWithBlock.EventType == TransferSession::OutputEventType::kMsgToSend);
691 VerifyBdxMessageType(inSuite, inContext, eventWithBlock.MsgData, MessageType::Block);
692 VerifyNoMoreOutput(inSuite, inContext, respondingSender);
693 System::PacketBufferHandle blockCopy =
694 System::PacketBufferHandle::NewWithData(eventWithBlock.MsgData->Start(), eventWithBlock.MsgData->DataLength());
696 // Pass Block message to receiver and verify matching Block is received
697 err = initiatingReceiver.HandleMessageReceived(std::move(eventWithBlock.MsgData), kNoAdvanceTime);
698 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
699 initiatingReceiver.PollOutput(outEvent, kNoAdvanceTime);
700 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kBlockReceived);
701 NL_TEST_ASSERT(inSuite, outEvent.blockdata.Data != nullptr);
702 VerifyNoMoreOutput(inSuite, inContext, initiatingReceiver);
704 SendAndVerifyQuery(inSuite, inContext, respondingSender, initiatingReceiver, outEvent);
706 // Verify receiving same Block twice fails and results in StatusReport event, and then InternalError event
707 err = initiatingReceiver.HandleMessageReceived(std::move(blockCopy), kNoAdvanceTime);
708 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
709 initiatingReceiver.PollOutput(outEvent, kNoAdvanceTime);
710 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kMsgToSend);
711 System::PacketBufferHandle statusReportMsg = outEvent.MsgData.Retain();
712 VerifyStatusReport(inSuite, inContext, std::move(outEvent.MsgData), StatusCode::kBadBlockCounter);
714 // All subsequent PollOutput() calls should return kInternalError
715 for (int i = 0; i < 5; ++i)
717 initiatingReceiver.PollOutput(outEvent, kNoAdvanceTime);
718 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kInternalError);
719 NL_TEST_ASSERT(inSuite, outEvent.statusData.statusCode == StatusCode::kBadBlockCounter);
722 err = respondingSender.HandleMessageReceived(std::move(statusReportMsg), kNoAdvanceTime);
723 NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
724 respondingSender.PollOutput(outEvent, kNoAdvanceTime);
725 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kStatusReceived);
726 NL_TEST_ASSERT(inSuite, outEvent.statusData.statusCode == StatusCode::kBadBlockCounter);
728 // All subsequent PollOutput() calls should return kInternalError
729 for (int i = 0; i < 5; ++i)
731 respondingSender.PollOutput(outEvent, kNoAdvanceTime);
732 NL_TEST_ASSERT(inSuite, outEvent.EventType == TransferSession::OutputEventType::kInternalError);
733 NL_TEST_ASSERT(inSuite, outEvent.statusData.statusCode == StatusCode::kBadBlockCounter);
740 * Test Suite that lists all the test functions.
743 static const nlTest sTests[] =
745 NL_TEST_DEF("TestInitiatingReceiverReceiverDrive", TestInitiatingReceiverReceiverDrive),
746 NL_TEST_DEF("TestInitiatingSenderSenderDrive", TestInitiatingSenderSenderDrive),
747 NL_TEST_DEF("TestBadAcceptMessageFields", TestBadAcceptMessageFields),
748 NL_TEST_DEF("TestTimeout", TestTimeout),
749 NL_TEST_DEF("TestDuplicateBlockError", TestDuplicateBlockError),
754 int TestBdxTransferSession_Setup(void * inContext)
756 CHIP_ERROR error = chip::Platform::MemoryInit();
757 if (error != CHIP_NO_ERROR)
762 int TestBdxTransferSession_Teardown(void * inContext)
764 chip::Platform::MemoryShutdown();
769 static nlTestSuite sSuite =
771 "Test-CHIP-TransferSession",
773 TestBdxTransferSession_Setup,
774 TestBdxTransferSession_Teardown
781 int TestBdxTransferSession()
783 // Run test suit against one context
784 nlTestRunner(&sSuite, nullptr);
786 return (nlTestRunnerStats(&sSuite));
789 CHIP_REGISTER_TEST_SUITE(TestBdxTransferSession)