From e72f95e0d9e4fff583d1d466e23cf0c36346236c Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Fri, 15 Jul 2016 14:35:57 -0400 Subject: [PATCH] fix test_remap_files (#905) fix test_remap_files --- src/peer_connection.cpp | 11 +- src/torrent.cpp | 36 ++- test/main.cpp | 2 +- test/setup_transfer.cpp | 63 +++- test/setup_transfer.hpp | 8 +- test/test.hpp | 20 +- test/test_remap_files.cpp | 588 ++++++++------------------------------ 7 files changed, 225 insertions(+), 503 deletions(-) diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 66a85a7c2..841836c8a 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -2843,11 +2843,11 @@ namespace libtorrent , std::bind(&peer_connection::on_disk_write_complete , self(), _1, p, t)); - std::uint64_t write_queue_size = m_counters.inc_stats_counter( + std::uint64_t const write_queue_size = m_counters.inc_stats_counter( counters::queued_write_bytes, p.length); m_outstanding_writing_bytes += p.length; - std::uint64_t max_queue_size = m_settings.get_int( + std::uint64_t const max_queue_size = m_settings.get_int( settings_pack::max_queued_disk_bytes); if (write_queue_size > max_queue_size && write_queue_size - p.length < max_queue_size @@ -3033,13 +3033,6 @@ namespace libtorrent TORRENT_ASSERT(p.start == j->d.io.offset); TORRENT_ASSERT(picker.num_peers(block_finished) == 0); - if (j->ret == -1 - && j->error.ec == boost::system::errc::operation_canceled) - { - picker.mark_as_canceled(block_finished, peer_info_struct()); - TORRENT_ASSERT_FAIL(); // how do we get here? - return; - } // std::fprintf(stderr, "peer_connection mark_as_finished peer: %p piece: %d block: %d\n" // , peer_info_struct(), block_finished.piece_index, block_finished.block_index); picker.mark_as_finished(block_finished, peer_info_struct()); diff --git a/src/torrent.cpp b/src/torrent.cpp index 590e9ff16..b9d3356a0 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -1288,6 +1288,8 @@ namespace libtorrent } } + // TODO: 3 there's some duplication between this function and + // peer_connection::incoming_piece(). is there a way to merge something? void torrent::add_piece(int piece, char const* data, int flags) { TORRENT_ASSERT(is_single_thread()); @@ -1333,14 +1335,28 @@ namespace libtorrent return; } inc_refcount("add_piece"); + m_stats_counters.inc_stats_counter(counters::queued_write_bytes, p.length); m_ses.disk_thread().async_write(&storage(), p, std::move(buffer) , std::bind(&torrent::on_disk_write_complete , shared_from_this(), _1, p)); + piece_block block(piece, i); + bool const was_finished = picker().is_piece_finished(p.piece); + bool const multi = picker().num_peers(block) > 1; + picker().mark_as_downloading(block, nullptr); picker().mark_as_writing(block, nullptr); + + if (multi) cancel_block(block); + + // did we just finish the piece? + // this means all blocks are either written + // to disk or are in the disk write cache + if (picker().is_piece_finished(p.piece) && !was_finished) + { + verify_piece(p.piece); + } } - verify_piece(piece); picker().dec_refcount(piece, nullptr); } @@ -1363,16 +1379,13 @@ namespace libtorrent schedule_storage_tick(); + m_stats_counters.inc_stats_counter(counters::queued_write_bytes, -p.length); + // std::fprintf(stderr, "torrent::on_disk_write_complete ret:%d piece:%d block:%d\n" // , j->ret, j->piece, j->offset/0x4000); INVARIANT_CHECK; - - if (m_abort) - { - return; - } - + if (m_abort) return; piece_block block_finished(p.piece, p.start / block_size()); if (j->ret == -1) @@ -1390,6 +1403,13 @@ namespace libtorrent picker().mark_as_finished(block_finished, nullptr); maybe_done_flushing(); + + if (alerts().should_post()) + { + alerts().emplace_alert(get_handle(), + tcp::endpoint(), peer_id(), int(block_finished.block_index) + , int(block_finished.piece_index)); + } } void torrent::on_disk_tick_done(disk_io_job const* j) @@ -10552,7 +10572,7 @@ namespace libtorrent // verify piece is used when checking resume data or when the user // adds a piece - void torrent::verify_piece(int piece) + void torrent::verify_piece(int const piece) { // picker().mark_as_checking(piece); diff --git a/test/main.cpp b/test/main.cpp index 8996a91a1..f31308c4a 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -68,7 +68,7 @@ using namespace libtorrent; // out, such as the log int old_stdout = -1; int old_stderr = -1; -bool redirect_output = false; +bool redirect_output = true; bool keep_files = false; extern int _g_test_idx; diff --git a/test/setup_transfer.cpp b/test/setup_transfer.cpp index 38517f59c..1acfb2799 100644 --- a/test/setup_transfer.cpp +++ b/test/setup_transfer.cpp @@ -33,6 +33,9 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include +#include +#include #include "libtorrent/session.hpp" #include "libtorrent/hasher.hpp" @@ -50,8 +53,6 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/broadcast_socket.hpp" // for supports_ipv6() #include "libtorrent/hex.hpp" // to_hex -#include -#include #include #include "test.hpp" @@ -604,6 +605,64 @@ boost::shared_ptr clone_ptr(boost::shared_ptr const& ptr) unsigned char random_byte() { return lt::random() & 0xff; } +std::vector generate_piece(int const idx, int const piece_size) +{ + using namespace libtorrent; + std::vector ret(piece_size); + + std::mt19937 rng(idx); + std::uniform_int_distribution rand(-128, 127); + for (char& c : ret) + { + c = static_cast(rand(rng)); + } + return ret; +} + +lt::file_storage make_file_storage(const int file_sizes[], int num_files + , int const piece_size, std::string base_name) +{ + using namespace libtorrent; + file_storage fs; + for (int i = 0; i != num_files; ++i) + { + char filename[200]; + std::snprintf(filename, sizeof(filename), "test%d", i); + char dirname[200]; + std::snprintf(dirname, sizeof(dirname), "%s%d", base_name.c_str() + , i / 5); + std::string full_path = combine_path(dirname, filename); + + fs.add_file(full_path, file_sizes[i]); + } + + fs.set_piece_length(piece_size); + fs.set_num_pieces(int((fs.total_size() + piece_size - 1) / piece_size)); + + return fs; +} + +boost::shared_ptr make_torrent(const int file_sizes[] + , int const num_files, int const piece_size) +{ + using namespace libtorrent; + file_storage fs = make_file_storage(file_sizes, num_files, piece_size); + + libtorrent::create_torrent ct(fs, piece_size, 0x4000 + , libtorrent::create_torrent::optimize_alignment); + + for (int i = 0; i < fs.num_pieces(); ++i) + { + std::vector const piece = generate_piece(i, fs.piece_size(i)); + ct.set_hash(i, hasher(piece.data(), int(piece.size())).final()); + } + + std::vector buf; + bencode(std::back_inserter(buf), ct.generate()); + error_code ec; + return boost::make_shared(&buf[0], int(buf.size()), ec); +} + void create_random_files(std::string const& path, const int file_sizes[], int num_files) { error_code ec; diff --git a/test/setup_transfer.hpp b/test/setup_transfer.hpp index d29829817..8afd511f4 100644 --- a/test/setup_transfer.hpp +++ b/test/setup_transfer.hpp @@ -81,7 +81,13 @@ EXPORT bool print_alerts(libtorrent::session& ses, char const* name EXPORT void wait_for_listen(libtorrent::session& ses, char const* name); EXPORT void wait_for_downloading(libtorrent::session& ses, char const* name); -EXPORT void create_random_files(std::string const& path, const int file_sizes[], int num_files); +EXPORT std::vector generate_piece(int idx, int const piece_size = 0x4000); +EXPORT libtorrent::file_storage make_file_storage(const int file_sizes[], int num_files + , int const piece_size, std::string base_name = "test_dir-"); +EXPORT boost::shared_ptr make_torrent(const int file_sizes[] + , int num_files, int piece_size); +EXPORT void create_random_files(std::string const& path, const int file_sizes[] + , int num_files); EXPORT boost::shared_ptr create_torrent(std::ostream* file = 0 , char const* name = "temporary", int piece_size = 16 * 1024, int num_pieces = 13 diff --git a/test/test.hpp b/test/test.hpp index c2802a5f9..510d9ffb7 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -110,11 +110,11 @@ extern int EXPORT _g_test_failures; #ifdef BOOST_NO_EXCEPTIONS #define TEST_CHECK(x) \ if (!(x)) \ - TEST_REPORT_AUX("TEST_CHECK failed: \"" #x "\"", __FILE__, __LINE__); + TEST_REPORT_AUX("TEST_ERROR: check failed: \"" #x "\"", __FILE__, __LINE__); #define TEST_EQUAL(x, y) \ if ((x) != (y)) { \ std::stringstream s__; \ - s__ << "TEST_EQUAL_ERROR:\n" #x ": " << (x) << "\nexpected: " << (y); \ + s__ << "TEST_ERROR: equal check failed:\n" #x ": " << (x) << "\nexpected: " << (y); \ TEST_REPORT_AUX(s__.str().c_str(), __FILE__, __LINE__); \ } #else @@ -122,37 +122,37 @@ extern int EXPORT _g_test_failures; try \ { \ if (!(x)) \ - TEST_REPORT_AUX("TEST_CHECK failed: \"" #x "\"", __FILE__, __LINE__); \ + TEST_REPORT_AUX("TEST_ERROR: check failed: \"" #x "\"", __FILE__, __LINE__); \ } \ catch (std::exception& e) \ { \ - TEST_ERROR("Exception thrown: " #x " :" + std::string(e.what())); \ + TEST_ERROR("TEST_ERROR: Exception thrown: " #x " :" + std::string(e.what())); \ } \ catch (...) \ { \ - TEST_ERROR("Exception thrown: " #x); \ + TEST_ERROR("TEST_ERROR: Exception thrown: " #x); \ } #define TEST_EQUAL(x, y) \ try { \ if ((x) != (y)) { \ std::stringstream s__; \ - s__ << "TEST_EQUAL_ERROR: " #x ": " << (x) << " expected: " << (y); \ + s__ << "TEST_ERROR: " #x ": " << (x) << " expected: " << (y); \ TEST_REPORT_AUX(s__.str().c_str(), __FILE__, __LINE__); \ } \ } \ catch (std::exception& e) \ { \ - TEST_ERROR("Exception thrown: " #x " :" + std::string(e.what())); \ + TEST_ERROR("TEST_ERROR: Exception thrown: " #x " :" + std::string(e.what())); \ } \ catch (...) \ { \ - TEST_ERROR("Exception thrown: " #x); \ + TEST_ERROR("TEST_ERROR: Exception thrown: " #x); \ } #endif #define TEST_ERROR(x) \ - TEST_REPORT_AUX((std::string("ERROR: \"") + (x) + "\"").c_str(), __FILE__, __LINE__) + TEST_REPORT_AUX((std::string("TEST_ERROR: \"") + (x) + "\"").c_str(), __FILE__, __LINE__) #define TEST_NOTHROW(x) \ try \ @@ -161,7 +161,7 @@ extern int EXPORT _g_test_failures; } \ catch (...) \ { \ - TEST_ERROR("Exception thrown: " #x); \ + TEST_ERROR("TEST_ERROR: Exception thrown: " #x); \ } #endif // TEST_HPP diff --git a/test/test_remap_files.cpp b/test/test_remap_files.cpp index 9fa479104..fa971709a 100644 --- a/test/test_remap_files.cpp +++ b/test/test_remap_files.cpp @@ -36,6 +36,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/torrent_info.hpp" #include "setup_transfer.hpp" #include "test.hpp" +#include "settings.hpp" #include #include @@ -46,514 +47,157 @@ using namespace libtorrent; namespace lt = libtorrent; using std::ignore; -namespace { - -template -boost::shared_ptr clone_ptr(boost::shared_ptr const& ptr) +bool all_of(std::vector const& v) { - return boost::shared_ptr(new T(*ptr)); + return std::all_of(v.begin(), v.end(), [](bool v){ return v; }); } -int peer_disconnects = 0; - -bool on_alert(alert const* a) +void test_remap_files(storage_mode_t storage_mode = storage_mode_sparse) { - if (alert_cast(a)) - ++peer_disconnects; - - return false; -} - -} // anonymous namespace - -void test_remap_files_gather(storage_mode_t storage_mode = storage_mode_sparse) -{ - // in case the previous run was terminated - error_code ec; - - int const alert_mask = alert::all_categories - & ~alert::progress_notification - & ~alert::stats_notification; - - session_proxy p1; - session_proxy p2; - - settings_pack sett; - sett.set_bool(settings_pack::enable_upnp, false); - sett.set_bool(settings_pack::enable_natpmp, false); - sett.set_bool(settings_pack::enable_lsd, false); - sett.set_bool(settings_pack::enable_dht, false); - sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:48075"); - sett.set_int(settings_pack::alert_mask, alert_mask); - - lt::session ses1(sett); - - sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:49075"); - lt::session ses2(sett); - - torrent_handle tor1; - torrent_handle tor2; - - create_directory("tmp1_remap", ec); - create_directory(combine_path("tmp1_remap", "test_torrent_dir"), ec); - if (ec) - { - std::fprintf(stderr, "error creating directory: %s\n" - , ec.message().c_str()); - TEST_CHECK(false); - return; - } - - static const int file_sizes[] = - { 50, 16000-50, 16000, 1700, 100, 8000, 8000, 1,1,10,10,10,1000,10,10,10,10,1000,10,10,10,1,1,1 - ,10,1000,1000,1000,10,1000,130,65000,340,750,20,300,400,5000,23000,900,43000,4000,43000,60, 40}; - - create_random_files(combine_path("tmp1_remap", "test_torrent_dir") - , file_sizes, sizeof(file_sizes)/sizeof(file_sizes[0])); - file_storage fs; - - // generate a torrent with pad files to make sure they - // are not requested web seeds - add_files(fs, combine_path("tmp1_remap", "test_torrent_dir")); - libtorrent::create_torrent ct(fs, 0x8000, 0x4000); - set_piece_hashes(ct, "tmp1_remap", ec); - if (ec) - { - std::fprintf(stderr, "error creating hashes for test torrent: %s\n" - , ec.message().c_str()); - TEST_CHECK(false); - return; - } - std::vector buf; - bencode(std::back_inserter(buf), ct.generate()); - boost::shared_ptr t(new torrent_info(&buf[0], int(buf.size()), ec)); - boost::shared_ptr t2(new torrent_info(&buf[0], int(buf.size()), ec)); - - // remap the files to a single one - file_storage st; - st.add_file("single_file", t->total_size()); - t2->remap_files(st); - - add_torrent_params params; - params.storage_mode = storage_mode; - params.flags &= ~add_torrent_params::flag_paused; - params.flags &= ~add_torrent_params::flag_auto_managed; - - wait_for_listen(ses1, "ses1"); - wait_for_listen(ses2, "ses2"); - - peer_disconnects = 0; - - // test using piece sizes smaller than 16kB - std::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, nullptr - , true, false, true, "_remap", 8 * 1024, &t, false, ¶ms - , true, false, &t2); - - std::fprintf(stderr, "\ntesting remap gather\n\n"); - - for (int i = 0; i < 100; ++i) - { - print_alerts(ses1, "ses1", true, true, true, &on_alert); - print_alerts(ses2, "ses2", true, true, true, &on_alert); - - torrent_status st1 = tor1.status(); - torrent_status st2 = tor2.status(); - - if (i % 10 == 0) - { - print_ses_rate(i / 10.f, &st1, &st2); - } - - if (st2.is_finished) break; - - if (st2.state != torrent_status::downloading) - { - static char const* state_str[] = - {"checking (q)", "checking", "dl metadata" - , "downloading", "finished", "seeding", "allocating", "checking (r)"}; - std::cerr << "st2 state: " << state_str[st2.state] << std::endl; - } - - TEST_CHECK(st1.state == torrent_status::seeding - || st1.state == torrent_status::checking_files); - TEST_CHECK(st2.state == torrent_status::downloading - || st2.state == torrent_status::checking_resume_data); - - if (peer_disconnects >= 2) break; - - std::this_thread::sleep_for(lt::milliseconds(100)); - } - - torrent_status st2 = tor2.status(); - TEST_CHECK(st2.is_seeding); - - if (!st2.is_seeding) return; - - std::fprintf(stderr, "\ntesting force recheck\n\n"); - - // test force rechecking a seeding torrent with remapped files - tor2.force_recheck(); - - for (int i = 0; i < 50; ++i) - { - print_alerts(ses2, "ses2", true, true, true, &on_alert); - - torrent_status st2 = tor2.status(); - - if (i % 10 == 0) - { - print_ses_rate(i / 10.f, nullptr, &st2); - } - - if (st2.state != torrent_status::checking_files) - { - static char const* state_str[] = - {"checking (q)", "checking", "dl metadata" - , "downloading", "finished", "seeding", "allocating", "checking (r)"}; - std::cerr << "st2 state: " << state_str[st2.state] << std::endl; - } - - if (st2.progress == 1.0) break; - - std::this_thread::sleep_for(lt::milliseconds(100)); - } - - st2 = tor2.status(); - TEST_CHECK(st2.is_seeding); - - p1 = ses1.abort(); - p2 = ses2.abort(); -} - -void test_remap_files_scatter(storage_mode_t storage_mode = storage_mode_sparse) -{ - int num_files = 10; + using namespace libtorrent; // in case the previous run was terminated error_code ec; - int const alert_mask = alert::all_categories - & ~alert::progress_notification - & ~alert::stats_notification; - - session_proxy p1; - session_proxy p2; - - settings_pack sett; - sett.set_bool(settings_pack::enable_upnp, false); - sett.set_bool(settings_pack::enable_natpmp, false); - sett.set_bool(settings_pack::enable_lsd, false); - sett.set_bool(settings_pack::enable_dht, false); - sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:48075"); - sett.set_int(settings_pack::alert_mask, alert_mask); - - lt::session ses1(sett); - - sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:49075"); - sett.set_int(settings_pack::alert_mask, alert_mask); - lt::session ses2(sett); - - torrent_handle tor1; - torrent_handle tor2; - - create_directory("tmp1_remap2", ec); - std::ofstream file("tmp1_remap2/temporary"); - boost::shared_ptr t = ::create_torrent(&file, "temporary", 32 * 1024, 7); - file.close(); - - file_storage fs; - for (int i = 0; i < num_files-1; ++i) - { - char name[100]; - std::snprintf(name, sizeof(name), "multifile/file%d.txt", i); - fs.add_file(name, t->total_size() / 10); - } - char name[100]; - std::snprintf(name, sizeof(name), "multifile/file%d.txt", num_files); - // the last file has to be a special case to make the size - // add up exactly (in case the total size is not divisible by 10). - fs.add_file(name, t->total_size() - fs.total_size()); - - boost::shared_ptr t2 = clone_ptr(t); - - t2->remap_files(fs); - - add_torrent_params params; - params.storage_mode = storage_mode; - params.flags &= ~add_torrent_params::flag_paused; - params.flags &= ~add_torrent_params::flag_auto_managed; - - wait_for_listen(ses1, "ses1"); - wait_for_listen(ses2, "ses2"); - - peer_disconnects = 0; - - // test using piece sizes smaller than 16kB - std::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, nullptr - , true, false, true, "_remap2", 8 * 1024, &t, false, ¶ms - , true, false, &t2); - - std::fprintf(stderr, "\ntesting remap scatter\n\n"); - - for (int i = 0; i < 50; ++i) - { - print_alerts(ses1, "ses1", true, true, true, &on_alert); - print_alerts(ses2, "ses2", true, true, true, &on_alert); - - torrent_status st1 = tor1.status(); - torrent_status st2 = tor2.status(); - - if (i % 10 == 0) - { - print_ses_rate(i / 10.f, &st1, &st2); - } - - if (st2.is_finished) break; - - if (st2.state != torrent_status::downloading) - { - static char const* state_str[] = - {"checking (q)", "checking", "dl metadata" - , "downloading", "finished", "seeding", "allocating", "checking (r)"}; - std::cerr << "st2 state: " << state_str[st2.state] << std::endl; - } - - TEST_CHECK(st1.state == torrent_status::seeding - || st1.state == torrent_status::checking_files); - TEST_CHECK(st2.state == torrent_status::downloading - || st2.state == torrent_status::checking_resume_data); - - if (peer_disconnects >= 2) break; - - std::this_thread::sleep_for(lt::milliseconds(100)); - } - - torrent_status st2 = tor2.status(); - TEST_CHECK(st2.is_seeding); - - if (!st2.is_seeding) return; - - std::fprintf(stderr, "\ntesting force recheck\n\n"); - - // test force rechecking a seeding torrent with remapped files - tor2.force_recheck(); - - for (int i = 0; i < 50; ++i) - { - print_alerts(ses2, "ses2", true, true, true, &on_alert); - - torrent_status st2 = tor2.status(); - - if (i % 10 == 0) - { - print_ses_rate(i / 10.f, nullptr, &st2); - } - - if (st2.state != torrent_status::checking_files) - { - static char const* state_str[] = - {"checking (q)", "checking", "dl metadata" - , "downloading", "finished", "seeding", "allocating", "checking (r)"}; - std::cerr << "st2 state: " << state_str[st2.state] << std::endl; - } - - if (st2.progress == 1.0) break; - - std::this_thread::sleep_for(lt::milliseconds(100)); - } - - st2 = tor2.status(); - TEST_CHECK(st2.is_seeding); - - p1 = ses1.abort(); - p2 = ses2.abort(); -} - -void test_remap_files_prio(storage_mode_t storage_mode = storage_mode_sparse) -{ - // in case the previous run was terminated - error_code ec; - - int const alert_mask = alert::all_categories - & ~alert::progress_notification - & ~alert::stats_notification; - - session_proxy p1; - session_proxy p2; - - settings_pack sett; - sett.set_bool(settings_pack::enable_upnp, false); - sett.set_bool(settings_pack::enable_natpmp, false); - sett.set_bool(settings_pack::enable_lsd, false); - sett.set_bool(settings_pack::enable_dht, false); - sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:48075"); - sett.set_int(settings_pack::alert_mask, alert_mask); - lt::session ses1(sett); - - sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:49075"); - lt::session ses2(sett); - - torrent_handle tor1; - torrent_handle tor2; - - create_directory("tmp1_remap3", ec); - create_directory(combine_path("tmp1_remap3", "test_torrent_dir"), ec); - // create a torrent with 2 files, remap them into 3 files and make sure // the file priorities don't break things static const int file_sizes[] = {100000, 100000}; - const int num_files = sizeof(file_sizes)/sizeof(file_sizes[0]); + const int num_files = 2; + const int piece_size = 0x8000; + auto t = make_torrent(file_sizes, num_files, piece_size); - create_random_files(combine_path("tmp1_remap3", "test_torrent_dir") - , file_sizes, num_files); + static const int remap_file_sizes[] = {10000, 10000, int(t->total_size() - 20000)}; + int const num_new_files = 3; - file_storage fs1; - const int piece_size = 0x4000; + file_storage fs = make_file_storage(remap_file_sizes, num_new_files + , piece_size, "multifile-"); - add_files(fs1, combine_path("tmp1_remap3", "test_torrent_dir")); - libtorrent::create_torrent ct(fs1, piece_size, 0x4000 - , libtorrent::create_torrent::optimize_alignment); + t->remap_files(fs); - // calculate the hash for all pieces - set_piece_hashes(ct, "tmp1_remap3", ec); - if (ec) std::fprintf(stderr, "ERROR: set_piece_hashes: (%d) %s\n" - , ec.value(), ec.message().c_str()); + int const alert_mask = alert::all_categories + & ~alert::stats_notification; - std::vector buf; - bencode(std::back_inserter(buf), ct.generate()); - boost::shared_ptr t(new torrent_info(&buf[0], int(buf.size()), ec)); + session_proxy p1; - int num_new_files = 3; - - file_storage fs; - for (int i = 0; i < num_new_files-1; ++i) - { - char name[100]; - std::snprintf(name, sizeof(name), "multifile/file%d.txt", i); - fs.add_file(name, t->total_size() / 10); - } - char name[100]; - std::snprintf(name, sizeof(name), "multifile/file%d.txt", num_new_files); - // the last file has to be a special case to make the size - // add up exactly (in case the total size is not divisible by 10). - fs.add_file(name, t->total_size() - fs.total_size()); - - boost::shared_ptr t2 = clone_ptr(t); - - t2->remap_files(fs); + settings_pack sett = settings(); + sett.set_int(settings_pack::alert_mask, alert_mask); + lt::session ses(sett); add_torrent_params params; + params.save_path = "."; params.storage_mode = storage_mode; - params.flags |= add_torrent_params::flag_paused; + params.flags &= ~add_torrent_params::flag_paused; params.flags &= ~add_torrent_params::flag_auto_managed; + params.ti = t; - wait_for_listen(ses1, "ses1"); - wait_for_listen(ses2, "ses2"); + torrent_handle tor1 = ses.add_torrent(params); - peer_disconnects = 0; + // write pieces + for (int i = 0; i < fs.num_pieces(); ++i) + { + std::vector piece = generate_piece(i, fs.piece_size(i)); + tor1.add_piece(i, piece.data()); + } - // test using piece sizes smaller than 16kB - std::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, nullptr - , true, false, true, "_remap3", 8 * 1024, &t, false, ¶ms - , true, false, &t2); + // read pieces + for (int i = 0; i < fs.num_pieces(); ++i) + { + tor1.read_piece(i); + } - std::vector file_prio(3, 1); - file_prio[0] = 0; - tor2.prioritize_files(file_prio); + // wait for all alerts to come back and verify the data against the expected + // piece adata + std::vector pieces(fs.num_pieces(), false); + std::vector passed(fs.num_pieces(), false); + std::vector files(fs.num_files(), false); - // torrent1 will attempt to connect to torrent2 - // make sure torrent2 is up and running by then - tor2.resume(); - std::this_thread::sleep_for(lt::milliseconds(500)); - tor1.resume(); + while (!all_of(pieces) || !all_of(passed) || !all_of(files)) + { + alert* a = ses.wait_for_alert(lt::seconds(5)); + if (a == nullptr) break; - std::fprintf(stderr, "\ntesting remap scatter prio\n\n"); + std::vector alerts; + ses.pop_alerts(&alerts); + + for (alert* i : alerts) + { + printf("%s\n", i->message().c_str()); + + read_piece_alert* rp = alert_cast(i); + if (rp) + { + int const idx = rp->piece; + TEST_EQUAL(t->piece_size(idx), rp->size); + + std::vector const piece = generate_piece(idx, t->piece_size(idx)); + TEST_CHECK(memcmp(rp->buffer.get(), piece.data(), rp->size) == 0); + TEST_CHECK(pieces[idx] == false); + pieces[idx] = true; + } + + file_completed_alert* fc = alert_cast(i); + if (fc) + { + int const idx = fc->index; + TEST_CHECK(files[idx] == false); + files[idx] = true; + } + + piece_finished_alert* pf = alert_cast(i); + if (pf) + { + int const idx = pf->piece_index; + TEST_CHECK(passed[idx] == false); + passed[idx] = true; + } + } + } + + TEST_CHECK(all_of(pieces)); + TEST_CHECK(all_of(files)); + TEST_CHECK(all_of(passed)); + + // just because we can read them back throught libtorrent, doesn't mean the + // files have hit disk yet (because of the cache). Retry a few times to try + // to pick up the files + for (int i = 0; i < num_new_files; ++i) + { + std::string name = fs.file_path(i); + for (int j = 0; j < 10 && !exists(name); ++j) + { + std::this_thread::sleep_for(lt::milliseconds(500)); + print_alerts(ses, "ses"); + } + + fprintf(stderr, "%s\n", name.c_str()); + TEST_CHECK(exists(name)); + } + + print_alerts(ses, "ses"); + + torrent_status st = tor1.status(); + TEST_EQUAL(st.is_seeding, true); + + std::fprintf(stderr, "\ntesting force recheck\n\n"); + + // test force rechecking a seeding torrent with remapped files + tor1.force_recheck(); for (int i = 0; i < 50; ++i) { - print_alerts(ses1, "ses1", true, true, true, &on_alert); - print_alerts(ses2, "ses2", true, true, true, &on_alert); - - torrent_status st1 = tor1.status(); - torrent_status st2 = tor2.status(); - - if (i % 10 == 0) - { - std::cerr - << "\033[32m" << int(st1.download_payload_rate / 1000.f) << "kB/s " - << "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st1.progress * 100) << "% " - << st1.num_peers - << ": " - << "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s " - << "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s " - << "\033[0m" << int(st2.progress * 100) << "% " - << st2.num_peers - << std::endl; - } - - if (st2.is_finished) break; - - if (st2.state != torrent_status::downloading) - { - static char const* state_str[] = - {"checking (q)", "checking", "dl metadata" - , "downloading", "finished", "seeding", "allocating", "checking (r)"}; - std::cerr << "st2 state: " << state_str[st2.state] << std::endl; - } - - TEST_CHECK(st1.state == torrent_status::seeding); - TEST_CHECK(st2.state == torrent_status::downloading - || st2.state == torrent_status::checking_resume_data); - - if (peer_disconnects >= 2) break; - + torrent_status st = tor1.status(); + if (st.is_seeding) break; std::this_thread::sleep_for(lt::milliseconds(100)); + print_alerts(ses, "ses"); } - torrent_status st2 = tor2.status(); - TEST_CHECK(st2.is_finished); - - p1 = ses1.abort(); - p2 = ses2.abort(); + print_alerts(ses, "ses"); + st = tor1.status(); + TEST_CHECK(st.is_seeding); } -using namespace libtorrent; - TORRENT_TEST(remap_files) { - test_remap_files_gather(); - - error_code ec; - remove_all("tmp1_remap", ec); - remove_all("tmp2_remap", ec); - remove_all("tmp1_remap2", ec); - remove_all("tmp2_remap2", ec); -} - -TORRENT_TEST(scatter) -{ - test_remap_files_scatter(); - - error_code ec; - remove_all("tmp1_remap", ec); - remove_all("tmp2_remap", ec); - remove_all("tmp1_remap2", ec); - remove_all("tmp2_remap2", ec); - remove_all("tmp1_remap3", ec); - remove_all("tmp2_remap3", ec); -} - -TORRENT_TEST(prio) -{ - test_remap_files_prio(); - - error_code ec; - remove_all("tmp1_remap", ec); - remove_all("tmp2_remap", ec); - remove_all("tmp1_remap2", ec); - remove_all("tmp2_remap2", ec); - remove_all("tmp1_remap3", ec); - remove_all("tmp2_remap3", ec); + test_remap_files(); }