#include #include #include #include #include "OwnedPduPacket.h" #include "cfdp/PduSenderMock.h" #include "fsfw/cfdp.h" #include "fsfw/cfdp/pdu/EofPduCreator.h" #include "fsfw/cfdp/pdu/FileDataCreator.h" #include "fsfw/cfdp/pdu/MetadataPduCreator.h" #include "mock/AcceptsTmMock.h" #include "mock/EventReportingProxyMock.h" #include "mock/FilesystemMock.h" #include "mock/MessageQueueMock.h" #include "mock/StorageManagerMock.h" #include "mock/cfdp/FaultHandlerMock.h" #include "mock/cfdp/RemoteConfigTableMock.h" #include "mock/cfdp/UserMock.h" TEST_CASE("CFDP Dest Handler", "[cfdp]") { using namespace cfdp; using namespace returnvalue; MessageQueueId_t destQueueId = 2; AcceptsTmMock tmReceiver(destQueueId); MessageQueueMock mqMock(destQueueId, MessageQueueIF::NO_QUEUE); auto localId = EntityId(UnsignedByteField(2)); auto remoteId = EntityId(UnsignedByteField(3)); FaultHandlerMock fhMock; LocalEntityCfg localEntityCfg(localId, IndicationCfg(), fhMock); FilesystemMock fsMock; UserMock userMock(fsMock); RemoteConfigTableMock remoteCfgTableMock; LostSegmentsList<128> lostSegmentsList; DestHandlerParams dp(localEntityCfg, userMock, remoteCfgTableMock, lostSegmentsList); EventReportingProxyMock eventReporterMock; LocalPool::LocalPoolConfig storeCfg = {{10, 32}, {10, 64}, {10, 128}, {10, 1024}}; PduSenderMock senderMock; FsfwParams fp(&eventReporterMock); RemoteEntityCfg cfg(remoteId); remoteCfgTableMock.addRemoteConfig(cfg); std::array pduBuf{}; size_t serLen = 0; store_address_t storeId; PduConfig conf; auto destHandler = DestHandler(senderMock, 4096, dp, fp); auto metadataPreparation = [&](Fss cfdpFileSize, ChecksumType checksumType) { const std::string srcNameString = "hello.txt"; const std::string destNameString = "hello-cpy.txt"; StringLv srcName(srcNameString); StringLv destName(destNameString); MetadataGenericInfo info(false, checksumType, std::move(cfdpFileSize)); const TransactionSeqNum seqNum(UnsignedByteField(1)); conf.sourceId = remoteId; conf.destId = localId; conf.mode = TransmissionMode::UNACKNOWLEDGED; conf.seqNum = seqNum; const MetadataPduCreator metadataCreator(conf, info, srcName, destName, nullptr, 0); REQUIRE(metadataCreator.serialize(pduBuf.data(), serLen, metadataCreator.getSerializedSize()) == OK); return OwnedPduPacket(metadataCreator.getPduType(), metadataCreator.getDirectiveCode(), pduBuf.data(), serLen); }; auto metadataCheck = [&](const cfdp::DestHandler::FsmResult& res, const char* sourceName, const char* destName, size_t fileLen) { REQUIRE(res.result == OK); REQUIRE(res.errors == 0); REQUIRE(userMock.metadataRecvd.size() == 1); auto& idMetadataPair = userMock.metadataRecvd.back(); REQUIRE(idMetadataPair.first == destHandler.getTransactionId()); REQUIRE(idMetadataPair.second.sourceId.getValue() == 3); REQUIRE(idMetadataPair.second.fileSize.getSize(nullptr) == fileLen); REQUIRE(strcmp(idMetadataPair.second.destFileName, destName) == 0); REQUIRE(strcmp(idMetadataPair.second.sourceFileName, sourceName) == 0); userMock.metadataRecvd.pop(); REQUIRE(fsMock.fileMap.find(destName) != fsMock.fileMap.end()); REQUIRE(res.result == OK); REQUIRE(res.state == CfdpState::BUSY_CLASS_1_NACKED); REQUIRE(res.step == DestHandler::TransactionStep::RECEIVING_FILE_DATA_PDUS); }; auto eofPreparation = [&](Fss cfdpFileSize, uint32_t crc) { EofInfo eofInfo(cfdp::ConditionCode::NO_ERROR, crc, std::move(cfdpFileSize)); EofPduCreator eofCreator(conf, eofInfo); REQUIRE(eofCreator.serialize(pduBuf.data(), serLen, eofCreator.getSerializedSize()) == OK); OwnedPduPacket packet(eofCreator.getPduType(), eofCreator.getDirectiveCode(), pduBuf.data(), serLen); return packet; }; auto eofCheck = [&](const cfdp::DestHandler::FsmResult& res, const TransactionId& id) { REQUIRE(res.result == OK); REQUIRE(res.state == CfdpState::IDLE); REQUIRE(res.errors == 0); REQUIRE(res.step == DestHandler::TransactionStep::IDLE); REQUIRE(userMock.eofRecvdRecvd.size() == 1); auto& eofId = userMock.eofRecvdRecvd.back(); CHECK(eofId == id); REQUIRE(userMock.finishedRecvd.size() == 1); auto& idParamPair = userMock.finishedRecvd.back(); CHECK(idParamPair.first == id); CHECK(idParamPair.second.condCode == ConditionCode::NO_ERROR); }; auto fileDataPduCheck = [&](const cfdp::DestHandler::FsmResult& res, const std::vector& idsToCheck) { REQUIRE(res.result == OK); REQUIRE(res.state == CfdpState::BUSY_CLASS_1_NACKED); REQUIRE(res.step == DestHandler::TransactionStep::RECEIVING_FILE_DATA_PDUS); }; SECTION("State") { CHECK(destHandler.getCfdpState() == CfdpState::IDLE); CHECK(destHandler.getTransactionStep() == DestHandler::TransactionStep::IDLE); } SECTION("Idle State Machine Iteration") { auto [packetsSent, result, step, state, errors, errorCodes] = destHandler.stateMachineNoPacket(); CHECK(result == OK); CHECK(errors == 0); CHECK(destHandler.getCfdpState() == CfdpState::IDLE); CHECK(destHandler.getTransactionStep() == DestHandler::TransactionStep::IDLE); } SECTION("Empty File Transfer") { const DestHandler::FsmResult& res = destHandler.stateMachineNoPacket(); CHECK(res.result == OK); Fss cfdpFileSize(0); auto packet = metadataPreparation(cfdpFileSize, ChecksumType::NULL_CHECKSUM); destHandler.stateMachine(packet); metadataCheck(res, "hello.txt", "hello-cpy.txt", 0); destHandler.stateMachine(packet); // REQUIRE(res.callStatus == CallStatus::CALL_AFTER_DELAY); auto transactionId = destHandler.getTransactionId(); auto ownedEofPacket = eofPreparation(cfdpFileSize, 0); // After EOF, operation is done because no closure was requested destHandler.stateMachine(ownedEofPacket); eofCheck(res, transactionId); } SECTION("Small File Transfer") { const DestHandler::FsmResult& res = destHandler.stateMachineNoPacket(); CHECK(res.result == OK); std::string fileData = "hello test data"; etl::crc32 crcCalc; crcCalc.add(fileData.begin(), fileData.end()); uint32_t crc32 = crcCalc.value(); Fss cfdpFileSize(fileData.size()); auto metadataPacket = metadataPreparation(cfdpFileSize, ChecksumType::CRC_32); destHandler.stateMachine(metadataPacket); metadataCheck(res, "hello.txt", "hello-cpy.txt", fileData.size()); destHandler.stateMachineNoPacket(); auto transactionId = destHandler.getTransactionId(); Fss offset(0); FileDataInfo fdPduInfo(offset, reinterpret_cast(fileData.data()), fileData.size()); FileDataCreator fdPduCreator(conf, fdPduInfo); REQUIRE(fdPduCreator.serialize(pduBuf.data(), serLen, fdPduCreator.getSerializedSize()) == OK); OwnedPduPacket fdPdu(fdPduCreator.getPduType(), std::nullopt, pduBuf.data(), serLen); destHandler.stateMachine(fdPdu); fileDataPduCheck(res, {storeId}); auto eofPacket = eofPreparation(cfdpFileSize, crc32); // After EOF, operation is done because no closure was requested destHandler.stateMachine(eofPacket); eofCheck(res, transactionId); } SECTION("Segmented File Transfer") { const DestHandler::FsmResult& res = destHandler.stateMachineNoPacket(); CHECK(res.result == OK); std::random_device dev; std::mt19937 rng(dev()); std::uniform_int_distribution distU8(0, 255); std::array largerFileData{}; for (auto& val : largerFileData) { val = distU8(rng); } etl::crc32 crcCalc; crcCalc.add(largerFileData.begin(), largerFileData.end()); uint32_t crc32 = crcCalc.value(); Fss cfdpFileSize(largerFileData.size()); auto metaPacket = metadataPreparation(cfdpFileSize, ChecksumType::CRC_32); destHandler.stateMachine(metaPacket); metadataCheck(res, "hello.txt", "hello-cpy.txt", largerFileData.size()); destHandler.stateMachineNoPacket(); auto transactionId = destHandler.getTransactionId(); std::vector idsToCheck; { Fss offset(0); FileDataInfo fdPduInfo(offset, (largerFileData.data()), largerFileData.size() / 2); FileDataCreator fdPduCreator(conf, fdPduInfo); REQUIRE(fdPduCreator.serialize(pduBuf.data(), serLen, fdPduCreator.getSerializedSize()) == OK); idsToCheck.push_back(storeId); OwnedPduPacket fdPdu(fdPduCreator.getPduType(), std::nullopt, pduBuf.data(), serLen); destHandler.stateMachine(fdPdu); } { Fss offset(512); FileDataInfo fdPduInfo(offset, largerFileData.data() + 512, largerFileData.size() / 2); FileDataCreator fdPduCreator(conf, fdPduInfo); REQUIRE(fdPduCreator.serialize(pduBuf.data(), serLen, fdPduCreator.getSerializedSize()) == OK); idsToCheck.push_back(storeId); OwnedPduPacket fdPdu(fdPduCreator.getPduType(), std::nullopt, pduBuf.data(), serLen); destHandler.stateMachine(fdPdu); } destHandler.stateMachineNoPacket(); fileDataPduCheck(res, idsToCheck); auto eofPacket = eofPreparation(cfdpFileSize, crc32); // After EOF, operation is done because no closure was requested destHandler.stateMachine(eofPacket); eofCheck(res, transactionId); } }