LCOV - code coverage report
Current view: top level - test/unitTest/fileTransfer - fileTransfer.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 410 438 93.6 %
Date: 2024-11-15 09:04:49 Functions: 75 78 96.2 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  This program is free software: you can redistribute it and/or modify
       5             :  *  it under the terms of the GNU General Public License as published by
       6             :  *  the Free Software Foundation, either version 3 of the License, or
       7             :  *  (at your option) any later version.
       8             :  *
       9             :  *  This program is distributed in the hope that it will be useful,
      10             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12             :  *  GNU General Public License for more details.
      13             :  *
      14             :  *  You should have received a copy of the GNU General Public License
      15             :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "fileutils.h"
      19             : #include "manager.h"
      20             : 
      21             : #include "jamidht/jamiaccount.h"
      22             : #include "../../test_runner.h"
      23             : #include "jami.h"
      24             : #include "data_transfer.h"
      25             : #include "jami/datatransfer_interface.h"
      26             : #include "account_const.h"
      27             : #include "common.h"
      28             : 
      29             : #include <dhtnet/connectionmanager.h>
      30             : 
      31             : #include <cppunit/TestAssert.h>
      32             : #include <cppunit/TestFixture.h>
      33             : #include <cppunit/extensions/HelperMacros.h>
      34             : 
      35             : #include <condition_variable>
      36             : #include <string>
      37             : #include <filesystem>
      38             : 
      39             : using namespace std::literals::chrono_literals;
      40             : using namespace libjami::Account;
      41             : using namespace std::literals::chrono_literals;
      42             : 
      43             : namespace jami {
      44             : namespace test {
      45             : 
      46             : struct UserData {
      47             :     std::string conversationId;
      48             :     bool removed {false};
      49             :     bool requestReceived {false};
      50             :     bool registered {false};
      51             :     bool stopped {false};
      52             :     bool deviceAnnounced {false};
      53             :     int code {0};
      54             :     std::vector<libjami::SwarmMessage> messages;
      55             :     std::vector<libjami::SwarmMessage> messagesUpdated;
      56             : };
      57             : 
      58             : class FileTransferTest : public CppUnit::TestFixture
      59             : {
      60             : public:
      61          11 :     FileTransferTest()
      62          11 :     {
      63             :         // Init daemon
      64          11 :         libjami::init(
      65             :             libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG));
      66          11 :         if (not Manager::instance().initialized)
      67           1 :             CPPUNIT_ASSERT(libjami::start("jami-sample.yml"));
      68          11 :     }
      69          22 :     ~FileTransferTest() { libjami::fini(); }
      70           2 :     static std::string name() { return "Call"; }
      71             :     bool compare(const std::string& fileA, const std::string& fileB) const;
      72             :     void setUp();
      73             :     void tearDown();
      74             : 
      75             :     std::string aliceId;
      76             :     UserData aliceData;
      77             :     std::string bobId;
      78             :     UserData bobData;
      79             :     std::string carlaId;
      80             :     UserData carlaData;
      81             : 
      82             :     std::filesystem::path sendPath {std::filesystem::current_path() / "SEND"};
      83             :     std::filesystem::path recvPath {std::filesystem::current_path() / "RECV"};
      84             :     std::filesystem::path recv2Path {std::filesystem::current_path() / "RECV2"};
      85             : 
      86             :     std::mutex mtx;
      87             :     std::unique_lock<std::mutex> lk {mtx};
      88             :     std::condition_variable cv;
      89             : 
      90             :     void connectSignals();
      91             : 
      92             : private:
      93             :     void testConversationFileTransfer();
      94             :     void testFileTransferInConversation();
      95             :     void testVcfFileTransferInConversation();
      96             :     void testBadSha3sumOut();
      97             :     void testBadSha3sumIn();
      98             :     void testAskToMultipleParticipants();
      99             :     void testCancelInTransfer();
     100             :     void testCancelOutTransfer();
     101             :     void testTransferInfo();
     102             :     void testRemoveHardLink();
     103             :     void testTooLarge();
     104             :     void testDeleteFile();
     105             : 
     106           2 :     CPPUNIT_TEST_SUITE(FileTransferTest);
     107           1 :     CPPUNIT_TEST(testConversationFileTransfer);
     108           1 :     CPPUNIT_TEST(testFileTransferInConversation);
     109           1 :     CPPUNIT_TEST(testVcfFileTransferInConversation);
     110           1 :     CPPUNIT_TEST(testBadSha3sumOut);
     111           1 :     CPPUNIT_TEST(testBadSha3sumIn);
     112           1 :     CPPUNIT_TEST(testAskToMultipleParticipants);
     113           1 :     CPPUNIT_TEST(testCancelInTransfer);
     114           1 :     CPPUNIT_TEST(testTransferInfo);
     115           1 :     CPPUNIT_TEST(testRemoveHardLink);
     116           1 :     CPPUNIT_TEST(testTooLarge);
     117           1 :     CPPUNIT_TEST(testDeleteFile);
     118           4 :     CPPUNIT_TEST_SUITE_END();
     119             : };
     120             : 
     121             : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(FileTransferTest, FileTransferTest::name());
     122             : 
     123             : bool
     124           0 : FileTransferTest::compare(const std::string& fileA, const std::string& fileB) const
     125             : {
     126           0 :     std::ifstream f1(fileA, std::ifstream::binary | std::ifstream::ate);
     127           0 :     std::ifstream f2(fileB, std::ifstream::binary | std::ifstream::ate);
     128             : 
     129           0 :     if (f1.fail() || f2.fail() || f1.tellg() != f2.tellg()) {
     130           0 :         return false;
     131             :     }
     132             : 
     133           0 :     f1.seekg(0, std::ifstream::beg);
     134           0 :     f2.seekg(0, std::ifstream::beg);
     135           0 :     return std::equal(std::istreambuf_iterator<char>(f1.rdbuf()),
     136             :                       std::istreambuf_iterator<char>(),
     137           0 :                       std::istreambuf_iterator<char>(f2.rdbuf()));
     138           0 : }
     139             : 
     140             : void
     141          11 : FileTransferTest::setUp()
     142             : {
     143          22 :     auto actors = load_actors_and_wait_for_announcement("actors/alice-bob-carla.yml");
     144          11 :     aliceId = actors["alice"];
     145          11 :     bobId = actors["bob"];
     146          11 :     carlaId = actors["carla"];
     147          11 :     aliceData = {};
     148          11 :     bobData = {};
     149          11 :     carlaData = {};
     150          11 : }
     151             : 
     152             : void
     153          11 : FileTransferTest::tearDown()
     154             : {
     155          11 :     std::filesystem::remove(sendPath);
     156          11 :     std::filesystem::remove(recvPath);
     157          11 :     std::filesystem::remove(recv2Path);
     158          44 :     wait_for_removal_of({aliceId, bobId, carlaId});
     159          11 : }
     160             : 
     161             : void
     162          10 : FileTransferTest::connectSignals()
     163             : {
     164          10 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     165          10 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>(
     166          21 :         [&](const std::string& accountId, const std::string& conversationId) {
     167          21 :             if (accountId == aliceId) {
     168          10 :                 aliceData.conversationId = conversationId;
     169          11 :             } else if (accountId == bobId) {
     170           9 :                 bobData.conversationId = conversationId;
     171           2 :             } else if (accountId == carlaId) {
     172           2 :                 carlaData.conversationId = conversationId;
     173             :             }
     174          21 :             cv.notify_one();
     175          21 :         }));
     176          10 :     confHandlers.insert(
     177          20 :         libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>(
     178          11 :             [&](const std::string& accountId,
     179             :                 const std::string& /* conversationId */,
     180             :                 std::map<std::string, std::string> /*metadatas*/) {
     181          11 :                 if (accountId == aliceId) {
     182           0 :                     aliceData.requestReceived = true;
     183          11 :                 } else if (accountId == bobId) {
     184           9 :                     bobData.requestReceived = true;
     185           2 :                 } else if (accountId == carlaId) {
     186           2 :                     carlaData.requestReceived = true;
     187             :                 }
     188          11 :                 cv.notify_one();
     189          11 :             }));
     190          10 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageReceived>(
     191          45 :         [&](const std::string& accountId,
     192             :             const std::string& /* conversationId */,
     193             :             libjami::SwarmMessage message) {
     194          45 :             std::unique_lock<std::mutex> lk {mtx};
     195          45 :             if (accountId == aliceId) {
     196          32 :                 aliceData.messages.emplace_back(message);
     197          13 :             } else if (accountId == bobId) {
     198          11 :                 bobData.messages.emplace_back(message);
     199           2 :             } else if (accountId == carlaId) {
     200           2 :                 carlaData.messages.emplace_back(message);
     201             :             }
     202          45 :             cv.notify_one();
     203          45 :         }));
     204          10 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageUpdated>(
     205           0 :         [&](const std::string& accountId,
     206             :             const std::string& /* conversationId */,
     207             :             libjami::SwarmMessage message) {
     208           0 :             if (accountId == aliceId) {
     209           0 :                 aliceData.messagesUpdated.emplace_back(message);
     210           0 :             } else if (accountId == bobId) {
     211           0 :                 bobData.messagesUpdated.emplace_back(message);
     212           0 :             } else if (accountId == carlaId) {
     213           0 :                 carlaData.messagesUpdated.emplace_back(message);
     214             :             }
     215           0 :             cv.notify_one();
     216           0 :         }));
     217          10 :     confHandlers.insert(
     218          20 :         libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>(
     219           1 :             [&](const std::string& accountId, const std::string&) {
     220           1 :                 if (accountId == aliceId)
     221           1 :                     aliceData.removed = true;
     222           0 :                 else if (accountId == bobId)
     223           0 :                     bobData.removed = true;
     224           1 :                 cv.notify_one();
     225           1 :             }));
     226          10 :     confHandlers.insert(libjami::exportable_callback<libjami::DataTransferSignal::DataTransferEvent>(
     227          43 :         [&](const std::string& accountId,
     228             :             const std::string& conversationId,
     229             :             const std::string&,
     230             :             const std::string& fileId,
     231             :             int code) {
     232          43 :             if (conversationId.empty())
     233           0 :                 return;
     234          43 :             if (accountId == aliceId)
     235          20 :                 aliceData.code = code;
     236          23 :             else if (accountId == bobId)
     237          18 :                 bobData.code = code;
     238           5 :             else if (accountId == carlaId)
     239           5 :                 carlaData.code = code;
     240          43 :             cv.notify_one();
     241             :         }));
     242          10 :     libjami::registerSignalHandlers(confHandlers);
     243          10 : }
     244             : 
     245             : void
     246           1 : FileTransferTest::testConversationFileTransfer()
     247             : {
     248           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     249           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     250           1 :     auto bobUri = bobAccount->getUsername();
     251           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     252           1 :     auto carlaUri = carlaAccount->getUsername();
     253           1 :     aliceAccount->trackBuddyPresence(carlaUri, true);
     254             : 
     255             :     // Enable carla
     256           1 :     Manager::instance().sendRegister(carlaId, true);
     257           1 :     wait_for_announcement_of(carlaId);
     258             : 
     259           1 :     connectSignals();
     260             : 
     261           1 :     auto convId = libjami::startConversation(aliceId);
     262             : 
     263           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     264           1 :     libjami::addConversationMember(aliceId, convId, carlaUri);
     265           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return bobData.requestReceived && carlaData.requestReceived; }));
     266             : 
     267           1 :     auto aliceMsgSize = aliceData.messages.size();
     268           1 :     libjami::acceptConversationRequest(bobId, convId);
     269           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     270           1 :     aliceMsgSize = aliceData.messages.size();
     271           1 :     auto bobMsgSize = bobData.messages.size();
     272           1 :     libjami::acceptConversationRequest(carlaId, convId);
     273           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size() && bobMsgSize + 1 == bobData.messages.size(); }));
     274             : 
     275             :     // Send file
     276           1 :     std::ofstream sendFile(sendPath);
     277           1 :     CPPUNIT_ASSERT(sendFile.is_open());
     278           1 :     sendFile << std::string(64000, 'A');
     279           1 :     sendFile.close();
     280             : 
     281           1 :     bobMsgSize = bobData.messages.size();
     282           1 :     auto carlaMsgSize = carlaData.messages.size();
     283           1 :     libjami::sendFile(aliceId, convId, sendPath, "SEND", "");
     284             : 
     285           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 45s, [&]() { return bobData.messages.size() == bobMsgSize + 1 && carlaData.messages.size() == carlaMsgSize + 1; }));
     286           1 :     auto id = bobData.messages.rbegin()->id;
     287           2 :     auto fileId = bobData.messages.rbegin()->body["fileId"];
     288             : 
     289           1 :     libjami::downloadFile(bobId, convId, id, fileId, recvPath);
     290           1 :     libjami::downloadFile(carlaId, convId, id, fileId, recv2Path);
     291             : 
     292           7 :     CPPUNIT_ASSERT(cv.wait_for(lk, 45s, [&]() { return carlaData.code == static_cast<int>(libjami::DataTransferEventCode::finished) && bobData.code == static_cast<int>(libjami::DataTransferEventCode::finished); }));
     293           1 : }
     294             : 
     295             : void
     296           1 : FileTransferTest::testFileTransferInConversation()
     297             : {
     298           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     299           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     300           1 :     auto bobUri = bobAccount->getUsername();
     301           1 :     auto aliceUri = aliceAccount->getUsername();
     302           1 :     connectSignals();
     303             : 
     304           1 :     auto convId = libjami::startConversation(aliceId);
     305             : 
     306           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     307           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     308             : 
     309           1 :     auto aliceMsgSize = aliceData.messages.size();
     310           1 :     libjami::acceptConversationRequest(bobId, convId);
     311           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     312             : 
     313             :     // Create file to send
     314           1 :     std::ofstream sendFile(sendPath);
     315           1 :     CPPUNIT_ASSERT(sendFile.is_open());
     316           1 :     sendFile << std::string(64000, 'A');
     317           1 :     sendFile.close();
     318             : 
     319           1 :     auto bobMsgSize = bobData.messages.size();
     320           1 :     libjami::sendFile(aliceId, convId, sendPath, "SEND", "");
     321           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize  + 1== bobData.messages.size(); }));
     322             : 
     323           1 :     auto id = bobData.messages.rbegin()->id;
     324           2 :     auto fileId = bobData.messages.rbegin()->body["fileId"];
     325           1 :     libjami::downloadFile(bobId, convId, id, fileId, recvPath);
     326             : 
     327           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 45s, [&]() { return aliceData.code == static_cast<int>(libjami::DataTransferEventCode::finished) && bobData.code == static_cast<int>(libjami::DataTransferEventCode::finished); }));
     328           1 : }
     329             : 
     330             : void
     331           1 : FileTransferTest::testVcfFileTransferInConversation()
     332             : {
     333           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     334           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     335           1 :     auto bobUri = bobAccount->getUsername();
     336           1 :     auto aliceUri = aliceAccount->getUsername();
     337           1 :     connectSignals();
     338           1 :     auto convId = libjami::startConversation(aliceId);
     339             : 
     340           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     341           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     342             : 
     343           1 :     auto aliceMsgSize = aliceData.messages.size();
     344           1 :     libjami::acceptConversationRequest(bobId, convId);
     345           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     346             : 
     347             :     // Create file to send
     348           1 :     std::ofstream sendFile(sendPath);
     349           1 :     CPPUNIT_ASSERT(sendFile.is_open());
     350           1 :     sendFile << std::string(64000, 'A');
     351           1 :     sendFile.close();
     352             : 
     353           1 :     auto bobMsgSize = bobData.messages.size();
     354           1 :     libjami::sendFile(aliceId, convId, sendPath, "SEND", "");
     355           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); }));
     356             : 
     357           1 :     auto id = bobData.messages.rbegin()->id;
     358           2 :     auto fileId = bobData.messages.rbegin()->body["fileId"];
     359           1 :     libjami::downloadFile(bobId, convId, id, fileId, recvPath);
     360             : 
     361           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 45s, [&]() { return aliceData.code == static_cast<int>(libjami::DataTransferEventCode::finished) && bobData.code == static_cast<int>(libjami::DataTransferEventCode::finished); }));
     362           1 : }
     363             : 
     364             : void
     365           1 : FileTransferTest::testBadSha3sumOut()
     366             : {
     367           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     368           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     369           1 :     auto bobUri = bobAccount->getUsername();
     370           1 :     auto aliceUri = aliceAccount->getUsername();
     371           1 :     connectSignals();
     372           1 :     auto convId = libjami::startConversation(aliceId);
     373             : 
     374           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     375           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     376             : 
     377           1 :     auto aliceMsgSize = aliceData.messages.size();
     378           1 :     libjami::acceptConversationRequest(bobId, convId);
     379           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     380             : 
     381             :     // Create file to send
     382           1 :     std::ofstream sendFile(sendPath);
     383           1 :     CPPUNIT_ASSERT(sendFile.is_open());
     384           1 :     sendFile << std::string(64000, 'A');
     385           1 :     sendFile.close();
     386             : 
     387           1 :     auto bobMsgSize = bobData.messages.size();
     388           1 :     libjami::sendFile(aliceId, convId, sendPath, "SEND", "");
     389           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); }));
     390             : 
     391             :     // modifiy file
     392           1 :     sendFile = std::ofstream(sendPath, std::ios::trunc);
     393           1 :     CPPUNIT_ASSERT(sendFile.is_open());
     394           1 :     sendFile << std::string(64000, 'B');
     395           1 :     sendFile.close();
     396             : 
     397           1 :     auto id = bobData.messages.rbegin()->id;
     398           2 :     auto fileId = bobData.messages.rbegin()->body["fileId"];
     399           1 :     libjami::downloadFile(bobId, convId, id, fileId, recvPath);
     400             : 
     401           3 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 45s, [&]() { return aliceData.code == static_cast<int>(libjami::DataTransferEventCode::finished) || bobData.code == static_cast<int>(libjami::DataTransferEventCode::finished); }));
     402           1 : }
     403             : 
     404             : void
     405           1 : FileTransferTest::testBadSha3sumIn()
     406             : {
     407           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     408           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     409           1 :     auto bobUri = bobAccount->getUsername();
     410           1 :     auto aliceUri = aliceAccount->getUsername();
     411           1 :     connectSignals();
     412           1 :     auto convId = libjami::startConversation(aliceId);
     413             : 
     414           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     415           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     416             : 
     417           1 :     auto aliceMsgSize = aliceData.messages.size();
     418           1 :     libjami::acceptConversationRequest(bobId, convId);
     419           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     420             : 
     421             :     // Create file to send
     422           1 :     std::ofstream sendFile(sendPath);
     423           1 :     CPPUNIT_ASSERT(sendFile.is_open());
     424           1 :     sendFile << std::string(64000, 'A');
     425           1 :     sendFile.close();
     426             : 
     427           1 :     aliceAccount->noSha3sumVerification(true);
     428           1 :     auto bobMsgSize = bobData.messages.size();
     429           1 :     libjami::sendFile(aliceId, convId, sendPath, "SEND", "");
     430           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); }));
     431             : 
     432             :     // modifiy file
     433           1 :     sendFile = std::ofstream(sendPath);
     434           1 :     CPPUNIT_ASSERT(sendFile.is_open());
     435             :     // Avoid ASAN error on big alloc   sendFile << std::string("B", 64000);
     436           1 :     sendFile << std::string(64000, 'B');
     437           1 :     sendFile.close();
     438             : 
     439           1 :     auto id = bobData.messages.rbegin()->id;
     440           2 :     auto fileId = bobData.messages.rbegin()->body["fileId"];
     441           1 :     libjami::downloadFile(bobId, convId, id, fileId, recvPath);
     442             : 
     443             :     // The file transfer will be sent but refused by bob
     444           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.code == static_cast<int>(libjami::DataTransferEventCode::finished); }));
     445           5 :     CPPUNIT_ASSERT(!cv.wait_for(lk, 30s, [&]() { return bobData.code == static_cast<int>(libjami::DataTransferEventCode::finished); }));
     446             : 
     447           1 :     std::filesystem::remove(sendPath);
     448           1 : }
     449             : 
     450             : void
     451           1 : FileTransferTest::testAskToMultipleParticipants()
     452             : {
     453           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     454           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     455           1 :     auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId);
     456           1 :     auto aliceUri = aliceAccount->getUsername();
     457           1 :     auto bobUri = bobAccount->getUsername();
     458           1 :     auto carlaUri = carlaAccount->getUsername();
     459           1 :     connectSignals();
     460           1 :     auto convId = libjami::startConversation(aliceId);
     461             : 
     462           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     463           1 :     libjami::addConversationMember(aliceId, convId, carlaUri);
     464           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return bobData.requestReceived && carlaData.requestReceived; }));
     465             : 
     466           1 :     auto aliceMsgSize = aliceData.messages.size();
     467           1 :     libjami::acceptConversationRequest(bobId, convId);
     468           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     469           1 :     aliceMsgSize = aliceData.messages.size();
     470           1 :     auto bobMsgSize = bobData.messages.size();
     471           1 :     libjami::acceptConversationRequest(carlaId, convId);
     472           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size() && bobMsgSize + 1 == bobData.messages.size(); }));
     473             : 
     474             :     // Create file to send
     475           1 :     std::ofstream sendFile(sendPath);
     476           1 :     CPPUNIT_ASSERT(sendFile.is_open());
     477           1 :     sendFile << std::string(64000, 'A');
     478           1 :     sendFile.close();
     479             : 
     480           1 :     bobMsgSize = bobData.messages.size();
     481           1 :     auto carlaMsgSize = carlaData.messages.size();
     482           1 :     libjami::sendFile(aliceId, convId, sendPath, "SEND", "");
     483             : 
     484           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 45s, [&]() { return bobData.messages.size() == bobMsgSize + 1 && carlaData.messages.size() == carlaMsgSize + 1; }));
     485           1 :     auto id = bobData.messages.rbegin()->id;
     486           2 :     auto fileId = bobData.messages.rbegin()->body["fileId"];
     487             : 
     488           1 :     libjami::downloadFile(carlaId, convId, id, fileId, recv2Path);
     489           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaData.code == static_cast<int>(libjami::DataTransferEventCode::finished); }));
     490           1 :     CPPUNIT_ASSERT(dhtnet::fileutils::isFile(recv2Path));
     491             : 
     492           1 :     libjami::downloadFile(bobId, convId, id, fileId, recvPath);
     493           6 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.code == static_cast<int>(libjami::DataTransferEventCode::finished); }));
     494           1 :     CPPUNIT_ASSERT(dhtnet::fileutils::isFile(recvPath));
     495           1 : }
     496             : 
     497             : void
     498           1 : FileTransferTest::testCancelInTransfer()
     499             : {
     500           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     501           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     502           1 :     auto bobUri = bobAccount->getUsername();
     503           1 :     auto aliceUri = aliceAccount->getUsername();
     504           1 :     connectSignals();
     505           1 :     auto convId = libjami::startConversation(aliceId);
     506             : 
     507           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     508           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     509             : 
     510           1 :     auto aliceMsgSize = aliceData.messages.size();
     511           1 :     libjami::acceptConversationRequest(bobId, convId);
     512           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     513             : 
     514             :     // Create file to send
     515           1 :     std::ofstream sendFile(sendPath);
     516           1 :     CPPUNIT_ASSERT(sendFile.is_open());
     517           1 :     sendFile << std::string(640000, 'A');
     518           1 :     sendFile.close();
     519             : 
     520           1 :     auto bobMsgSize = bobData.messages.size();
     521           1 :     libjami::sendFile(aliceId, convId, sendPath, "SEND", "");
     522           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); }));
     523           1 :     auto id = bobData.messages.rbegin()->id;
     524           2 :     auto fileId = bobData.messages.rbegin()->body["fileId"];
     525             : 
     526           1 :     libjami::downloadFile(bobId, convId, id, fileId, recvPath);
     527           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.code == static_cast<int>(libjami::DataTransferEventCode::ongoing); }));
     528             : 
     529           1 :     libjami::cancelDataTransfer(bobId, convId, fileId);
     530           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.code == static_cast<int>(libjami::DataTransferEventCode::closed_by_peer); }));
     531           1 :     CPPUNIT_ASSERT(!dhtnet::fileutils::isFile(recvPath));
     532           1 :     CPPUNIT_ASSERT(!bobAccount->dataTransfer(convId)->isWaiting(fileId));
     533           1 : }
     534             : 
     535             : void
     536           1 : FileTransferTest::testTransferInfo()
     537             : {
     538           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     539           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     540           1 :     auto bobUri = bobAccount->getUsername();
     541           1 :     auto aliceUri = aliceAccount->getUsername();
     542           1 :     connectSignals();
     543           1 :     auto convId = libjami::startConversation(aliceId);
     544             : 
     545           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     546           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     547             : 
     548           1 :     auto aliceMsgSize = aliceData.messages.size();
     549           1 :     libjami::acceptConversationRequest(bobId, convId);
     550           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     551             : 
     552             :     // Create file to send
     553           1 :     std::ofstream sendFile(sendPath);
     554           1 :     CPPUNIT_ASSERT(sendFile.is_open());
     555           1 :     sendFile << std::string(640000, 'A');
     556           1 :     sendFile.close();
     557             : 
     558           1 :     auto bobMsgSize = bobData.messages.size();
     559           1 :     libjami::sendFile(aliceId, convId, sendPath, "SEND", "");
     560           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); }));
     561           1 :     auto id = bobData.messages.rbegin()->id;
     562           2 :     auto fileId = bobData.messages.rbegin()->body["fileId"];
     563             : 
     564             :     int64_t totalSize, bytesProgress;
     565           1 :     std::string path;
     566           1 :     CPPUNIT_ASSERT(libjami::fileTransferInfo(bobId, convId, fileId, path, totalSize, bytesProgress)
     567             :                    == libjami::DataTransferError::invalid_argument);
     568           1 :     CPPUNIT_ASSERT(bytesProgress == 0);
     569           1 :     CPPUNIT_ASSERT(!std::filesystem::is_regular_file(path));
     570             :     // No check for total as not started
     571             : 
     572           1 :     libjami::downloadFile(bobId, convId, id, fileId, recvPath);
     573           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.code == static_cast<int>(libjami::DataTransferEventCode::finished); }));
     574           1 :     CPPUNIT_ASSERT(libjami::fileTransferInfo(bobId, convId, fileId, path, totalSize, bytesProgress)
     575             :                    == libjami::DataTransferError::success);
     576           1 :     CPPUNIT_ASSERT(bytesProgress == 640000);
     577           1 :     CPPUNIT_ASSERT(totalSize == 640000);
     578           1 :     CPPUNIT_ASSERT(dhtnet::fileutils::isFile(path));
     579           1 : }
     580             : 
     581             : void
     582           1 : FileTransferTest::testRemoveHardLink()
     583             : {
     584           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     585           1 :     connectSignals();
     586           1 :     auto convId = libjami::startConversation(aliceId);
     587             : 
     588           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); }));
     589             : 
     590             :     // Send file
     591           1 :     std::ofstream sendFile(sendPath);
     592           1 :     CPPUNIT_ASSERT(sendFile.is_open());
     593           1 :     sendFile << std::string(64000, 'A');
     594           1 :     sendFile.close();
     595             : 
     596           1 :     libjami::sendFile(aliceId, convId, sendPath, std::filesystem::absolute("SEND"), "");
     597             : 
     598           1 :     auto aliceMsgSize = aliceData.messages.size();
     599           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 45s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); }));
     600             : 
     601           1 :     CPPUNIT_ASSERT(libjami::removeConversation(aliceId, convId));
     602           2 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.removed; }));
     603             : 
     604           1 :     auto content = fileutils::loadTextFile(sendPath);
     605           1 :     CPPUNIT_ASSERT(content.find("AAA") != std::string::npos);
     606           1 : }
     607             : 
     608             : void
     609           1 : FileTransferTest::testTooLarge()
     610             : {
     611           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     612           1 :     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     613           1 :     auto bobUri = bobAccount->getUsername();
     614           1 :     connectSignals();
     615           1 :     auto convId = libjami::startConversation(aliceId);
     616             : 
     617           1 :     libjami::addConversationMember(aliceId, convId, bobUri);
     618           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; }));
     619             : 
     620           1 :     auto aliceMsgSize = aliceData.messages.size();
     621           1 :     libjami::acceptConversationRequest(bobId, convId);
     622           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
     623             :         return aliceMsgSize + 1 == aliceData.messages.size(); }));
     624             : 
     625             :     // Send file
     626           1 :     std::ofstream sendFile(sendPath);
     627           1 :     CPPUNIT_ASSERT(sendFile.is_open());
     628           1 :     sendFile << std::string(64000, 'A');
     629           1 :     sendFile.close();
     630             : 
     631           1 :     auto bobMsgSize = bobData.messages.size();
     632           1 :     libjami::sendFile(aliceId, convId, sendPath, "SEND", "");
     633           5 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); }));
     634           1 :     auto id = bobData.messages.rbegin()->id;
     635           2 :     auto fileId = bobData.messages.rbegin()->body["fileId"];
     636             : 
     637             :     // Add some data for the reception. This will break the final shasum
     638           2 :     std::ofstream recvFile(recvPath + std::string(".tmp"));
     639           1 :     CPPUNIT_ASSERT(recvFile.is_open());
     640           1 :     recvFile << std::string(1000, 'B');
     641           1 :     recvFile.close();
     642           1 :     libjami::downloadFile(bobId, convId, id, fileId, recvPath);
     643             : 
     644           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]() { return bobData.code == static_cast<int>(libjami::DataTransferEventCode::closed_by_host); }));
     645           1 :     CPPUNIT_ASSERT(!dhtnet::fileutils::isFile(recvPath));
     646           1 : }
     647             : 
     648             : void
     649           1 : FileTransferTest::testDeleteFile()
     650             : {
     651           1 :     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     652           1 :     auto convId = libjami::startConversation(aliceId);
     653             : 
     654           1 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     655           1 :     bool conversationReady = false;
     656           1 :     std::string iid, tid;
     657           1 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageReceived>(
     658           1 :         [&](const std::string& accountId,
     659             :             const std::string& /* conversationId */,
     660             :             libjami::SwarmMessage message) {
     661           1 :             if (message.type == "application/data-transfer+json") {
     662           1 :                 if (accountId == aliceId) {
     663           1 :                     iid = message.id;
     664           1 :                     tid = message.body["tid"];
     665             :                 }
     666             :             }
     667           1 :             cv.notify_one();
     668           1 :         }));
     669           1 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>(
     670           0 :         [&](const std::string& accountId, const std::string& /* conversationId */) {
     671           0 :             if (accountId == bobId) {
     672           0 :                 conversationReady = true;
     673           0 :                 cv.notify_one();
     674             :             }
     675           0 :         }));
     676           1 :     bool messageUpdated = false;
     677           1 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageUpdated>(
     678           1 :         [&](const std::string& accountId,
     679             :             const std::string& /* conversationId */,
     680             :             libjami::SwarmMessage message) {
     681           1 :             if (accountId == aliceId && message.type == "application/data-transfer+json" && message.body["tid"].empty()) {
     682           1 :                 messageUpdated = true;
     683             :             }
     684           1 :             cv.notify_one();
     685           1 :         }));
     686           1 :     libjami::registerSignalHandlers(confHandlers);
     687             : 
     688             :     // Create file to send
     689           1 :     std::ofstream sendFile(sendPath);
     690           1 :     CPPUNIT_ASSERT(sendFile.is_open());
     691           1 :     sendFile << std::string(64000, 'A');
     692           1 :     sendFile.close();
     693             : 
     694           1 :     libjami::sendFile(aliceId, convId, sendPath, "SEND", "");
     695             : 
     696           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !iid.empty(); }));
     697           2 :     auto dataPath = fileutils::get_data_dir() / aliceId / "conversation_data" / convId;
     698           2 :     CPPUNIT_ASSERT(dhtnet::fileutils::isFile(dataPath / fmt::format("{}_{}", iid, tid)));
     699             : 
     700             :     // Delete file
     701           1 :     libjami::sendMessage(aliceId, convId, ""s, iid, 1);
     702             : 
     703             :     // Verify message is updated
     704           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messageUpdated; }));
     705             :     // Verify file is deleted
     706           2 :     CPPUNIT_ASSERT(!dhtnet::fileutils::isFile(dataPath / fmt::format("{}_{}", iid, tid)));
     707             : 
     708           1 :     libjami::unregisterSignalHandlers();
     709           1 :     std::this_thread::sleep_for(5s);
     710           1 : }
     711             : 
     712             : } // namespace test
     713             : } // namespace jami
     714             : 
     715           1 : RING_TEST_RUNNER(jami::test::FileTransferTest::name())

Generated by: LCOV version 1.14