1 /*
2
3 Copyright (c) 2008, Arvid Norberg
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
30
31 */
32
33 #include "libtorrent/session.hpp"
34 #include "libtorrent/session_settings.hpp"
35 #include "libtorrent/time.hpp"
36 #include "libtorrent/hasher.hpp"
37 #include "libtorrent/create_torrent.hpp"
38 #include "libtorrent/alert_types.hpp"
39 #include "libtorrent/torrent.hpp"
40 #include "libtorrent/peer_info.hpp"
41 #include "libtorrent/extensions.hpp"
42 #include "libtorrent/aux_/path.hpp" // for combine_path, current_working_directory
43 #include "libtorrent/magnet_uri.hpp"
44 #include "libtorrent/random.hpp"
45 #include "settings.hpp"
46 #include <tuple>
47 #include <iostream>
48
49 #include "test.hpp"
50 #include "test_utils.hpp"
51 #include "setup_transfer.hpp"
52
53 using namespace lt;
54
55 namespace {
56
wait_priority(torrent_handle const & h,aux::vector<download_priority_t,file_index_t> const & prio)57 bool wait_priority(torrent_handle const& h, aux::vector<download_priority_t, file_index_t> const& prio)
58 {
59 for (int i = 0; i < 10; ++i)
60 {
61 if (h.get_file_priorities() == prio) return true;
62
63 #ifdef NDEBUG
64 std::this_thread::sleep_for(lt::milliseconds(100));
65 #else
66 std::this_thread::sleep_for(lt::milliseconds(300));
67 #endif
68 }
69
70 return h.get_file_priorities() == prio;
71 }
72
prioritize_files(torrent_handle const & h,aux::vector<download_priority_t,file_index_t> const & prio)73 bool prioritize_files(torrent_handle const& h, aux::vector<download_priority_t, file_index_t> const& prio)
74 {
75 h.prioritize_files(prio);
76 return wait_priority(h, prio);
77 }
78
test_running_torrent(std::shared_ptr<torrent_info> info,std::int64_t file_size)79 void test_running_torrent(std::shared_ptr<torrent_info> info, std::int64_t file_size)
80 {
81 settings_pack pack = settings();
82 pack.set_int(settings_pack::alert_mask, alert_category::piece_progress | alert_category::storage);
83 pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48130");
84 pack.set_int(settings_pack::max_retry_port_bind, 10);
85 lt::session ses(pack);
86
87 aux::vector<download_priority_t, file_index_t> zeroes;
88 zeroes.resize(1000, 0_pri);
89 add_torrent_params p;
90 p.flags &= ~torrent_flags::paused;
91 p.flags &= ~torrent_flags::auto_managed;
92 p.ti = info;
93 p.save_path = ".";
94
95 // make sure we correctly handle the case where we pass in
96 // more values than there are files
97 p.file_priorities = zeroes;
98
99 error_code ec;
100 torrent_handle h = ses.add_torrent(p, ec);
101 if (ec)
102 {
103 std::printf("add_torrent: %s\n", ec.message().c_str());
104 return;
105 }
106
107 aux::vector<download_priority_t, file_index_t> ones(std::size_t(info->num_files()), 1_pri);
108 TEST_CHECK(prioritize_files(h, ones));
109
110 torrent_status st = h.status();
111
112 TEST_EQUAL(st.total_wanted, file_size); // we want the single file
113 TEST_EQUAL(st.total_wanted_done, 0);
114
115 aux::vector<download_priority_t, file_index_t> prio(std::size_t(info->num_files()), 1_pri);
116 prio[file_index_t(0)] = 0_pri;
117 TEST_CHECK(prioritize_files(h, prio));
118 st = h.status();
119
120 st = h.status();
121 TEST_EQUAL(st.total_wanted, 0); // we don't want anything
122 TEST_EQUAL(st.total_wanted_done, 0);
123 TEST_EQUAL(int(h.get_file_priorities().size()), info->num_files());
124
125 if (info->num_files() > 1)
126 {
127 prio[file_index_t{1}] = 0_pri;
128 TEST_CHECK(prioritize_files(h, prio));
129
130 st = h.status();
131 TEST_EQUAL(st.total_wanted, file_size);
132 TEST_EQUAL(st.total_wanted_done, 0);
133 }
134
135 if (info->num_pieces() > 0)
136 {
137 h.piece_priority(piece_index_t(0), 1_pri);
138 st = h.status();
139 TEST_CHECK(st.pieces.size() > 0 && st.pieces[piece_index_t(0)] == false);
140 std::vector<char> piece(std::size_t(info->piece_length()));
141 for (int i = 0; i < int(piece.size()); ++i)
142 piece[std::size_t(i)] = (i % 26) + 'A';
143 h.add_piece(piece_index_t(0), &piece[0], torrent_handle::overwrite_existing);
144
145 // wait until the piece is done writing and hashing
146 wait_for_alert(ses, piece_finished_alert::alert_type, "piece_finished_alert");
147 st = h.status();
148 TEST_CHECK(st.pieces.size() > 0);
149
150 std::cout << "reading piece 0" << std::endl;
151 h.read_piece(piece_index_t(0));
152 alert const* a = wait_for_alert(ses, read_piece_alert::alert_type, "read_piece");
153 TEST_CHECK(a);
154 read_piece_alert const* rpa = alert_cast<read_piece_alert>(a);
155 TEST_CHECK(rpa);
156 if (rpa)
157 {
158 std::cout << "SUCCEEDED!" << std::endl;
159 TEST_CHECK(std::memcmp(&piece[0], rpa->buffer.get()
160 , std::size_t(info->piece_size(piece_index_t(0)))) == 0);
161 TEST_CHECK(rpa->size == info->piece_size(piece_index_t(0)));
162 TEST_CHECK(rpa->piece == piece_index_t(0));
163 TEST_CHECK(hasher(piece).final() == info->hash_for_piece(piece_index_t(0)));
164 }
165 }
166
167 TEST_CHECK(h.get_file_priorities() == prio);
168 }
169
test_large_piece_size(int const size)170 void test_large_piece_size(int const size)
171 {
172 entry torrent;
173 entry& info = torrent["info"];
174 info["pieces"] = "aaaaaaaaaaaaaaaaaaaa";
175 info["name"] = "test";
176 info["piece length"] = size;
177 info["length"] = size;
178
179 std::vector<char> buf;
180 bencode(std::back_inserter(buf), torrent);
181 add_torrent_params atp;
182 atp.ti = std::make_shared<torrent_info>(buf, from_span);
183 atp.save_path = ".";
184
185 lt::session ses;
186 auto h = ses.add_torrent(atp);
187 TEST_CHECK(h.status().errc == error_code(lt::errors::invalid_piece_size));
188 h.clear_error();
189 TEST_CHECK(h.status().errc == error_code(lt::errors::invalid_piece_size));
190 }
191
192 } // anonymous namespace
193
TORRENT_TEST(long_names)194 TORRENT_TEST(long_names)
195 {
196 entry torrent;
197 entry& info = torrent["info"];
198 info["pieces"] = "aaaaaaaaaaaaaaaaaaaa";
199 info["name"] = "slightly shorter name, it's kind of sad that people started "
200 "the trend of incorrectly encoding the regular name field and then adding "
201 "another one with correct encoding";
202 info["name.utf-8"] = "this is a long ass name in order to try to make "
203 "make_magnet_uri overflow and hopefully crash. Although, by the time you "
204 "read this that particular bug should have been fixed";
205 info["piece length"] = 16 * 1024;
206 info["length"] = 3245;
207
208 std::vector<char> buf;
209 bencode(std::back_inserter(buf), torrent);
210 auto ti = std::make_shared<torrent_info>(buf, from_span);
211 }
212
TORRENT_TEST(large_piece_size)213 TORRENT_TEST(large_piece_size)
214 {
215 test_large_piece_size(32768 * 16 * 1024);
216 test_large_piece_size(65536 * 16 * 1024);
217 test_large_piece_size(65537 * 16 * 1024);
218 }
219
TORRENT_TEST(total_wanted)220 TORRENT_TEST(total_wanted)
221 {
222 file_storage fs;
223
224 fs.add_file("test_torrent_dir4/tmp1", 1024);
225 fs.add_file("test_torrent_dir4/tmp2", 1024);
226 fs.add_file("test_torrent_dir4/tmp3", 1024);
227 fs.add_file("test_torrent_dir4/tmp4", 1024);
228
229 lt::create_torrent t(fs, 1024);
230 std::vector<char> tmp;
231 bencode(std::back_inserter(tmp), t.generate());
232 auto info = std::make_shared<torrent_info>(tmp, from_span);
233
234 settings_pack pack = settings();
235 pack.set_int(settings_pack::alert_mask, alert_category::storage);
236 pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48130");
237 pack.set_int(settings_pack::max_retry_port_bind, 10);
238 lt::session ses(pack);
239
240 add_torrent_params p;
241 p.ti = info;
242 p.save_path = ".";
243
244 // we just want 1 out of 4 files, 1024 out of 4096 bytes
245 p.file_priorities.resize(4, 0_pri);
246 p.file_priorities[1] = 1_pri;
247
248 torrent_handle h = ses.add_torrent(std::move(p));
249
250 torrent_status st = h.status();
251 TEST_EQUAL(st.total_wanted, 1024);
252 TEST_EQUAL(st.total_wanted_done, 0);
253
254 // make sure that selecting and unseleting a file quickly still end up with
255 // the last set priority
256 h.file_priority(file_index_t{1}, default_priority);
257 h.file_priority(file_index_t{1}, dont_download);
258 TEST_CHECK(wait_priority(h, aux::vector<download_priority_t, file_index_t>(static_cast<std::size_t>(fs.num_files()))));
259 TEST_EQUAL(h.status({}).total_wanted, 0);
260 }
261
TORRENT_TEST(added_peers)262 TORRENT_TEST(added_peers)
263 {
264 file_storage fs;
265
266 fs.add_file("test_torrent_dir4/tmp1", 1024);
267
268 lt::create_torrent t(fs, 1024);
269 std::vector<char> tmp;
270 bencode(std::back_inserter(tmp), t.generate());
271 auto info = std::make_shared<torrent_info>(tmp, from_span);
272
273 settings_pack pack = settings();
274 pack.set_str(settings_pack::listen_interfaces, "0.0.0.0:48130");
275 pack.set_int(settings_pack::max_retry_port_bind, 10);
276 lt::session ses(pack);
277
278 add_torrent_params p = parse_magnet_uri(
279 "magnet:?xt=urn:btih:abababababababababababababababababababab&x.pe=127.0.0.1:48081&x.pe=127.0.0.2:48082");
280 p.ti = info;
281 p.info_hash.clear();
282 p.save_path = ".";
283
284 torrent_handle h = ses.add_torrent(std::move(p));
285
286 h.save_resume_data();
287 alert const* a = wait_for_alert(ses, save_resume_data_alert::alert_type);
288
289 TEST_CHECK(a);
290 save_resume_data_alert const* ra = alert_cast<save_resume_data_alert>(a);
291 TEST_CHECK(ra);
292 if (ra) TEST_EQUAL(ra->params.peers.size(), 2);
293 }
294
TORRENT_TEST(mismatching_info_hash)295 TORRENT_TEST(mismatching_info_hash)
296 {
297 file_storage fs;
298 fs.add_file("test_torrent_dir4/tmp1", 1024);
299 lt::create_torrent t(fs, 1024);
300 std::vector<char> tmp;
301 bencode(std::back_inserter(tmp), t.generate());
302 auto info = std::make_shared<torrent_info>(tmp, from_span);
303
304 add_torrent_params p;
305 p.ti = std::move(info);
306
307 // this info-hash is definitely different from the one in `info`, this
308 // should trigger a failure
309 p.info_hash = lt::sha1_hash("01010101010101010101");
310 p.save_path = ".";
311
312 lt::session ses(settings());
313 error_code ec;
314 torrent_handle h = ses.add_torrent(std::move(p), ec);
315 TEST_CHECK(ec == lt::errors::mismatching_info_hash);
316 TEST_CHECK(!h.is_valid());
317 }
318
TORRENT_TEST(exceed_file_prio)319 TORRENT_TEST(exceed_file_prio)
320 {
321 file_storage fs;
322 fs.add_file("test_torrent_dir4/tmp1", 1024);
323 lt::create_torrent t(fs, 1024);
324 std::vector<char> tmp;
325 bencode(std::back_inserter(tmp), t.generate());
326 auto info = std::make_shared<torrent_info>(tmp, from_span);
327
328 add_torrent_params p;
329 p.ti = std::move(info);
330
331 p.file_priorities.resize(9999, lt::low_priority);
332 p.save_path = ".";
333
334 lt::session ses(settings());
335 error_code ec;
336 torrent_handle h = ses.add_torrent(std::move(p));
337 auto const prios = h.get_file_priorities();
338 TEST_CHECK(prios.size() == 1);
339 }
340
TORRENT_TEST(exceed_piece_prio)341 TORRENT_TEST(exceed_piece_prio)
342 {
343 file_storage fs;
344 fs.add_file("test_torrent_dir4/tmp1", 1024);
345 lt::create_torrent t(fs, 1024);
346 std::vector<char> tmp;
347 bencode(std::back_inserter(tmp), t.generate());
348 auto info = std::make_shared<torrent_info>(tmp, from_span);
349 std::size_t const num_pieces = std::size_t(info->num_pieces());
350
351 add_torrent_params p;
352 p.ti = std::move(info);
353
354 p.piece_priorities.resize(9999, lt::low_priority);
355 p.save_path = ".";
356
357 lt::session ses(settings());
358 error_code ec;
359 torrent_handle h = ses.add_torrent(std::move(p));
360 auto const prios = h.get_piece_priorities();
361 TEST_CHECK(prios.size() == num_pieces);
362 }
363
TORRENT_TEST(exceed_piece_prio_magnet)364 TORRENT_TEST(exceed_piece_prio_magnet)
365 {
366 add_torrent_params p;
367 p.info_hash = sha1_hash("abababababababababab");
368 p.piece_priorities.resize(9999, lt::low_priority);
369 p.save_path = ".";
370
371 lt::session ses(settings());
372 error_code ec;
373 torrent_handle h = ses.add_torrent(std::move(p));
374 auto const prios = h.get_piece_priorities();
375 TEST_CHECK(prios.empty());
376 }
377
TORRENT_TEST(torrent)378 TORRENT_TEST(torrent)
379 {
380 /* {
381 remove("test_torrent_dir2/tmp1");
382 remove("test_torrent_dir2/tmp2");
383 remove("test_torrent_dir2/tmp3");
384 file_storage fs;
385 std::int64_t file_size = 256 * 1024;
386 fs.add_file("test_torrent_dir2/tmp1", file_size);
387 fs.add_file("test_torrent_dir2/tmp2", file_size);
388 fs.add_file("test_torrent_dir2/tmp3", file_size);
389 lt::create_torrent t(fs, 128 * 1024);
390 t.add_tracker("http://non-existing.com/announce");
391
392 std::vector<char> piece(128 * 1024);
393 for (int i = 0; i < int(piece.size()); ++i)
394 piece[i] = (i % 26) + 'A';
395
396 // calculate the hash for all pieces
397 sha1_hash ph = hasher(piece).final();
398 int num = t.num_pieces();
399 TEST_CHECK(t.num_pieces() > 0);
400 for (int i = 0; i < num; ++i)
401 t.set_hash(i, ph);
402
403 std::vector<char> tmp;
404 std::back_insert_iterator<std::vector<char>> out(tmp);
405 bencode(out, t.generate());
406 error_code ec;
407 auto info = std::make_shared<torrent_info>(tmp, std::ref(ec), from_span);
408 TEST_CHECK(info->num_pieces() > 0);
409
410 test_running_torrent(info, file_size);
411 }
412 */
413 {
414 file_storage fs;
415
416 fs.add_file("test_torrent_dir2/tmp1", 1024);
417 lt::create_torrent t(fs, 1024, 6);
418
419 std::vector<char> piece(1024);
420 for (int i = 0; i < int(piece.size()); ++i)
421 piece[std::size_t(i)] = (i % 26) + 'A';
422
423 // calculate the hash for all pieces
424 sha1_hash const ph = hasher(piece).final();
425 TEST_CHECK(t.num_pieces() > 0);
426 for (auto const i : fs.piece_range())
427 t.set_hash(i, ph);
428
429 std::vector<char> tmp;
430 std::back_insert_iterator<std::vector<char>> out(tmp);
431 bencode(out, t.generate());
432 auto info = std::make_shared<torrent_info>(tmp, from_span);
433 test_running_torrent(info, 1024);
434 }
435 }
436
437 #ifndef TORRENT_DISABLE_EXTENSIONS
438 struct test_plugin : lt::torrent_plugin {};
439
440 struct plugin_creator
441 {
plugin_creatorplugin_creator442 explicit plugin_creator(int& c) : m_called(c) {}
443
444 std::shared_ptr<lt::torrent_plugin>
operator ()plugin_creator445 operator()(torrent_handle const&, void*)
446 {
447 ++m_called;
448 return std::make_shared<test_plugin>();
449 }
450
451 int& m_called;
452 };
453
TORRENT_TEST(duplicate_is_not_error)454 TORRENT_TEST(duplicate_is_not_error)
455 {
456 file_storage fs;
457
458 fs.add_file("test_torrent_dir2/tmp1", 1024);
459 lt::create_torrent t(fs, 128 * 1024, 6);
460
461 std::vector<char> piece(128 * 1024);
462 for (int i = 0; i < int(piece.size()); ++i)
463 piece[std::size_t(i)] = (i % 26) + 'A';
464
465 // calculate the hash for all pieces
466 sha1_hash ph = hasher(piece).final();
467 TEST_CHECK(t.num_pieces() > 0);
468 for (auto const i : fs.piece_range())
469 t.set_hash(i, ph);
470
471 std::vector<char> tmp;
472 std::back_insert_iterator<std::vector<char>> out(tmp);
473 bencode(out, t.generate());
474
475 int called = 0;
476 plugin_creator creator(called);
477
478 add_torrent_params p;
479 p.ti = std::make_shared<torrent_info>(tmp, from_span);
480 p.flags &= ~torrent_flags::paused;
481 p.flags &= ~torrent_flags::auto_managed;
482 p.flags &= ~torrent_flags::duplicate_is_error;
483 p.save_path = ".";
484 p.extensions.push_back(creator);
485
486 lt::session ses(settings());
487 ses.async_add_torrent(p);
488 ses.async_add_torrent(std::move(p));
489
490 wait_for_downloading(ses, "ses");
491
492 // we should only have added the plugin once
493 TEST_EQUAL(called, 1);
494 }
495 #endif
496
TORRENT_TEST(torrent_total_size_zero)497 TORRENT_TEST(torrent_total_size_zero)
498 {
499 file_storage fs;
500
501 fs.add_file("test_torrent_dir2/tmp1", 0);
502 TEST_CHECK(fs.num_files() == 1);
503 TEST_CHECK(fs.total_size() == 0);
504
505 error_code ec;
506 lt::create_torrent t1(fs);
507 set_piece_hashes(t1, ".", ec);
508 TEST_CHECK(ec);
509
510 fs.add_file("test_torrent_dir2/tmp2", 0);
511 TEST_CHECK(fs.num_files() == 2);
512 TEST_CHECK(fs.total_size() == 0);
513
514 ec.clear();
515 lt::create_torrent t2(fs);
516 set_piece_hashes(t2, ".", ec);
517 TEST_CHECK(ec);
518 }
519
TORRENT_TEST(rename_file)520 TORRENT_TEST(rename_file)
521 {
522 file_storage fs;
523
524 fs.add_file("test3/tmp1", 20);
525 fs.add_file("test3/tmp2", 20);
526 lt::create_torrent t(fs, 128 * 1024, 6);
527
528 std::vector<char> tmp;
529 std::back_insert_iterator<std::vector<char>> out(tmp);
530 bencode(out, t.generate());
531 auto info = std::make_shared<torrent_info>(tmp, from_span);
532
533 TEST_EQUAL(info->files().file_path(file_index_t(0)), combine_path("test3","tmp1"));
534
535 // move "test3/tmp1" -> "tmp1"
536 info->rename_file(file_index_t(0), "tmp1");
537
538 TEST_EQUAL(info->files().file_path(file_index_t(0)), "tmp1");
539 }
540
541 #if TORRENT_ABI_VERSION == 1
TORRENT_TEST(async_load_deprecated)542 TORRENT_TEST(async_load_deprecated)
543 {
544 settings_pack pack = settings();
545 lt::session ses(pack);
546
547 add_torrent_params p;
548 p.flags &= ~torrent_flags::paused;
549 p.flags &= ~torrent_flags::auto_managed;
550 std::string dir = parent_path(current_working_directory());
551
552 p.url = "file://" + combine_path(combine_path(dir, "test_torrents"), "base.torrent");
553 p.save_path = ".";
554 ses.async_add_torrent(std::move(p));
555
556 alert const* a = wait_for_alert(ses, add_torrent_alert::alert_type);
557 TEST_CHECK(a);
558 if (a == nullptr) return;
559 auto const* ta = alert_cast<add_torrent_alert const>(a);
560 TEST_CHECK(ta);
561 if (ta == nullptr) return;
562 TEST_CHECK(!ta->error);
563 TEST_CHECK(ta->params.ti->name() == "temp");
564 }
565 #endif
566
TORRENT_TEST(torrent_status)567 TORRENT_TEST(torrent_status)
568 {
569 TEST_EQUAL(static_cast<int>(torrent_status::error_file_none), -1);
570 #if TORRENT_ABI_VERSION == 1
571 TEST_EQUAL(static_cast<int>(torrent_status::error_file_url), -2);
572 TEST_EQUAL(static_cast<int>(torrent_status::error_file_metadata), -4);
573 #endif
574 TEST_EQUAL(static_cast<int>(torrent_status::error_file_ssl_ctx), -3);
575 TEST_EQUAL(static_cast<int>(torrent_status::error_file_exception), -5);
576 }
577
578 namespace {
579
test_queue(add_torrent_params)580 void test_queue(add_torrent_params)
581 {
582 lt::settings_pack pack = settings();
583 // we're not testing the hash check, just accept the data we write
584 pack.set_bool(settings_pack::disable_hash_checks, true);
585 lt::session ses(pack);
586
587 std::vector<torrent_handle> torrents;
588 for(int i = 0; i < 6; i++)
589 {
590 file_storage fs;
591 std::stringstream file_path;
592 file_path << "test_torrent_dir4/queue" << i;
593 fs.add_file(file_path.str(), 1024);
594 lt::create_torrent t(fs, 128 * 1024, 6);
595
596 std::vector<char> buf;
597 bencode(std::back_inserter(buf), t.generate());
598 auto ti = std::make_shared<torrent_info>(buf, from_span);
599 add_torrent_params p;
600 p.ti = ti;
601 p.save_path = ".";
602 torrents.push_back(ses.add_torrent(std::move(p)));
603 }
604
605 print_alerts(ses, "ses");
606
607 std::vector<download_priority_t> pieces(
608 std::size_t(torrents[5].torrent_file()->num_pieces()), 0_pri);
609 torrents[5].prioritize_pieces(pieces);
610 torrent_handle finished = torrents[5];
611
612 wait_for_alert(ses, torrent_finished_alert::alert_type, "ses");
613
614 // add_torrent should be ordered
615 TEST_EQUAL(finished.queue_position(), no_pos);
616 TEST_EQUAL(torrents[0].queue_position(), queue_position_t{0});
617 TEST_EQUAL(torrents[1].queue_position(), queue_position_t{1});
618 TEST_EQUAL(torrents[2].queue_position(), queue_position_t{2});
619 TEST_EQUAL(torrents[3].queue_position(), queue_position_t{3});
620 TEST_EQUAL(torrents[4].queue_position(), queue_position_t{4});
621
622 // test top and bottom
623 torrents[2].queue_position_top();
624 torrents[1].queue_position_bottom();
625
626 TEST_EQUAL(finished.queue_position(), no_pos);
627 TEST_EQUAL(torrents[2].queue_position(), queue_position_t{0});
628 TEST_EQUAL(torrents[0].queue_position(), queue_position_t{1});
629 TEST_EQUAL(torrents[3].queue_position(), queue_position_t{2});
630 TEST_EQUAL(torrents[4].queue_position(), queue_position_t{3});
631 TEST_EQUAL(torrents[1].queue_position(), queue_position_t{4});
632
633 // test set pos
634 torrents[0].queue_position_set(queue_position_t{0});
635 torrents[1].queue_position_set(queue_position_t{1});
636 // torrent 2 should be get moved down by 0 and 1 to pos 2
637
638 TEST_EQUAL(finished.queue_position(), no_pos);
639 TEST_EQUAL(torrents[0].queue_position(), queue_position_t{0});
640 TEST_EQUAL(torrents[1].queue_position(), queue_position_t{1});
641 TEST_EQUAL(torrents[2].queue_position(), queue_position_t{2});
642 TEST_EQUAL(torrents[3].queue_position(), queue_position_t{3});
643 TEST_EQUAL(torrents[4].queue_position(), queue_position_t{4});
644
645 //test strange up and down commands
646 torrents[0].queue_position_up();
647 torrents[4].queue_position_down();
648
649 TEST_EQUAL(finished.queue_position(), no_pos);
650 TEST_EQUAL(torrents[0].queue_position(), queue_position_t{0});
651 TEST_EQUAL(torrents[1].queue_position(), queue_position_t{1});
652 TEST_EQUAL(torrents[2].queue_position(), queue_position_t{2});
653 TEST_EQUAL(torrents[3].queue_position(), queue_position_t{3});
654 TEST_EQUAL(torrents[4].queue_position(), queue_position_t{4});
655
656 torrents[1].queue_position_up();
657 torrents[3].queue_position_down();
658 finished.queue_position_up();
659
660 TEST_EQUAL(finished.queue_position(), no_pos);
661 TEST_EQUAL(torrents[1].queue_position(), queue_position_t{0});
662 TEST_EQUAL(torrents[0].queue_position(), queue_position_t{1});
663 TEST_EQUAL(torrents[2].queue_position(), queue_position_t{2});
664 TEST_EQUAL(torrents[4].queue_position(), queue_position_t{3});
665 TEST_EQUAL(torrents[3].queue_position(), queue_position_t{4});
666
667 torrents[1].queue_position_down();
668 torrents[3].queue_position_up();
669 finished.queue_position_down();
670
671
672 TEST_EQUAL(finished.queue_position(), no_pos);
673 TEST_EQUAL(torrents[0].queue_position(), queue_position_t{0});
674 TEST_EQUAL(torrents[1].queue_position(), queue_position_t{1});
675 TEST_EQUAL(torrents[2].queue_position(), queue_position_t{2});
676 TEST_EQUAL(torrents[3].queue_position(), queue_position_t{3});
677 TEST_EQUAL(torrents[4].queue_position(), queue_position_t{4});
678
679 // test set pos on not existing pos
680 torrents[3].queue_position_set(queue_position_t{10});
681 finished.queue_position_set(queue_position_t{10});
682
683 TEST_EQUAL(finished.queue_position(), no_pos);
684 TEST_EQUAL(torrents[0].queue_position(), queue_position_t{0});
685 TEST_EQUAL(torrents[1].queue_position(), queue_position_t{1});
686 TEST_EQUAL(torrents[2].queue_position(), queue_position_t{2});
687 TEST_EQUAL(torrents[4].queue_position(), queue_position_t{3});
688 TEST_EQUAL(torrents[3].queue_position(), queue_position_t{4});
689 }
690
691 } // anonymous namespace
692
TORRENT_TEST(queue)693 TORRENT_TEST(queue)
694 {
695 test_queue(add_torrent_params());
696 }
697
TORRENT_TEST(queue_paused)698 TORRENT_TEST(queue_paused)
699 {
700 add_torrent_params p;
701 p.flags |= torrent_flags::paused;
702 p.flags &= ~torrent_flags::auto_managed;
703 test_queue(p);
704 }
705
TORRENT_TEST(test_move_storage_no_metadata)706 TORRENT_TEST(test_move_storage_no_metadata)
707 {
708 lt::session ses(settings());
709 add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:abababababababababababababababababababab");
710 p.save_path = "save_path";
711 torrent_handle h = ses.add_torrent(p);
712
713 TEST_EQUAL(h.status().save_path, complete("save_path"));
714
715 h.move_storage("save_path_1");
716
717 TEST_EQUAL(h.status().save_path, complete("save_path_1"));
718 }
719
TORRENT_TEST(test_have_piece_no_metadata)720 TORRENT_TEST(test_have_piece_no_metadata)
721 {
722 lt::session ses(settings());
723 add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:abababababababababababababababababababab");
724 p.save_path = "save_path";
725 torrent_handle h = ses.add_torrent(p);
726
727 TEST_EQUAL(h.have_piece(piece_index_t{-1}), false);
728 TEST_EQUAL(h.have_piece(piece_index_t{0}), false);
729 TEST_EQUAL(h.have_piece(piece_index_t{100}), false);
730 }
731
TORRENT_TEST(test_have_piece_out_of_range)732 TORRENT_TEST(test_have_piece_out_of_range)
733 {
734 lt::session ses(settings());
735
736 add_torrent_params p;
737 static std::array<const int, 2> const file_sizes{{100000, 100000}};
738 int const piece_size = 0x8000;
739 p.ti = make_torrent(file_sizes, piece_size);
740 p.save_path = "save_path";
741 p.flags |= torrent_flags::seed_mode;
742 torrent_handle h = ses.add_torrent(p);
743
744 TEST_EQUAL(h.have_piece(piece_index_t{-1}), false);
745 TEST_EQUAL(h.have_piece(piece_index_t{0}), true);
746 TEST_EQUAL(h.have_piece(piece_index_t{100}), false);
747 }
748
TORRENT_TEST(test_read_piece_no_metadata)749 TORRENT_TEST(test_read_piece_no_metadata)
750 {
751 lt::session ses(settings());
752 add_torrent_params p = parse_magnet_uri("magnet:?xt=urn:btih:abababababababababababababababababababab");
753 p.save_path = "save_path";
754 torrent_handle h = ses.add_torrent(p);
755
756 h.read_piece(piece_index_t{-1});
757
758 alert const* a = wait_for_alert(ses, read_piece_alert::alert_type, "read_piece_alert");
759 TEST_CHECK(a);
760 if (auto* rp = alert_cast<read_piece_alert>(a))
761 {
762 TEST_CHECK(rp->error == error_code(lt::errors::no_metadata, lt::libtorrent_category()));
763 }
764 }
765
TORRENT_TEST(test_read_piece_out_of_range)766 TORRENT_TEST(test_read_piece_out_of_range)
767 {
768 lt::session ses(settings());
769
770 add_torrent_params p;
771 static std::array<const int, 2> const file_sizes{{100000, 100000}};
772 int const piece_size = 0x8000;
773 p.ti = make_torrent(file_sizes, piece_size);
774 p.save_path = "save_path";
775 p.flags |= torrent_flags::seed_mode;
776 torrent_handle h = ses.add_torrent(p);
777
778 h.read_piece(piece_index_t{-1});
779
780 alert const* a = wait_for_alert(ses, read_piece_alert::alert_type, "read_piece_alert");
781 TEST_CHECK(a);
782 if (auto* rp = alert_cast<read_piece_alert>(a))
783 {
784 TEST_CHECK(rp->error == error_code(lt::errors::invalid_piece_index
785 , lt::libtorrent_category()));
786 }
787 }
788
789 namespace {
790 int const piece_size = 0x4000 * 128;
791
test_fs()792 file_storage test_fs()
793 {
794 file_storage fs;
795 fs.set_piece_length(piece_size);
796 fs.add_file("temp", 99999999999);
797 fs.set_num_pieces(int((fs.total_size() + piece_size - 1) / piece_size));
798 return fs;
799 }
800 }
801
TORRENT_TEST(test_calc_bytes_pieces)802 TORRENT_TEST(test_calc_bytes_pieces)
803 {
804 auto const fs = test_fs();
805 TEST_EQUAL(calc_bytes(fs, piece_count{2, 0, false}), 2 * piece_size);
806 }
807
TORRENT_TEST(test_calc_bytes_pieces_last)808 TORRENT_TEST(test_calc_bytes_pieces_last)
809 {
810 auto const fs = test_fs();
811 TEST_EQUAL(calc_bytes(fs, piece_count{2, 0, true}), piece_size + fs.total_size() % piece_size);
812 }
813
TORRENT_TEST(test_calc_bytes_no_pieces)814 TORRENT_TEST(test_calc_bytes_no_pieces)
815 {
816 auto const fs = test_fs();
817 TEST_EQUAL(calc_bytes(fs, piece_count{0, 0, false}), 0);
818 }
819
TORRENT_TEST(test_calc_bytes_all_pieces)820 TORRENT_TEST(test_calc_bytes_all_pieces)
821 {
822 auto const fs = test_fs();
823 TEST_EQUAL(calc_bytes(fs, piece_count{fs.num_pieces(), 0, true}), fs.total_size());
824 }
825
TORRENT_TEST(test_calc_bytes_all_pieces_one_pad)826 TORRENT_TEST(test_calc_bytes_all_pieces_one_pad)
827 {
828 auto const fs = test_fs();
829 TEST_EQUAL(calc_bytes(fs, piece_count{fs.num_pieces(), 1, true}), fs.total_size() - 0x4000);
830 }
831
TORRENT_TEST(test_calc_bytes_all_pieces_two_pad)832 TORRENT_TEST(test_calc_bytes_all_pieces_two_pad)
833 {
834 auto const fs = test_fs();
835 TEST_EQUAL(calc_bytes(fs, piece_count{fs.num_pieces(), 2, true}), fs.total_size() - 2 * 0x4000);
836 }
837
838 #if TORRENT_HAS_SYMLINK
TORRENT_TEST(symlinks_restore)839 TORRENT_TEST(symlinks_restore)
840 {
841 // downloading test torrent with symlinks
842 std::string const work_dir = current_working_directory();
843 lt::add_torrent_params p;
844 p.ti = std::make_shared<lt::torrent_info>(combine_path(
845 combine_path(parent_path(work_dir), "test_torrents"), "symlink2.torrent"));
846 p.flags &= ~lt::torrent_flags::paused;
847 p.save_path = work_dir;
848 settings_pack pack = settings();
849 pack.set_int(libtorrent::settings_pack::alert_mask, libtorrent::alert_category::status | libtorrent::alert_category::error);
850 lt::session ses(std::move(pack));
851 ses.add_torrent(p);
852
853 wait_for_alert(ses, torrent_checked_alert::alert_type, "torrent_checked_alert");
854
855 std::string const f = combine_path(combine_path(work_dir, "Some.framework"), "SDL2");
856 TEST_CHECK(aux::get_file_attributes(f) & file_storage::flag_symlink);
857 TEST_CHECK(aux::get_symlink_path(f) == "Versions/A/SDL2");
858 }
859 #endif
860
TORRENT_TEST(redundant_add_piece)861 TORRENT_TEST(redundant_add_piece)
862 {
863 file_storage fs;
864 fs.add_file("tmp1", 128 * 1024 * 8);
865 lt::create_torrent t(fs, 128 * 1024, 6);
866
867 TEST_CHECK(t.num_pieces() > 0);
868
869 std::vector<char> piece_data(std::size_t(fs.piece_length()), 0);
870 aux::random_bytes(piece_data);
871
872 sha1_hash const ph = lt::hasher(piece_data).final();
873 for (auto const i : fs.piece_range())
874 t.set_hash(i, ph);
875
876 std::vector<char> buf;
877 bencode(std::back_inserter(buf), t.generate());
878 auto ti = std::make_shared<torrent_info>(buf, from_span);
879
880 lt::session ses(settings());
881 lt::add_torrent_params atp;
882 atp.ti = ti;
883 atp.flags &= ~torrent_flags::paused;
884 atp.save_path = ".";
885 auto h = ses.add_torrent(atp);
886 wait_for_downloading(ses, "");
887
888 h.add_piece(piece_index_t{0}, piece_data.data());
889 h.set_piece_deadline(piece_index_t{0}, 0, torrent_handle::alert_when_available);
890 h.prioritize_pieces(std::vector<lt::download_priority_t>(std::size_t(ti->num_pieces()), lt::dont_download));
891 h.add_piece(piece_index_t{0}, piece_data.data());
892 std::this_thread::sleep_for(lt::seconds(2));
893 }
894
TORRENT_TEST(test_in_session)895 TORRENT_TEST(test_in_session)
896 {
897 lt::session ses(settings());
898 std::shared_ptr<torrent_info> ti = generate_torrent();
899 add_torrent_params p;
900 p.ti = ti;
901 p.save_path = ".";
902 torrent_handle h = ses.add_torrent(p);
903 TEST_CHECK(h.in_session());
904 ses.remove_torrent(h);
905 TEST_CHECK(!h.in_session());
906 }
907