/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "lib/self_test.h" #include "network/NetFileTransfer.h" #include "network/NetMessage.h" #include #include #include #include #include #include #include #include using namespace std::literals; namespace { constexpr const char* MESSAGECONTENT{"Some example message content"}; class MessageQueues { public: bool SendMessage(const CNetMessage* message) { switch (message->GetType()) { case NMT_FILE_TRANSFER_REQUEST: requests.push_back(*static_cast(message)); break; case NMT_FILE_TRANSFER_RESPONSE: responses.push_back(*static_cast(message)); break; case NMT_FILE_TRANSFER_DATA: data.push_back(*static_cast(message)); break; case NMT_FILE_TRANSFER_ACK: acknowledgements.push_back(*static_cast(message)); break; default: TS_FAIL("Unhandeled message type"); } return true; } std::vector requests; std::vector responses; std::vector data; std::vector acknowledgements; }; void CheckSizes(MessageQueues& queues, size_t requestSize, size_t responseSize, size_t dataSize, size_t acknowledgementSize) { TS_ASSERT_EQUALS(queues.requests.size(), requestSize); TS_ASSERT_EQUALS(queues.responses.size(), responseSize); TS_ASSERT_EQUALS(queues.data.size(), dataSize); TS_ASSERT_EQUALS(queues.acknowledgements.size(), acknowledgementSize); } struct Participant { MessageQueues queues; CNetFileTransferer transferer{queues}; }; } class TestFileTransfer : public CxxTest::TestSuite { public: void test_transfer() { // The client requests some data from the server. Participant server; Participant client; bool complete{false}; client.transferer.StartTask(CNetFileTransferer::RequestType::LOADGAME, [&complete](std::string buffer) { // This callback is executed exactly once. const bool previousComplete{std::exchange(complete, true)}; TS_ASSERT(!previousComplete); TS_ASSERT_STR_EQUALS(buffer, MESSAGECONTENT); }); CheckSizes(client.queues, 1, 0, 0, 0); server.transferer.StartResponse(client.queues.requests.at(0).m_RequestID, MESSAGECONTENT); CheckSizes(server.queues, 0, 0, 0, 0); while (server.queues.responses.size() == 0) { std::this_thread::sleep_for(10ms); server.transferer.Poll(); } CheckSizes(server.queues, 0, 1, 1, 0); client.transferer.HandleMessageReceive(server.queues.responses.at(0)); CheckSizes(client.queues, 1, 0, 0, 0); server.transferer.Poll(); // If `MESSAGECONTENT` would be longer another message would be sent. CheckSizes(server.queues, 0, 1, 1, 0); TS_ASSERT(!complete); client.transferer.HandleMessageReceive(server.queues.data.at(0)); CheckSizes(client.queues, 1, 0, 0, 1); TS_ASSERT(complete); server.transferer.HandleMessageReceive(client.queues.acknowledgements.at(0)); CheckSizes(server.queues, 0, 1, 1, 0); } void test_RequestType() { for (const auto& requestType : {CNetFileTransferer::RequestType::LOADGAME, CNetFileTransferer::RequestType::REJOIN}) { Participant client; client.transferer.StartTask(requestType, [](auto&&){}); TS_ASSERT_EQUALS(static_cast( client.queues.requests.at(0).m_RequestType), requestType); } } };