parent
0a3cb77d23
commit
e72f95e0d9
|
@ -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());
|
||||
|
|
|
@ -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<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)
|
||||
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -33,6 +33,9 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <fstream>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <tuple>
|
||||
#include <functional>
|
||||
#include <random>
|
||||
|
||||
#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 <tuple>
|
||||
#include <functional>
|
||||
#include <boost/make_shared.hpp>
|
||||
|
||||
#include "test.hpp"
|
||||
|
@ -604,6 +605,64 @@ boost::shared_ptr<T> clone_ptr(boost::shared_ptr<T> const& ptr)
|
|||
unsigned char random_byte()
|
||||
{ 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)
|
||||
{
|
||||
error_code ec;
|
||||
|
|
|
@ -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<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
|
||||
, char const* name = "temporary", int piece_size = 16 * 1024, int num_pieces = 13
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -36,6 +36,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/torrent_info.hpp"
|
||||
#include "setup_transfer.hpp"
|
||||
#include "test.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
@ -46,514 +47,157 @@ using namespace libtorrent;
|
|||
namespace lt = libtorrent;
|
||||
using std::ignore;
|
||||
|
||||
namespace {
|
||||
|
||||
template <class T>
|
||||
boost::shared_ptr<T> clone_ptr(boost::shared_ptr<T> const& ptr)
|
||||
bool all_of(std::vector<bool> const& v)
|
||||
{
|
||||
return boost::shared_ptr<T>(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<peer_disconnected_alert>(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<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, ¶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<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, ¶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<char> buf;
|
||||
bencode(std::back_inserter(buf), ct.generate());
|
||||
boost::shared_ptr<torrent_info> 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<torrent_info> 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<char> 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<int> 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<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
|
||||
// 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<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));
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue