fix test_remap_files (#905)

fix test_remap_files
This commit is contained in:
Arvid Norberg 2016-07-15 14:35:57 -04:00 committed by GitHub
parent 0a3cb77d23
commit e72f95e0d9
7 changed files with 225 additions and 503 deletions

View File

@ -2843,11 +2843,11 @@ namespace libtorrent
, std::bind(&peer_connection::on_disk_write_complete , std::bind(&peer_connection::on_disk_write_complete
, self(), _1, p, t)); , 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); counters::queued_write_bytes, p.length);
m_outstanding_writing_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); settings_pack::max_queued_disk_bytes);
if (write_queue_size > max_queue_size if (write_queue_size > max_queue_size
&& write_queue_size - p.length < 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(p.start == j->d.io.offset);
TORRENT_ASSERT(picker.num_peers(block_finished) == 0); 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" // 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); // , peer_info_struct(), block_finished.piece_index, block_finished.block_index);
picker.mark_as_finished(block_finished, peer_info_struct()); picker.mark_as_finished(block_finished, peer_info_struct());

View File

@ -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) void torrent::add_piece(int piece, char const* data, int flags)
{ {
TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(is_single_thread());
@ -1333,14 +1335,28 @@ namespace libtorrent
return; return;
} }
inc_refcount("add_piece"); 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) m_ses.disk_thread().async_write(&storage(), p, std::move(buffer)
, std::bind(&torrent::on_disk_write_complete , std::bind(&torrent::on_disk_write_complete
, shared_from_this(), _1, p)); , shared_from_this(), _1, p));
piece_block block(piece, i); 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_downloading(block, nullptr);
picker().mark_as_writing(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); picker().dec_refcount(piece, nullptr);
} }
@ -1363,16 +1379,13 @@ namespace libtorrent
schedule_storage_tick(); 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" // std::fprintf(stderr, "torrent::on_disk_write_complete ret:%d piece:%d block:%d\n"
// , j->ret, j->piece, j->offset/0x4000); // , j->ret, j->piece, j->offset/0x4000);
INVARIANT_CHECK; INVARIANT_CHECK;
if (m_abort) return;
if (m_abort)
{
return;
}
piece_block block_finished(p.piece, p.start / block_size()); piece_block block_finished(p.piece, p.start / block_size());
if (j->ret == -1) if (j->ret == -1)
@ -1390,6 +1403,13 @@ namespace libtorrent
picker().mark_as_finished(block_finished, nullptr); picker().mark_as_finished(block_finished, nullptr);
maybe_done_flushing(); maybe_done_flushing();
if (alerts().should_post<block_finished_alert>())
{
alerts().emplace_alert<block_finished_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) 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 // verify piece is used when checking resume data or when the user
// adds a piece // adds a piece
void torrent::verify_piece(int piece) void torrent::verify_piece(int const piece)
{ {
// picker().mark_as_checking(piece); // picker().mark_as_checking(piece);

View File

@ -68,7 +68,7 @@ using namespace libtorrent;
// out, such as the log // out, such as the log
int old_stdout = -1; int old_stdout = -1;
int old_stderr = -1; int old_stderr = -1;
bool redirect_output = false; bool redirect_output = true;
bool keep_files = false; bool keep_files = false;
extern int _g_test_idx; extern int _g_test_idx;

View File

@ -33,6 +33,9 @@ POSSIBILITY OF SUCH DAMAGE.
#include <fstream> #include <fstream>
#include <deque> #include <deque>
#include <map> #include <map>
#include <tuple>
#include <functional>
#include <random>
#include "libtorrent/session.hpp" #include "libtorrent/session.hpp"
#include "libtorrent/hasher.hpp" #include "libtorrent/hasher.hpp"
@ -50,8 +53,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/broadcast_socket.hpp" // for supports_ipv6() #include "libtorrent/broadcast_socket.hpp" // for supports_ipv6()
#include "libtorrent/hex.hpp" // to_hex #include "libtorrent/hex.hpp" // to_hex
#include <tuple>
#include <functional>
#include <boost/make_shared.hpp> #include <boost/make_shared.hpp>
#include "test.hpp" #include "test.hpp"
@ -604,6 +605,64 @@ boost::shared_ptr<T> clone_ptr(boost::shared_ptr<T> const& ptr)
unsigned char random_byte() unsigned char random_byte()
{ return lt::random() & 0xff; } { return lt::random() & 0xff; }
std::vector<char> generate_piece(int const idx, int const piece_size)
{
using namespace libtorrent;
std::vector<char> ret(piece_size);
std::mt19937 rng(idx);
std::uniform_int_distribution<int> rand(-128, 127);
for (char& c : ret)
{
c = static_cast<char>(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<lt::torrent_info> 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<char> const piece = generate_piece(i, fs.piece_size(i));
ct.set_hash(i, hasher(piece.data(), int(piece.size())).final());
}
std::vector<char> buf;
bencode(std::back_inserter(buf), ct.generate());
error_code ec;
return boost::make_shared<torrent_info>(&buf[0], int(buf.size()), ec);
}
void create_random_files(std::string const& path, const int file_sizes[], int num_files) void create_random_files(std::string const& path, const int file_sizes[], int num_files)
{ {
error_code ec; error_code ec;

View File

@ -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_listen(libtorrent::session& ses, char const* name);
EXPORT void wait_for_downloading(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<char> 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<libtorrent::torrent_info> 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<libtorrent::torrent_info> create_torrent(std::ostream* file = 0 EXPORT boost::shared_ptr<libtorrent::torrent_info> create_torrent(std::ostream* file = 0
, char const* name = "temporary", int piece_size = 16 * 1024, int num_pieces = 13 , char const* name = "temporary", int piece_size = 16 * 1024, int num_pieces = 13

View File

@ -110,11 +110,11 @@ extern int EXPORT _g_test_failures;
#ifdef BOOST_NO_EXCEPTIONS #ifdef BOOST_NO_EXCEPTIONS
#define TEST_CHECK(x) \ #define TEST_CHECK(x) \
if (!(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) \ #define TEST_EQUAL(x, y) \
if ((x) != (y)) { \ if ((x) != (y)) { \
std::stringstream s__; \ 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__); \ TEST_REPORT_AUX(s__.str().c_str(), __FILE__, __LINE__); \
} }
#else #else
@ -122,37 +122,37 @@ extern int EXPORT _g_test_failures;
try \ try \
{ \ { \
if (!(x)) \ 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) \ 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 (...) \ catch (...) \
{ \ { \
TEST_ERROR("Exception thrown: " #x); \ TEST_ERROR("TEST_ERROR: Exception thrown: " #x); \
} }
#define TEST_EQUAL(x, y) \ #define TEST_EQUAL(x, y) \
try { \ try { \
if ((x) != (y)) { \ if ((x) != (y)) { \
std::stringstream s__; \ 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__); \ TEST_REPORT_AUX(s__.str().c_str(), __FILE__, __LINE__); \
} \ } \
} \ } \
catch (std::exception& e) \ 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 (...) \ catch (...) \
{ \ { \
TEST_ERROR("Exception thrown: " #x); \ TEST_ERROR("TEST_ERROR: Exception thrown: " #x); \
} }
#endif #endif
#define TEST_ERROR(x) \ #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) \ #define TEST_NOTHROW(x) \
try \ try \
@ -161,7 +161,7 @@ extern int EXPORT _g_test_failures;
} \ } \
catch (...) \ catch (...) \
{ \ { \
TEST_ERROR("Exception thrown: " #x); \ TEST_ERROR("TEST_ERROR: Exception thrown: " #x); \
} }
#endif // TEST_HPP #endif // TEST_HPP

View File

@ -36,6 +36,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/torrent_info.hpp" #include "libtorrent/torrent_info.hpp"
#include "setup_transfer.hpp" #include "setup_transfer.hpp"
#include "test.hpp" #include "test.hpp"
#include "settings.hpp"
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
@ -46,514 +47,157 @@ using namespace libtorrent;
namespace lt = libtorrent; namespace lt = libtorrent;
using std::ignore; using std::ignore;
namespace { bool all_of(std::vector<bool> const& v)
template <class T>
boost::shared_ptr<T> clone_ptr(boost::shared_ptr<T> const& ptr)
{ {
return boost::shared_ptr<T>(new T(*ptr)); return std::all_of(v.begin(), v.end(), [](bool v){ return v; });
} }
int peer_disconnects = 0; void test_remap_files(storage_mode_t storage_mode = storage_mode_sparse)
bool on_alert(alert const* a)
{ {
if (alert_cast<peer_disconnected_alert>(a)) using namespace libtorrent;
++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<char> buf;
bencode(std::back_inserter(buf), ct.generate());
boost::shared_ptr<torrent_info> t(new torrent_info(&buf[0], int(buf.size()), ec));
boost::shared_ptr<torrent_info> 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, &params
, 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;
// in case the previous run was terminated // in case the previous run was terminated
error_code ec; 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<torrent_info> 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<torrent_info> 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, &params
, 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 // create a torrent with 2 files, remap them into 3 files and make sure
// the file priorities don't break things // the file priorities don't break things
static const int file_sizes[] = {100000, 100000}; 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") static const int remap_file_sizes[] = {10000, 10000, int(t->total_size() - 20000)};
, file_sizes, num_files); int const num_new_files = 3;
file_storage fs1; file_storage fs = make_file_storage(remap_file_sizes, num_new_files
const int piece_size = 0x4000; , piece_size, "multifile-");
add_files(fs1, combine_path("tmp1_remap3", "test_torrent_dir")); t->remap_files(fs);
libtorrent::create_torrent ct(fs1, piece_size, 0x4000
, libtorrent::create_torrent::optimize_alignment);
// calculate the hash for all pieces int const alert_mask = alert::all_categories
set_piece_hashes(ct, "tmp1_remap3", ec); & ~alert::stats_notification;
if (ec) std::fprintf(stderr, "ERROR: set_piece_hashes: (%d) %s\n"
, ec.value(), ec.message().c_str());
std::vector<char> buf; session_proxy p1;
bencode(std::back_inserter(buf), ct.generate());
boost::shared_ptr<torrent_info> t(new torrent_info(&buf[0], int(buf.size()), ec));
int num_new_files = 3; settings_pack sett = settings();
sett.set_int(settings_pack::alert_mask, alert_mask);
file_storage fs; lt::session ses(sett);
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<torrent_info> t2 = clone_ptr(t);
t2->remap_files(fs);
add_torrent_params params; add_torrent_params params;
params.save_path = ".";
params.storage_mode = storage_mode; 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.flags &= ~add_torrent_params::flag_auto_managed;
params.ti = t;
wait_for_listen(ses1, "ses1"); torrent_handle tor1 = ses.add_torrent(params);
wait_for_listen(ses2, "ses2");
peer_disconnects = 0; // write pieces
for (int i = 0; i < fs.num_pieces(); ++i)
{
std::vector<char> piece = generate_piece(i, fs.piece_size(i));
tor1.add_piece(i, piece.data());
}
// test using piece sizes smaller than 16kB // read pieces
std::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, nullptr for (int i = 0; i < fs.num_pieces(); ++i)
, true, false, true, "_remap3", 8 * 1024, &t, false, &params {
, true, false, &t2); tor1.read_piece(i);
}
std::vector<int> file_prio(3, 1); // wait for all alerts to come back and verify the data against the expected
file_prio[0] = 0; // piece adata
tor2.prioritize_files(file_prio); std::vector<bool> pieces(fs.num_pieces(), false);
std::vector<bool> passed(fs.num_pieces(), false);
std::vector<bool> files(fs.num_files(), false);
// torrent1 will attempt to connect to torrent2 while (!all_of(pieces) || !all_of(passed) || !all_of(files))
// make sure torrent2 is up and running by then {
tor2.resume(); alert* a = ses.wait_for_alert(lt::seconds(5));
if (a == nullptr) break;
std::vector<alert*> alerts;
ses.pop_alerts(&alerts);
for (alert* i : alerts)
{
printf("%s\n", i->message().c_str());
read_piece_alert* rp = alert_cast<read_piece_alert>(i);
if (rp)
{
int const idx = rp->piece;
TEST_EQUAL(t->piece_size(idx), rp->size);
std::vector<char> 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<file_completed_alert>(i);
if (fc)
{
int const idx = fc->index;
TEST_CHECK(files[idx] == false);
files[idx] = true;
}
piece_finished_alert* pf = alert_cast<piece_finished_alert>(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)); std::this_thread::sleep_for(lt::milliseconds(500));
tor1.resume(); print_alerts(ses, "ses");
}
std::fprintf(stderr, "\ntesting remap scatter prio\n\n"); 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) for (int i = 0; i < 50; ++i)
{ {
print_alerts(ses1, "ses1", true, true, true, &on_alert); torrent_status st = tor1.status();
print_alerts(ses2, "ses2", true, true, true, &on_alert); if (st.is_seeding) break;
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;
std::this_thread::sleep_for(lt::milliseconds(100)); std::this_thread::sleep_for(lt::milliseconds(100));
print_alerts(ses, "ses");
} }
torrent_status st2 = tor2.status(); print_alerts(ses, "ses");
TEST_CHECK(st2.is_finished); st = tor1.status();
TEST_CHECK(st.is_seeding);
p1 = ses1.abort();
p2 = ses2.abort();
} }
using namespace libtorrent;
TORRENT_TEST(remap_files) TORRENT_TEST(remap_files)
{ {
test_remap_files_gather(); test_remap_files();
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);
} }