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