Merge pull request #313 from arvidn/setup-swarm-cleanup

clean up setup-swarm simulation utility to be more flexible
This commit is contained in:
Arvid Norberg 2015-12-12 14:07:26 -05:00
commit 550b0bb047
23 changed files with 1176 additions and 1327 deletions

View File

@ -243,7 +243,8 @@ namespace libtorrent
// set to true when this torrent has been paused but
// is waiting to finish all current download requests
// before actually closing all connections
// before actually closing all connections, When in graceful pause mode,
// m_allow_peers is also false.
bool m_graceful_pause_mode:1;
// state subscription. If set, a pointer to this torrent

View File

@ -12,7 +12,6 @@ project
<simulator>on
<library>/torrent//torrent/<export-extra>on
<library>/libtorrent_test//libtorrent_test
<source>swarm_suite.cpp
<source>setup_swarm.cpp
<source>setup_dht.cpp
<source>create_torrent.cpp

@ -1 +1 @@
Subproject commit a4b10668ea22e9328a2668baf8990b3685fe7578
Subproject commit 8417cbee7f1a813f032c5c1ed81e1f2881af7580

View File

@ -41,174 +41,361 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/ip_filter.hpp"
#include "libtorrent/alert_types.hpp"
#include <boost/bind.hpp>
#include <fstream>
#include "settings.hpp"
#include "setup_swarm.hpp"
#include "setup_transfer.hpp" // for create_torrent
namespace lt = libtorrent;
using namespace sim;
namespace {
struct swarm
{
swarm(int num_nodes, sim::simulation& sim, swarm_setup_provider& config)
: m_sim(sim)
, m_config(config)
, m_ios(m_sim, asio::ip::address_v4::from_string("0.0.0.0"))
, m_start_time(lt::clock_type::now())
, m_timer(m_ios)
, m_shutting_down(false)
, m_tick(0)
int transfer_rate(lt::address ip)
{
for (int i = 0; i < num_nodes; ++i)
{
// create a new io_service
char ep[30];
snprintf(ep, sizeof(ep), "50.0.%d.%d", (i + 1) >> 8, (i + 1) & 0xff);
m_io_service.push_back(boost::make_shared<sim::asio::io_service>(
boost::ref(m_sim), asio::ip::address_v4::from_string(ep)));
lt::settings_pack pack = m_config.add_session(i);
boost::shared_ptr<lt::session> ses =
boost::make_shared<lt::session>(pack
, boost::ref(*m_io_service.back()));
m_nodes.push_back(ses);
m_config.on_session_added(i, *ses);
// reserve a slot in here for when the torrent gets added (notified by
// an alert)
m_torrents.push_back(lt::torrent_handle());
lt::add_torrent_params params = m_config.add_torrent(i);
if (!params.save_path.empty())
ses->async_add_torrent(params);
ses->set_alert_notify([i,this] {
// this function is called inside libtorrent and we cannot perform work
// immediately in it. We have to notify the outside to pull all the alerts
m_io_service[i]->post(boost::bind(&swarm::on_alerts, this, i));
});
}
m_timer.expires_from_now(lt::seconds(1));
m_timer.async_wait(boost::bind(&swarm::on_tick, this, _1));
// in order to get a heterogeneous network, the last digit in the IP
// address determines the latency to that node as well as upload and
// download rates.
int last_digit;
if (ip.is_v4())
last_digit = ip.to_v4().to_bytes()[3];
else
last_digit = ip.to_v6().to_bytes()[15];
return (last_digit + 4) * 5;
}
void on_tick(lt::error_code const& ec)
{
if (ec || m_shutting_down) return;
++m_tick;
if (m_config.tick(m_tick))
{
terminate();
return;
}
m_timer.expires_from_now(lt::seconds(1));
m_timer.async_wait(boost::bind(&swarm::on_tick, this, _1));
}
void on_alerts(int session_index)
{
std::vector<lt::alert*> alerts;
lt::session* ses = m_nodes[session_index].get();
// when shutting down, we may have destructed the session
if (ses == NULL) return;
bool term = false;
ses->pop_alerts(&alerts);
for (lt::alert* a : alerts)
{
lt::time_duration d = a->timestamp() - m_start_time;
boost::uint32_t millis = lt::duration_cast<lt::milliseconds>(d).count();
printf("%4d.%03d: [%02d] %s\n", millis / 1000, millis % 1000,
session_index, a->message().c_str());
// if a torrent was added save the torrent handle
if (lt::add_torrent_alert* at = lt::alert_cast<lt::add_torrent_alert>(a))
{
lt::torrent_handle h = at->handle;
m_torrents[session_index] = h;
// let the config object have a chance to set up the torrent
m_config.on_torrent_added(session_index, h);
// now, connect this torrent to all the others in the swarm
for (int k = 0; k < session_index; ++k)
{
char ep[30];
snprintf(ep, sizeof(ep), "50.0.%d.%d", (k + 1) >> 8, (k + 1) & 0xff);
h.connect_peer(lt::tcp::endpoint(
lt::address_v4::from_string(ep), 6881));
}
}
if (m_config.on_alert(a, session_index, m_torrents
, *m_nodes[session_index]))
term = true;
}
if (term) terminate();
}
void run()
{
m_sim.run();
printf("simulation::run() returned\n");
}
void terminate()
{
printf("TERMINATING\n");
m_config.on_exit(m_torrents);
// terminate simulation
for (int i = 0; i < int(m_nodes.size()); ++i)
{
m_zombies.push_back(m_nodes[i]->abort());
m_nodes[i].reset();
}
m_shutting_down = true;
}
private:
sim::simulation& m_sim;
swarm_setup_provider& m_config;
asio::io_service m_ios;
lt::time_point m_start_time;
std::vector<boost::shared_ptr<lt::session> > m_nodes;
std::vector<boost::shared_ptr<sim::asio::io_service> > m_io_service;
std::vector<lt::torrent_handle> m_torrents;
std::vector<lt::session_proxy> m_zombies;
lt::deadline_timer m_timer;
bool m_shutting_down;
int m_tick;
};
} // anonymous namespace
void setup_swarm(int num_nodes, swarm_setup_provider& cfg)
typedef sim::chrono::high_resolution_clock::duration duration;
using sim::chrono::milliseconds;
sim::route dsl_config::incoming_route(asio::ip::address ip)
{
sim::default_config network_cfg;
int rate = transfer_rate(ip);
auto it = m_incoming.find(ip);
if (it != m_incoming.end()) return sim::route().append(it->second);
it = m_incoming.insert(it, std::make_pair(ip, std::make_shared<queue>(
std::ref(m_sim->get_io_service())
, rate * 1000
, lt::duration_cast<duration>(milliseconds(rate / 2))
, 200 * 1000, "DSL modem in")));
return sim::route().append(it->second);
}
sim::route dsl_config::outgoing_route(asio::ip::address ip)
{
int rate = transfer_rate(ip);
auto it = m_outgoing.find(ip);
if (it != m_outgoing.end()) return sim::route().append(it->second);
it = m_outgoing.insert(it, std::make_pair(ip, std::make_shared<queue>(
std::ref(m_sim->get_io_service()), rate * 1000
, lt::duration_cast<duration>(milliseconds(rate / 2)), 200 * 1000, "DSL modem out")));
return sim::route().append(it->second);
}
std::string save_path(int swarm_id, int idx)
{
char path[200];
snprintf(path, sizeof(path), "swarm-%04d-peer-%02d"
, swarm_id, idx);
return path;
}
lt::address addr(char const* str)
{
return lt::address::from_string(str);
}
void add_extra_peers(lt::session& ses)
{
auto handles = ses.get_torrents();
TEST_EQUAL(handles.size(), 1);
auto h = handles[0];
for (int i = 0; i < 30; ++i)
{
char ep[30];
snprintf(ep, sizeof(ep), "60.0.0.%d", i + 1);
h.connect_peer(lt::tcp::endpoint(addr(ep), 6881));
}
}
lt::torrent_status get_status(lt::session& ses)
{
auto handles = ses.get_torrents();
TEST_EQUAL(handles.size(), 1);
auto h = handles[0];
return h.status();
}
bool has_metadata(lt::session& ses)
{
auto handles = ses.get_torrents();
TEST_EQUAL(handles.size(), 1);
auto h = handles[0];
return h.status().has_metadata;
}
bool is_seed(lt::session& ses)
{
auto handles = ses.get_torrents();
TEST_EQUAL(handles.size(), 1);
auto h = handles[0];
return h.status().is_seeding;
}
int completed_pieces(lt::session& ses)
{
auto handles = ses.get_torrents();
TEST_EQUAL(handles.size(), 1);
auto h = handles[0];
return h.status().num_pieces;
}
void utp_only(lt::settings_pack& p)
{
using namespace libtorrent;
p.set_bool(settings_pack::enable_outgoing_tcp, false);
p.set_bool(settings_pack::enable_incoming_tcp, false);
p.set_bool(settings_pack::enable_outgoing_utp, true);
p.set_bool(settings_pack::enable_incoming_utp, true);
}
void enable_enc(lt::settings_pack& p)
{
using namespace libtorrent;
p.set_bool(settings_pack::prefer_rc4, true);
p.set_int(settings_pack::in_enc_policy, settings_pack::pe_forced);
p.set_int(settings_pack::out_enc_policy, settings_pack::pe_forced);
p.set_int(settings_pack::allowed_enc_level, settings_pack::pe_both);
}
void setup_swarm(int num_nodes
, swarm_test type
, std::function<void(lt::settings_pack&)> new_session
, std::function<void(lt::add_torrent_params&)> add_torrent
, std::function<void(lt::alert const*, lt::session&)> on_alert
, std::function<int(int, lt::session&)> terminate)
{
dsl_config network_cfg;
sim::simulation sim{network_cfg};
setup_swarm(num_nodes, sim, cfg);
setup_swarm(num_nodes, type, sim, new_session
, add_torrent, on_alert, terminate);
}
void setup_swarm(int num_nodes, sim::simulation& sim, swarm_setup_provider& cfg)
void setup_swarm(int num_nodes
, swarm_test type
, sim::simulation& sim
, std::function<void(lt::settings_pack&)> new_session
, std::function<void(lt::add_torrent_params&)> add_torrent
, std::function<void(lt::alert const*, lt::session&)> on_alert
, std::function<int(int, lt::session&)> terminate)
{
swarm s(num_nodes, sim, cfg);
s.run();
lt::settings_pack pack = settings();
lt::add_torrent_params p;
p.flags &= ~lt::add_torrent_params::flag_paused;
p.flags &= ~lt::add_torrent_params::flag_auto_managed;
setup_swarm(num_nodes, type, sim, pack, p, new_session
, add_torrent, on_alert, terminate);
}
void setup_swarm(int num_nodes
, swarm_test type
, sim::simulation& sim
, lt::settings_pack const& default_settings
, lt::add_torrent_params const& default_add_torrent
, std::function<void(lt::settings_pack&)> new_session
, std::function<void(lt::add_torrent_params&)> add_torrent
, std::function<void(lt::alert const*, lt::session&)> on_alert
, std::function<int(int, lt::session&)> terminate)
{
setup_swarm(num_nodes, type, sim
, default_settings
, default_add_torrent
, [](lt::session&) {}
, new_session
, add_torrent
, on_alert
, terminate);
}
void setup_swarm(int num_nodes
, swarm_test type
, sim::simulation& sim
, lt::settings_pack const& default_settings
, lt::add_torrent_params const& default_add_torrent
, std::function<void(lt::session&)> init_session
, std::function<void(lt::settings_pack&)> new_session
, std::function<void(lt::add_torrent_params&)> add_torrent
, std::function<void(lt::alert const*, lt::session&)> on_alert
, std::function<int(int, lt::session&)> terminate)
{
asio::io_service ios(sim, addr("0.0.0.0"));
lt::time_point start_time(lt::clock_type::now());
std::vector<boost::shared_ptr<lt::session> > nodes;
std::vector<boost::shared_ptr<sim::asio::io_service> > io_service;
std::vector<lt::session_proxy> zombies;
lt::deadline_timer timer(ios);
lt::error_code ec;
int swarm_id = test_counter();
std::string path = save_path(swarm_id, 0);
lt::create_directory(path, ec);
if (ec) fprintf(stderr, "failed to create directory: \"%s\": %s\n"
, path.c_str(), ec.message().c_str());
std::ofstream file(lt::combine_path(path, "temporary").c_str());
auto ti = ::create_torrent(&file, "temporary", 0x4000, 9, false);
file.close();
// session 0 is the one we're testing. The others provide the scaffolding
// it's either a downloader or a seed
for (int i = 0; i < num_nodes; ++i)
{
// create a new io_service
char ep[30];
snprintf(ep, sizeof(ep), "50.0.%d.%d", (i + 1) >> 8, (i + 1) & 0xff);
io_service.push_back(boost::make_shared<sim::asio::io_service>(
boost::ref(sim), addr(ep)));
lt::settings_pack pack = default_settings;
// make sure the sessions have different peer ids
lt::peer_id pid;
std::generate(&pid[0], &pid[0] + 20, &random_byte);
pack.set_str(lt::settings_pack::peer_fingerprint, pid.to_string());
if (i == 0) new_session(pack);
boost::shared_ptr<lt::session> ses =
boost::make_shared<lt::session>(pack
, boost::ref(*io_service.back()));
init_session(*ses);
nodes.push_back(ses);
if (i > 0)
{
// the other sessions should not talk to each other
lt::ip_filter filter;
filter.add_rule(addr("0.0.0.0"), addr("255.255.255.255"), lt::ip_filter::blocked);
filter.add_rule(addr("50.0.0.1"), addr("50.0.0.1"), 0);
ses->set_ip_filter(filter);
}
lt::add_torrent_params p = default_add_torrent;
if (type == swarm_test::download)
{
// in download tests, session 0 is a downloader and every other session
// is a seed. save path 0 is where the files are, so that's for seeds
p.save_path = save_path(swarm_id, i > 0 ? 0 : 1);
}
else
{
// in seed tests, session 0 is a seed and every other session
// a downloader. save path 0 is where the files are, so that's for seeds
p.save_path = save_path(swarm_id, i);
}
p.ti = ti;
if (i == 0) add_torrent(p);
ses->async_add_torrent(p);
ses->set_alert_notify([&, i]() {
// this function is called inside libtorrent and we cannot perform work
// immediately in it. We have to notify the outside to pull all the alerts
io_service[i]->post([&,i]()
{
lt::session* ses = nodes[i].get();
// when shutting down, we may have destructed the session
if (ses == nullptr) return;
std::vector<lt::alert*> alerts;
ses->pop_alerts(&alerts);
// to debug the sessions not under test, comment out the following
// line
if (i != 0) return;
for (lt::alert* a : alerts)
{
// only print alerts from the session under test
lt::time_duration d = a->timestamp() - start_time;
boost::uint32_t millis = lt::duration_cast<lt::milliseconds>(d).count();
printf("%4d.%03d: %s\n", millis / 1000, millis % 1000
, a->message().c_str());
// if a torrent was added save the torrent handle
if (lt::add_torrent_alert* at = lt::alert_cast<lt::add_torrent_alert>(a))
{
lt::torrent_handle h = at->handle;
// now, connect this torrent to all the others in the swarm
// start at 1 to avoid self-connects
for (int k = 1; k < num_nodes; ++k)
{
// TODO: the pattern of creating an address from a format
// string and an integer is common. It should probably be
// factored out into its own function
char ep[30];
snprintf(ep, sizeof(ep), "50.0.%d.%d", (k + 1) >> 8, (k + 1) & 0xff);
h.connect_peer(lt::tcp::endpoint(addr(ep), 6881));
}
}
on_alert(a, *ses);
}
});
});
}
int tick = 0;
std::function<void(lt::error_code const&)> on_tick
= [&](lt::error_code const& ec)
{
if (ec) return;
bool shut_down = terminate(tick, *nodes[0]);
if (type == swarm_test::upload)
{
shut_down |= std::all_of(nodes.begin() + 1, nodes.end()
, [](boost::shared_ptr<lt::session> const& s)
{ return is_seed(*s); });
if (tick > 70 * (num_nodes - 1) && !shut_down)
{
TEST_ERROR("seeding failed!");
}
}
if (shut_down)
{
printf("TERMINATING\n");
// terminate simulation
for (int i = 0; i < int(nodes.size()); ++i)
{
zombies.push_back(nodes[i]->abort());
nodes[i].reset();
}
return;
}
++tick;
timer.expires_from_now(lt::seconds(1));
timer.async_wait(on_tick);
};
timer.expires_from_now(lt::seconds(1));
timer.async_wait(on_tick);
sim.run();
}

View File

@ -30,54 +30,85 @@ POSSIBILITY OF SUCH DAMAGE.
*/
#include "libtorrent/io_service.hpp"
#include "libtorrent/settings_pack.hpp"
#include "libtorrent/add_torrent_params.hpp"
#include "libtorrent/torrent_handle.hpp"
#include "simulator/simulator.hpp"
#include "libtorrent/address.hpp"
#include <functional>
#ifndef TORRENT_SWARM_SETUP_PROVIDER_HPP_INCLUDED
#define TORRENT_SWARM_SETUP_PROVIDER_HPP_INCLUDED
#ifndef TORRENT_SETUP_SWARM_HPP_INCLUDED
#define TORRENT_SETUP_SWARM_HPP_INCLUDED
namespace libtorrent
{
class alert;
class session;
struct add_torrent_params;
struct settings_pack;
struct torrent_handle;
struct torrent_status;
}
struct swarm_setup_provider
namespace lt = libtorrent;
enum class swarm_test { download, upload };
void setup_swarm(int num_nodes
, swarm_test type
, std::function<void(lt::settings_pack&)> new_session
, std::function<void(lt::add_torrent_params&)> add_torrent
, std::function<void(lt::alert const*, lt::session&)> on_alert
, std::function<int(int, lt::session&)> terminate);
void setup_swarm(int num_nodes
, swarm_test type
, sim::simulation& sim
, std::function<void(lt::settings_pack&)> new_session
, std::function<void(lt::add_torrent_params&)> add_torrent
, std::function<void(lt::alert const*, lt::session&)> on_alert
, std::function<int(int, lt::session&)> terminate);
void setup_swarm(int num_nodes
, swarm_test type
, sim::simulation& sim
, lt::settings_pack const& default_settings
, lt::add_torrent_params const& default_add_torrent
, std::function<void(lt::settings_pack&)> new_session
, std::function<void(lt::add_torrent_params&)> add_torrent
, std::function<void(lt::alert const*, lt::session&)> on_alert
, std::function<int(int, lt::session&)> terminate);
void setup_swarm(int num_nodes
, swarm_test type
, sim::simulation& sim
, lt::settings_pack const& default_settings
, lt::add_torrent_params const& default_add_torrent
, std::function<void(lt::session&)> init_session
, std::function<void(lt::settings_pack&)> new_session
, std::function<void(lt::add_torrent_params&)> add_torrent
, std::function<void(lt::alert const*, lt::session&)> on_alert
, std::function<int(int, lt::session&)> terminate);
bool has_metadata(lt::session& ses);
bool is_seed(lt::session& ses);
int completed_pieces(lt::session& ses);
void add_extra_peers(lt::session& ses);
lt::torrent_status get_status(lt::session& ses);
std::string save_path(int swarm_id, int idx);
// construct an address from string
lt::address addr(char const* str);
// disable TCP and enable uTP
void utp_only(lt::settings_pack& pack);
// force encrypted connections
void enable_enc(lt::settings_pack& pack);
struct dsl_config : sim::default_config
{
// can be used to check expected end conditions
virtual void on_exit(std::vector<libtorrent::torrent_handle> const& torrents) {}
// called for every alert. if the simulation is done, return true
virtual bool on_alert(libtorrent::alert const* alert
, int session_idx
, std::vector<libtorrent::torrent_handle> const& handles
, libtorrent::session& ses) { return false; }
// called for every torrent that's added (and every session that's started).
// this is useful to give every session a unique save path and to make some
// sessions seeds and others downloaders
virtual libtorrent::add_torrent_params add_torrent(int idx) = 0;
// called for every torrent that's added once the torrent_handle comes back.
// can be used to set options on the torrent
virtual void on_torrent_added(int idx, libtorrent::torrent_handle h) {}
// called for every session that's created. Leaves an opportunity for the
// configuration object to add extensions etc.
virtual void on_session_added(int idx, libtorrent::session& ses) {}
// called for every session that's added
virtual libtorrent::settings_pack add_session(int idx) = 0;
// called once a second. if it returns true, the simulation is terminated
// by default, simulations end after 200 seconds
virtual bool tick(int t) { return t > 200; }
virtual sim::route incoming_route(lt::address ip) override;
virtual sim::route outgoing_route(lt::address ip) override;
};
void setup_swarm(int num_nodes, swarm_setup_provider& config);
void setup_swarm(int num_nodes, sim::simulation& sim, swarm_setup_provider& config);
#endif

View File

@ -1,142 +0,0 @@
/*
Copyright (c) 2015, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_SWARM_CONFIG_HPP_INCLUDED
#define TORRENT_SWARM_CONFIG_HPP_INCLUDED
#include "libtorrent/torrent_handle.hpp"
#include "libtorrent/add_torrent_params.hpp"
#include "libtorrent/time.hpp"
#include "libtorrent/torrent_status.hpp"
#include "setup_swarm.hpp"
#include "settings.hpp"
#include "setup_transfer.hpp" // for create_torrent
#include <string>
#include <fstream>
using namespace libtorrent;
namespace lt = libtorrent;
struct swarm_config : swarm_setup_provider
{
swarm_config()
: m_start_time(lt::clock_type::now())
{
m_swarm_id = test_counter();
error_code ec;
std::string path = save_path(0);
create_directory(path, ec);
if (ec) fprintf(stderr, "failed to create directory: \"%s\": %s\n"
, path.c_str(), ec.message().c_str());
std::ofstream file(combine_path(path, "temporary").c_str());
m_ti = ::create_torrent(&file, "temporary", 0x4000, 9, false);
file.close();
}
virtual void on_exit(std::vector<torrent_handle> const& torrents) override
{
TEST_CHECK(torrents.size() > 0);
for (int i = 0; i < int(torrents.size()); ++i)
{
torrent_status st = torrents[i].status();
TEST_CHECK(st.is_seeding);
TEST_CHECK(st.total_upload > 0 || st.total_download > 0);
}
}
// called for every alert. if the simulation is done, return true
virtual bool on_alert(libtorrent::alert const* alert
, int session_idx
, std::vector<libtorrent::torrent_handle> const& torrents
, libtorrent::session& ses) override
{
if (torrents.empty()) return false;
bool all_are_seeding = true;
for (int i = 0; i < int(torrents.size()); ++i)
{
if (torrents[i].status().is_seeding)
continue;
all_are_seeding = false;
break;
}
// if all torrents are seeds, terminate the simulation, we're done
return all_are_seeding;
}
// called for every torrent that's added (and every session that's started).
// this is useful to give every session a unique save path and to make some
// sessions seeds and others downloaders
virtual libtorrent::add_torrent_params add_torrent(int idx) override
{
add_torrent_params p;
p.flags &= ~add_torrent_params::flag_paused;
p.flags &= ~add_torrent_params::flag_auto_managed;
p.ti = m_ti;
p.save_path = save_path(idx);
return p;
}
virtual std::string save_path(int idx) const
{
char path[200];
snprintf(path, sizeof(path), "swarm-%04d-peer-%02d"
, m_swarm_id, idx);
return path;
}
// called for every session that's added
virtual libtorrent::settings_pack add_session(int idx) override
{
settings_pack pack = settings();
// make sure the sessions have different peer ids
lt::peer_id pid;
std::generate(&pid[0], &pid[0] + 20, &random_byte);
pack.set_str(lt::settings_pack::peer_fingerprint, pid.to_string());
return pack;
}
protected:
int m_swarm_id;
lt::time_point m_start_time;
boost::shared_ptr<libtorrent::torrent_info> m_ti;
};
#endif // TORRENT_SWARM_CONFIG_HPP_INCLUDED

View File

@ -1,238 +0,0 @@
/*
Copyright (c) 2015, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "libtorrent/session.hpp"
#include "libtorrent/settings_pack.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/random.hpp"
#include "libtorrent/time.hpp" // for clock_type
#include "test.hpp"
#include "setup_swarm.hpp"
#include "swarm_suite.hpp"
#include "swarm_config.hpp"
using namespace libtorrent;
namespace lt = libtorrent;
struct test_swarm_config : swarm_config
{
test_swarm_config(int flags)
: swarm_config()
, m_flags(flags)
, m_paused_once(false)
, m_resumed_once(false)
{}
virtual bool tick(int t) override
{
return (m_flags & early_shutdown) ? t >= 1 : t > 200;
}
virtual void on_exit(std::vector<torrent_handle> const& torrents) override
{
if ((m_flags & early_shutdown) == 0)
{
swarm_config::on_exit(torrents);
}
// if we stopped and started again, we loose some time and need a bit
// more slack for completion
if (m_flags & stop_start_seed)
{
TEST_CHECK(lt::clock_type::now() < m_start_time + lt::milliseconds(4700));
}
else if (m_flags & stop_start_download)
{
TEST_CHECK(lt::clock_type::now() < m_start_time + lt::milliseconds(2800));
}
else
{
TEST_CHECK(lt::clock_type::now() < m_start_time + lt::milliseconds(2100));
}
}
virtual bool on_alert(libtorrent::alert const* alert
, int session_idx
, std::vector<libtorrent::torrent_handle> const& torrents
, libtorrent::session& ses) override
{
if (((m_flags & stop_start_download)
|| (m_flags & stop_start_seed))
&& m_paused_once == false)
{
torrent_status st_seed = torrents[0].status();
torrent_status st_dl = torrents[1].status();
int flags = 0;
if (m_flags & graceful_pause)
flags = torrent_handle::graceful_pause;
if (m_flags & stop_start_download)
{
if (st_dl.total_wanted_done > st_dl.total_wanted / 2
&& st_dl.paused == false)
{
m_paused_once = true;
torrents[1].auto_managed(false);
torrents[1].pause(flags);
}
}
if (m_flags & stop_start_seed)
{
if (st_dl.total_wanted_done > st_dl.total_wanted / 2
&& st_seed.paused == false)
{
m_paused_once = true;
torrents[0].auto_managed(false);
torrents[0].pause(flags);
}
}
}
if (alert_cast<torrent_paused_alert>(alert))
{
TEST_EQUAL(m_resumed_once, false);
if (m_flags & stop_start_download)
{
torrents[1].resume();
m_resumed_once = true;
}
if (m_flags & stop_start_seed)
{
torrents[0].resume();
m_resumed_once = true;
}
}
return swarm_config::on_alert(alert, session_idx, torrents, ses);
}
virtual void on_torrent_added(int session_index, torrent_handle h) override
{
if (m_flags & add_extra_peers)
{
for (int i = 0; i < 30; ++i)
{
char ep[30];
snprintf(ep, sizeof(ep), "60.0.0.%d", i + 1);
h.connect_peer(lt::tcp::endpoint(
lt::address_v4::from_string(ep), 6881));
}
}
}
// called for every torrent that's added (and every session that's started).
// this is useful to give every session a unique save path and to make some
// sessions seeds and others downloaders
virtual libtorrent::add_torrent_params add_torrent(int idx) override
{
add_torrent_params p = swarm_config::add_torrent(idx);
// only the first session is set to seed mode
if (idx == 0)
{
if (m_flags & seed_mode) p.flags |= add_torrent_params::flag_seed_mode;
}
return p;
}
// called for every session that's added
virtual libtorrent::settings_pack add_session(int idx) override
{
settings_pack pack = swarm_config::add_session(idx);
pack.set_bool(settings_pack::strict_super_seeding, m_flags & strict_super_seeding);
if (m_flags & suggest_read_cache)
pack.set_int(settings_pack::suggest_mode, settings_pack::suggest_read_cache);
else
pack.set_int(settings_pack::suggest_mode, 0);
if (m_flags & explicit_cache)
{
pack.set_bool(settings_pack::explicit_read_cache, true);
pack.set_int(settings_pack::explicit_cache_interval, 5);
}
else
{
pack.set_bool(settings_pack::explicit_read_cache, false);
}
if (m_flags & utp_only)
{
pack.set_bool(settings_pack::enable_incoming_utp, true);
pack.set_bool(settings_pack::enable_outgoing_utp, true);
pack.set_bool(settings_pack::enable_incoming_tcp, false);
pack.set_bool(settings_pack::enable_outgoing_tcp, false);
}
else
{
pack.set_bool(settings_pack::enable_incoming_utp, false);
pack.set_bool(settings_pack::enable_outgoing_utp, false);
pack.set_bool(settings_pack::enable_incoming_tcp, true);
pack.set_bool(settings_pack::enable_outgoing_tcp, true);
}
pack.set_int(settings_pack::alert_mask, alert::all_categories);
return pack;
}
private:
int m_flags;
bool m_paused_once;
bool m_resumed_once;
};
void simulate_swarm(int flags)
{
fprintf(stderr, "\n\n ==== TEST SWARM === %s%s%s%s%s%s%s%s%s%s===\n\n\n"
, (flags & super_seeding) ? "super-seeding ": ""
, (flags & strict_super_seeding) ? "strict-super-seeding ": ""
, (flags & seed_mode) ? "seed-mode ": ""
, (flags & time_critical) ? "time-critical ": ""
, (flags & suggest_read_cache) ? "suggest-read-cache ": ""
, (flags & explicit_cache) ? "explicit-cache ": ""
, (flags & utp_only) ? "utp-only": ""
, (flags & stop_start_download) ? "stop-start-download ": ""
, (flags & stop_start_seed) ? "stop-start-seed ": ""
, (flags & stop_start_seed) ? "graceful-pause ": ""
);
test_swarm_config cfg(flags);
setup_swarm(2, cfg);
}

View File

@ -1,52 +0,0 @@
/*
Copyright (c) 2014, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.hpp"
enum test_flags_t
{
super_seeding = 1,
strict_super_seeding = 2,
seed_mode = 4,
time_critical = 8,
suggest_read_cache = 16,
explicit_cache = 32,
utp_only = 64,
stop_start_download = 128,
stop_start_seed = 256,
graceful_pause = 1024,
add_extra_peers = 2048,
early_shutdown = 4096
};
void EXPORT simulate_swarm(int flags = 0);

View File

@ -35,15 +35,16 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/settings_pack.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/deadline_timer.hpp"
#include "swarm_config.hpp"
#include "settings.hpp"
#include "create_torrent.hpp"
#include "simulator/simulator.hpp"
#include <iostream>
using namespace sim;
using namespace libtorrent;
const int num_torrents = 10;
namespace lt = libtorrent;
using sim::asio::ip::address_v4;
@ -96,11 +97,11 @@ void run_test(Settings const& sett, Setup const& setup, Test const& test)
TORRENT_TEST(dont_count_slow_torrents)
{
run_test(
[](settings_pack& sett) {
[](lt::settings_pack& sett) {
// session settings
sett.set_bool(settings_pack::dont_count_slow_torrents, true);
sett.set_int(settings_pack::active_downloads, 1);
sett.set_int(settings_pack::active_seeds, 1);
sett.set_bool(lt::settings_pack::dont_count_slow_torrents, true);
sett.set_int(lt::settings_pack::active_downloads, 1);
sett.set_int(lt::settings_pack::active_seeds, 1);
},
[](lt::session& ses) {
@ -108,8 +109,8 @@ TORRENT_TEST(dont_count_slow_torrents)
for (int i = 0; i < num_torrents; ++i)
{
lt::add_torrent_params params = create_torrent(i, false);
params.flags |= add_torrent_params::flag_auto_managed;
params.flags |= add_torrent_params::flag_paused;
params.flags |= lt::add_torrent_params::flag_auto_managed;
params.flags |= lt::add_torrent_params::flag_paused;
ses.async_add_torrent(params);
}
},

View File

@ -33,7 +33,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "test.hpp"
#include "settings.hpp"
#include "setup_swarm.hpp"
#include "swarm_config.hpp"
#include "simulator/simulator.hpp"
#include "simulator/http_server.hpp"
#include "simulator/socks_server.hpp"

View File

@ -32,218 +32,139 @@ POSSIBILITY OF SUCH DAMAGE.
#include "test.hpp"
#include "test_utils.hpp"
#include "swarm_config.hpp"
#include "settings.hpp"
#include "setup_swarm.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/thread.hpp"
#include "libtorrent/add_torrent_params.hpp"
#include "libtorrent/magnet_uri.hpp"
#include "libtorrent/extensions/metadata_transfer.hpp"
#include "libtorrent/extensions/ut_metadata.hpp"
#include <boost/tuple/tuple.hpp>
using namespace libtorrent;
namespace lt = libtorrent;
enum flags_t
{
clear_files = 1,
// disconnect immediately after receiving the metadata (to test that
// edge case, it caused a crash once)
disconnect = 2,
disconnect = 1,
// force encryption (to make sure the plugin uses the peer_connection
// API in a compatible way)
full_encryption = 4,
full_encryption = 2,
// have the downloader connect to the seeder
// (instead of the other way around)
reverse = 8,
reverse = 4,
// only use uTP
utp = 16,
utp = 8,
// upload-only mode
upload_only = 32
upload_only = 16
};
struct test_swarm_config : swarm_config
void run_metadata_test(int flags)
{
test_swarm_config(int flags
, boost::shared_ptr<torrent_plugin> (*plugin)(torrent_handle const&, void*))
: swarm_config()
, m_flags(flags)
, m_plugin(plugin)
, m_metadata_alerts(0)
{}
int metadata_alerts = 0;
// called for every session that's added
virtual libtorrent::settings_pack add_session(int idx) override
sim::default_config cfg;
sim::simulation sim{cfg};
lt::settings_pack default_settings = settings();
if (flags & full_encryption)
{
settings_pack s = swarm_config::add_session(idx);
fprintf(stderr, " session %d\n", idx);
fprintf(stderr, "\n==== test transfer: %s%s%s%s%s%s ====\n\n"
, (m_flags & clear_files) ? "clear-files " : ""
, (m_flags & disconnect) ? "disconnect " : ""
, (m_flags & full_encryption) ? "encryption " : ""
, (m_flags & reverse) ? "reverse " : ""
, (m_flags & utp) ? "utp " : ""
, (m_flags & upload_only) ? "upload_only " : "");
s.set_int(settings_pack::out_enc_policy, settings_pack::pe_forced);
s.set_int(settings_pack::in_enc_policy, settings_pack::pe_forced);
s.set_bool(settings_pack::prefer_rc4, m_flags & full_encryption);
if (m_flags & utp)
{
s.set_bool(settings_pack::enable_incoming_utp, true);
s.set_bool(settings_pack::enable_outgoing_utp, true);
s.set_bool(settings_pack::enable_incoming_tcp, false);
s.set_bool(settings_pack::enable_outgoing_tcp, false);
}
else
{
s.set_bool(settings_pack::enable_incoming_utp, false);
s.set_bool(settings_pack::enable_outgoing_utp, false);
s.set_bool(settings_pack::enable_incoming_tcp, true);
s.set_bool(settings_pack::enable_outgoing_tcp, true);
}
return s;
enable_enc(default_settings);
}
virtual libtorrent::add_torrent_params add_torrent(int idx) override
if (flags & utp)
{
add_torrent_params p = swarm_config::add_torrent(idx);
if (m_flags & reverse)
{
p.save_path = save_path(1 - idx);
}
if (idx == 1)
{
// this is the guy who should download the metadata.
p.info_hash = p.ti->info_hash();
p.ti.reset();
}
return p;
utp_only(default_settings);
}
void on_session_added(int idx, session& ses) override
lt::add_torrent_params default_add_torrent;
if (flags & upload_only)
{
ses.add_extension(m_plugin);
default_add_torrent.flags |= add_torrent_params::flag_upload_mode;
}
bool on_alert(libtorrent::alert const* alert
, int session_idx
, std::vector<libtorrent::torrent_handle> const& handles
, libtorrent::session& ses) override
{
if (alert_cast<metadata_received_alert>(alert))
{
m_metadata_alerts += 1;
setup_swarm(2, (flags & reverse) ? swarm_test::upload : swarm_test::download
// add session
, [](lt::settings_pack& pack) {}
// add torrent
, [](lt::add_torrent_params& params) {
// we want to add the torrent via magnet link
params.url = lt::make_magnet_uri(*params.ti);
params.ti.reset();
params.flags &= ~add_torrent_params::flag_upload_mode;
}
// on alert
, [&](lt::alert const* a, lt::session& ses) {
// make sure this function can be called on
// torrents without metadata
if ((m_flags & disconnect) == 0)
{
handles[session_idx].status();
if (alert_cast<metadata_received_alert>(a))
{
metadata_alerts += 1;
if (flags & disconnect)
{
ses.remove_torrent(ses.get_torrents()[0]);
}
}
}
if ((m_flags & disconnect)
&& session_idx == 1
&& alert_cast<metadata_received_alert>(alert))
// terminate
, [&](int ticks, lt::session& ses) -> bool
{
ses.remove_torrent(handles[session_idx]);
return true;
}
return false;
}
if (flags & reverse)
{
return true;
}
virtual void on_torrent_added(int idx, torrent_handle h) override
{
if (idx == 0) return;
if (ticks > 70)
{
TEST_ERROR("timeout");
return true;
}
if ((flags & upload_only) && has_metadata(ses))
{
// the other peer is in upload mode and should not have sent any
// actual payload to us
TEST_CHECK(!is_seed(ses));
return true;
}
if (m_flags & upload_only)
{
h.set_upload_mode(true);
}
}
if (is_seed(ses))
{
TEST_CHECK((flags & upload_only) == 0);
return true;
}
virtual void on_exit(std::vector<torrent_handle> const& torrents) override
{
TEST_EQUAL(m_metadata_alerts, 1);
// in this case we should have completed without downloading anything
// because the downloader had upload only set
if (m_flags & upload_only) return;
return false;
});
swarm_config::on_exit(torrents);
}
private:
int m_flags;
boost::shared_ptr<torrent_plugin> (*m_plugin)(torrent_handle const&, void*);
int m_metadata_alerts;
};
TEST_EQUAL(metadata_alerts, 1);
}
TORRENT_TEST(ut_metadata_encryption_reverse)
{
test_swarm_config cfg(full_encryption | reverse, &create_ut_metadata_plugin);
setup_swarm(2, cfg);
run_metadata_test(full_encryption | reverse);
}
TORRENT_TEST(ut_metadata_encryption_utp)
{
test_swarm_config cfg(full_encryption | utp, &create_ut_metadata_plugin);
setup_swarm(2, cfg);
run_metadata_test(full_encryption | utp);
}
TORRENT_TEST(ut_metadata_reverse)
{
test_swarm_config cfg(reverse, &create_ut_metadata_plugin);
setup_swarm(2, cfg);
run_metadata_test(reverse);
}
TORRENT_TEST(ut_metadata_upload_only)
{
test_swarm_config cfg(upload_only, &create_ut_metadata_plugin);
setup_swarm(2, cfg);
run_metadata_test(upload_only);
}
#ifndef TORRENT_NO_DEPRECATE
// TODO: add more flags combinations?
TORRENT_TEST(metadata_encryption_reverse)
{
test_swarm_config cfg(full_encryption | reverse, &create_metadata_plugin);
setup_swarm(2, cfg);
}
TORRENT_TEST(metadata_encryption_utp)
{
test_swarm_config cfg(full_encryption | utp, &create_metadata_plugin);
setup_swarm(2, cfg);
}
TORRENT_TEST(metadata_reverse)
{
test_swarm_config cfg(reverse, &create_metadata_plugin);
setup_swarm(2, cfg);
}
TORRENT_TEST(metadata_upload_only)
{
test_swarm_config cfg(upload_only, &create_metadata_plugin);
setup_swarm(2, cfg);
}
#endif

View File

@ -33,14 +33,13 @@ POSSIBILITY OF SUCH DAMAGE.
#include <algorithm>
#include <iostream>
#include "libtorrent/hasher.hpp"
#include "libtorrent/pe_crypto.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/random.hpp"
#include "setup_transfer.hpp"
#include "swarm_config.hpp"
#include "test.hpp"
#include "settings.hpp"
#include "setup_swarm.hpp"
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
@ -49,18 +48,14 @@ namespace lt = libtorrent;
char const* pe_policy(boost::uint8_t policy)
{
using namespace libtorrent;
if (policy == settings_pack::pe_disabled) return "disabled";
else if (policy == settings_pack::pe_enabled) return "enabled";
else if (policy == settings_pack::pe_forced) return "forced";
return "unknown";
}
void display_settings(libtorrent::settings_pack const& s)
void display_pe_settings(libtorrent::settings_pack const& s)
{
using namespace libtorrent;
fprintf(stderr, "out_enc_policy - %s\tin_enc_policy - %s\n"
, pe_policy(s.get_int(settings_pack::out_enc_policy))
, pe_policy(s.get_int(settings_pack::in_enc_policy)));
@ -72,108 +67,126 @@ void display_settings(libtorrent::settings_pack const& s)
, s.get_bool(settings_pack::prefer_rc4) ? "true": "false");
}
struct test_swarm_config : swarm_config
void test_transfer(int enc_policy, int level, bool prefer_rc4)
{
test_swarm_config(libtorrent::settings_pack::enc_policy policy
, libtorrent::settings_pack::enc_level level
, bool prefer_rc4)
: swarm_config()
, m_policy(policy)
, m_level(level)
, m_prefer_rc4(prefer_rc4)
{}
lt::settings_pack default_settings = settings();
default_settings.set_bool(settings_pack::prefer_rc4, prefer_rc4);
default_settings.set_int(settings_pack::in_enc_policy, enc_policy);
default_settings.set_int(settings_pack::out_enc_policy, enc_policy);
default_settings.set_int(settings_pack::allowed_enc_level, level);
display_pe_settings(default_settings);
// called for every session that's added
virtual libtorrent::settings_pack add_session(int idx) override
{
settings_pack s = swarm_config::add_session(idx);
sim::default_config cfg;
sim::simulation sim{cfg};
fprintf(stderr, " session %d\n", idx);
s.set_int(settings_pack::out_enc_policy, settings_pack::pe_enabled);
s.set_int(settings_pack::in_enc_policy, settings_pack::pe_enabled);
s.set_int(settings_pack::allowed_enc_level, settings_pack::pe_both);
if (idx == 1)
{
s.set_int(settings_pack::out_enc_policy, m_policy);
s.set_int(settings_pack::in_enc_policy, m_policy);
s.set_int(settings_pack::allowed_enc_level, m_level);
s.set_bool(settings_pack::prefer_rc4, m_prefer_rc4);
lt::add_torrent_params default_add_torrent;
default_add_torrent.flags &= ~lt::add_torrent_params::flag_paused;
default_add_torrent.flags &= ~lt::add_torrent_params::flag_auto_managed;
setup_swarm(2, swarm_test::download, sim, default_settings, default_add_torrent
// add session
, [](lt::settings_pack& pack) {
pack.set_int(settings_pack::out_enc_policy, settings_pack::pe_enabled);
pack.set_int(settings_pack::in_enc_policy, settings_pack::pe_enabled);
pack.set_int(settings_pack::allowed_enc_level, settings_pack::pe_both);
pack.set_bool(settings_pack::prefer_rc4, false);
}
display_settings(s);
return s;
}
private:
libtorrent::settings_pack::enc_policy m_policy;
libtorrent::settings_pack::enc_level m_level;
bool m_prefer_rc4;
};
// add torrent
, [](lt::add_torrent_params& params) {}
// on alert
, [](lt::alert const* a, lt::session& ses) {}
// terminate
, [](int ticks, lt::session& ses) -> bool
{
if (ticks > 20)
{
TEST_ERROR("timeout");
return true;
}
return is_seed(ses);
});
}
TORRENT_TEST(pe_disabled)
{
using namespace libtorrent;
test_swarm_config cfg(settings_pack::pe_disabled, settings_pack::pe_both, false);
setup_swarm(2, cfg);
test_transfer(settings_pack::pe_disabled, settings_pack::pe_plaintext, false);
}
TORRENT_TEST(forced_plaintext)
{
using namespace libtorrent;
test_swarm_config cfg(settings_pack::pe_forced, settings_pack::pe_both, false);
setup_swarm(2, cfg);
test_transfer(settings_pack::pe_forced, settings_pack::pe_plaintext, false);
}
TORRENT_TEST(forced_rc4)
{
using namespace libtorrent;
test_swarm_config cfg(settings_pack::pe_forced, settings_pack::pe_rc4, false);
setup_swarm(2, cfg);
test_transfer(settings_pack::pe_forced, settings_pack::pe_rc4, true);
}
TORRENT_TEST(forced_both)
{
using namespace libtorrent;
test_swarm_config cfg(settings_pack::pe_forced, settings_pack::pe_both, false);
setup_swarm(2, cfg);
test_transfer(settings_pack::pe_forced, settings_pack::pe_both, false);
}
TORRENT_TEST(forced_both_prefer_rc4)
{
using namespace libtorrent;
test_swarm_config cfg(settings_pack::pe_forced, settings_pack::pe_both, true);
setup_swarm(2, cfg);
test_transfer(settings_pack::pe_forced, settings_pack::pe_both, true);
}
TORRENT_TEST(enabled_plaintext)
{
using namespace libtorrent;
test_swarm_config cfg(settings_pack::pe_enabled, settings_pack::pe_plaintext, false);
setup_swarm(2, cfg);
test_transfer(settings_pack::pe_forced, settings_pack::pe_plaintext, false);
}
TORRENT_TEST(enabled_rc4)
{
using namespace libtorrent;
test_swarm_config cfg(settings_pack::pe_enabled, settings_pack::pe_rc4, false);
setup_swarm(2, cfg);
test_transfer(settings_pack::pe_enabled, settings_pack::pe_rc4, false);
}
TORRENT_TEST(enabled_both)
{
using namespace libtorrent;
test_swarm_config cfg(settings_pack::pe_enabled, settings_pack::pe_both, false);
setup_swarm(2, cfg);
test_transfer(settings_pack::pe_enabled, settings_pack::pe_both, false);
}
TORRENT_TEST(enabled_both_prefer_rc4)
{
using namespace libtorrent;
test_swarm_config cfg(settings_pack::pe_enabled, settings_pack::pe_both, true);
setup_swarm(2, cfg);
test_transfer(settings_pack::pe_enabled, settings_pack::pe_both, true);
}
// make sure that a peer with encryption disabled cannot talk to a peer with
// encryption forced
TORRENT_TEST(disabled_failing)
{
lt::settings_pack default_settings = settings();
default_settings.set_bool(settings_pack::prefer_rc4, false);
default_settings.set_int(settings_pack::in_enc_policy, settings_pack::pe_disabled);
default_settings.set_int(settings_pack::out_enc_policy, settings_pack::pe_disabled);
default_settings.set_int(settings_pack::allowed_enc_level, settings_pack::pe_both);
display_pe_settings(default_settings);
sim::default_config cfg;
sim::simulation sim{cfg};
lt::add_torrent_params default_add_torrent;
default_add_torrent.flags &= ~lt::add_torrent_params::flag_paused;
default_add_torrent.flags &= ~lt::add_torrent_params::flag_auto_managed;
setup_swarm(2, swarm_test::download, sim, default_settings, default_add_torrent
// add session
, [](lt::settings_pack& pack) {
pack.set_int(settings_pack::out_enc_policy, settings_pack::pe_forced);
pack.set_int(settings_pack::in_enc_policy, settings_pack::pe_forced);
pack.set_int(settings_pack::allowed_enc_level, settings_pack::pe_both);
pack.set_bool(settings_pack::prefer_rc4, true);
}
// add torrent
, [](lt::add_torrent_params& params) {}
// on alert
, [](lt::alert const* a, lt::session& ses) {}
// terminate
, [](int ticks, lt::session& ses) -> bool
{
// this download should never succeed
TEST_CHECK(!is_seed(ses));
return ticks > 120;
});
}
#else
TORRENT_TEST(disabled)

View File

@ -30,18 +30,44 @@ POSSIBILITY OF SUCH DAMAGE.
*/
#include "swarm_suite.hpp"
#include "setup_swarm.hpp"
#include "libtorrent/add_torrent_params.hpp"
#include "libtorrent/settings_pack.hpp"
#include "test.hpp"
TORRENT_TEST(plain)
using namespace libtorrent;
TORRENT_TEST(super_seeding)
{
// with super seeding
simulate_swarm(super_seeding);
setup_swarm(5, swarm_test::upload
// add session
, [](lt::settings_pack& pack) {}
// add torrent
, [](lt::add_torrent_params& params) {
params.flags |= add_torrent_params::flag_super_seeding;
}
// on alert
, [](lt::alert const* a, lt::session& ses) {}
// terminate
, [](int ticks, lt::session& ses) -> bool
{ return true; });
}
TORRENT_TEST(strict)
TORRENT_TEST(strict_super_seeding)
{
// with strict super seeding
simulate_swarm(super_seeding | strict_super_seeding);
setup_swarm(5, swarm_test::upload
// add session
, [](lt::settings_pack& pack) {
pack.set_bool(settings_pack::strict_super_seeding, true);
}
// add torrent
, [](lt::add_torrent_params& params) {
params.flags |= add_torrent_params::flag_super_seeding;
}
// on alert
, [](lt::alert const* a, lt::session& ses) {}
// terminate
, [](int ticks, lt::session& ses) -> bool
{ return true; });
}

View File

@ -30,68 +30,289 @@ POSSIBILITY OF SUCH DAMAGE.
*/
#include "swarm_suite.hpp"
#include "setup_swarm.hpp"
#include "test.hpp"
#include "libtorrent/alert.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/session.hpp"
using namespace libtorrent;
TORRENT_TEST(seed_mode)
{
// with seed mode
simulate_swarm(seed_mode);
setup_swarm(2, swarm_test::upload
// add session
, [](lt::settings_pack& pack) {}
// add torrent
, [](lt::add_torrent_params& params) {
params.flags |= add_torrent_params::flag_seed_mode;
}
// on alert
, [](lt::alert const* a, lt::session& ses) {}
// terminate
, [](int ticks, lt::session& ses) -> bool
{ return true; });
}
TORRENT_TEST(plain)
{
simulate_swarm();
setup_swarm(2, swarm_test::download
// add session
, [](lt::settings_pack& pack) {}
// add torrent
, [](lt::add_torrent_params& params) {}
// on alert
, [](lt::alert const* a, lt::session& ses) {}
// terminate
, [](int ticks, lt::session& ses) -> bool
{
if (ticks > 75)
{
TEST_ERROR("timeout");
return true;
}
if (!is_seed(ses)) return false;
printf("completed in %d ticks\n", ticks);
return true;
});
}
TORRENT_TEST(suggest)
{
// with suggest pieces
simulate_swarm(suggest_read_cache);
setup_swarm(2, swarm_test::download
// add session
, [](lt::settings_pack& pack) {
pack.set_int(settings_pack::suggest_mode, settings_pack::suggest_read_cache);
}
// add torrent
, [](lt::add_torrent_params& params) {}
// on alert
, [](lt::alert const* a, lt::session& ses) {}
// terminate
, [](int ticks, lt::session& ses) -> bool
{
if (ticks > 75)
{
TEST_ERROR("timeout");
return true;
}
if (!is_seed(ses)) return false;
printf("completed in %d ticks\n", ticks);
return true;
});
}
TORRENT_TEST(utp)
TORRENT_TEST(utp_only)
{
simulate_swarm(utp_only);
setup_swarm(2, swarm_test::download
// add session
, [](lt::settings_pack& pack) {
pack.set_bool(settings_pack::enable_incoming_utp, true);
pack.set_bool(settings_pack::enable_outgoing_utp, true);
pack.set_bool(settings_pack::enable_incoming_tcp, false);
pack.set_bool(settings_pack::enable_outgoing_tcp, false);
}
// add torrent
, [](lt::add_torrent_params& params) {}
// on alert
, [](lt::alert const* a, lt::session& ses) {}
// terminate
, [](int ticks, lt::session& ses) -> bool
{
if (ticks > 75)
{
TEST_ERROR("timeout");
return true;
}
if (!is_seed(ses)) return false;
return true;
});
}
void test_stop_start_download(swarm_test type, bool graceful)
{
bool paused_once = false;
bool resumed = false;
setup_swarm(3, type
// add session
, [](lt::settings_pack& pack) {
// this test will pause and resume the torrent immediately, we expect
// to reconnect immediately too, so disable the min reconnect time
// limit.
pack.set_int(settings_pack::min_reconnect_time, 0);
}
// add torrent
, [](lt::add_torrent_params& params) {
}
// on alert
, [&](lt::alert const* a, lt::session& ses) {
if (lt::alert_cast<lt::torrent_added_alert>(a))
add_extra_peers(ses);
if (auto tp = lt::alert_cast<lt::torrent_paused_alert>(a))
{
TEST_EQUAL(resumed, false);
printf("\nSTART\n\n");
tp->handle.resume();
resumed = true;
}
}
// terminate
, [&](int ticks, lt::session& ses) -> bool
{
if (paused_once == false)
{
auto st = get_status(ses);
const bool limit_reached = (type == swarm_test::download)
? st.total_wanted_done > st.total_wanted / 2
: st.total_payload_upload >= 3 * 16 * 1024;
if (limit_reached)
{
printf("\nSTOP\n\n");
auto h = ses.get_torrents()[0];
h.pause(graceful ? torrent_handle::graceful_pause : 0);
paused_once = true;
}
}
printf("tick: %d\n", ticks);
const int timeout = type == swarm_test::download ? 20 : 91;
if (ticks > timeout)
{
TEST_ERROR("timeout");
return true;
}
if (type == swarm_test::upload) return false;
if (!is_seed(ses)) return false;
printf("completed in %d ticks\n", ticks);
return true;
});
TEST_EQUAL(paused_once, true);
TEST_EQUAL(resumed, true);
}
TORRENT_TEST(stop_start_download)
{
simulate_swarm(stop_start_download | add_extra_peers);
test_stop_start_download(swarm_test::download, false);
}
TORRENT_TEST(stop_start_download_graceful)
{
simulate_swarm(stop_start_download | graceful_pause | add_extra_peers);
test_stop_start_download(swarm_test::download, true);
}
TORRENT_TEST(stop_start_download_graceful_no_peers)
{
bool paused_once = false;
bool resumed = false;
setup_swarm(1, swarm_test::download
// add session
, [](lt::settings_pack& pack) {}
// add torrent
, [](lt::add_torrent_params& params) {}
// on alert
, [&](lt::alert const* a, lt::session& ses) {
if (auto tp = lt::alert_cast<lt::torrent_paused_alert>(a))
{
TEST_EQUAL(resumed, false);
printf("\nSTART\n\n");
tp->handle.resume();
resumed = true;
}
}
// terminate
, [&](int ticks, lt::session& ses) -> bool
{
if (paused_once == false
&& ticks == 6)
{
printf("\nSTOP\n\n");
auto h = ses.get_torrents()[0];
h.pause(torrent_handle::graceful_pause);
paused_once = true;
}
printf("tick: %d\n", ticks);
// when there's only one node (i.e. no peers) we won't ever download
// the torrent. It's just a test to make sure we still get the
// torrent_paused_alert
return ticks > 60;
});
TEST_EQUAL(paused_once, true);
TEST_EQUAL(resumed, true);
}
TORRENT_TEST(stop_start_seed)
{
simulate_swarm(stop_start_seed | add_extra_peers);
test_stop_start_download(swarm_test::upload, false);
}
TORRENT_TEST(stop_start_seed_graceful)
{
simulate_swarm(stop_start_seed | graceful_pause | add_extra_peers);
test_stop_start_download(swarm_test::upload, true);
}
TORRENT_TEST(explicit_cache)
{
// test explicit cache
simulate_swarm(suggest_read_cache | explicit_cache);
setup_swarm(2, swarm_test::download
// add session
, [](lt::settings_pack& pack) {
pack.set_int(settings_pack::suggest_mode, settings_pack::suggest_read_cache);
pack.set_bool(settings_pack::explicit_read_cache, true);
pack.set_int(settings_pack::explicit_cache_interval, 5);
}
// add torrent
, [](lt::add_torrent_params& params) {}
// on alert
, [](lt::alert const* a, lt::session& ses) {}
// terminate
, [](int ticks, lt::session& ses) -> bool
{
if (ticks > 75)
{
TEST_ERROR("timeout");
return true;
}
if (!is_seed(ses)) return false;
return true;
});
}
TORRENT_TEST(shutdown)
{
simulate_swarm(early_shutdown);
setup_swarm(2, swarm_test::download
// add session
, [](lt::settings_pack& pack) {}
// add torrent
, [](lt::add_torrent_params& params) {}
// on alert
, [](lt::alert const* a, lt::session& ses) {}
// terminate
, [](int ticks, lt::session& ses) -> bool
{
if (completed_pieces(ses) == 0) return false;
TEST_EQUAL(is_seed(ses), false);
return true;
});
}
// TODO: the swarm_suite is probably not a very good abstraction, it's not
// configurable enough.
// TODO: add test that makes sure a torrent in graceful pause mode won't make
// outgoing connections
// TODO: add test that makes sure a torrent in graceful pause mode won't accept
// incoming connections
// TODO: add test that makes sure a torrent in graceful pause mode only posts
// the torrent_paused_alert once, and exactly once
// TODO: test allow-fast
// TODO: test the different storage allocation modes
// TODO: test contiguous buffers

View File

@ -31,7 +31,7 @@ POSSIBILITY OF SUCH DAMAGE.
*/
#include "test.hpp"
#include "swarm_config.hpp"
#include "setup_swarm.hpp"
#include "simulator/simulator.hpp"
#include "libtorrent/alert_types.hpp"
@ -40,73 +40,61 @@ using namespace sim;
namespace lt = libtorrent;
// this is a test for torrent_status time counters are correct
struct test_swarm_config : swarm_config
{
test_swarm_config()
: swarm_config()
{}
bool on_alert(libtorrent::alert const* alert
, int session_idx
, std::vector<libtorrent::torrent_handle> const& handles
, libtorrent::session& ses) override
{
if (torrent_added_alert const* ta = alert_cast<torrent_added_alert>(alert))
{
TEST_CHECK(!m_handle.is_valid());
m_start_time = lt::clock_type::now();
m_handle = ta->handle;
}
return false;
}
virtual void on_exit(std::vector<torrent_handle> const& torrents) override {}
virtual bool tick(int t) override
{
// once an hour, verify that the timers seem correct
if ((t % 3600) == 0)
{
lt::time_point now = lt::clock_type::now();
int since_start = total_seconds(now - m_start_time) - 1;
torrent_status st = m_handle.status();
TEST_EQUAL(st.active_time, since_start);
TEST_EQUAL(st.seeding_time, since_start);
TEST_EQUAL(st.finished_time, since_start);
TEST_EQUAL(st.last_scrape, -1);
TEST_EQUAL(st.time_since_upload, -1);
// checking the torrent counts as downloading
// eventually though, we've forgotten about it and go back to -1
if (since_start > 65000)
{
TEST_EQUAL(st.time_since_download, -1);
}
else
{
TEST_EQUAL(st.time_since_download, since_start);
}
}
// simulate 20 hours of uptime. Currently, the session_time and internal
// peer timestamps are 16 bits counting seconds, so they can only
// represent about 18 hours. The clock steps forward in 4 hour increments
// to stay within that range
return t > 20 * 60 * 60;
}
private:
lt::time_point m_start_time;
lt::torrent_handle m_handle;
};
TORRENT_TEST(status_timers)
{
sim::default_config network_cfg;
sim::simulation sim{network_cfg};
lt::time_point start_time;
lt::torrent_handle handle;
test_swarm_config cfg;
setup_swarm(1, sim, cfg);
setup_swarm(1, swarm_test::upload
// add session
, [](lt::settings_pack& pack) {}
// add torrent
, [](lt::add_torrent_params& params) {}
// on alert
, [&](lt::alert const* a, lt::session& ses) {
if (auto ta = alert_cast<torrent_added_alert>(a))
{
TEST_CHECK(!handle.is_valid());
start_time = lt::clock_type::now();
handle = ta->handle;
}
}
// terminate
, [&](int ticks, lt::session& ses) -> bool
{
// simulate 20 hours of uptime. Currently, the session_time and internal
// peer timestamps are 16 bits counting seconds, so they can only
// represent about 18 hours. The clock steps forward in 4 hour increments
// to stay within that range
if (ticks > 20 * 60 * 60)
return true;
// once an hour, verify that the timers seem correct
if ((ticks % 3600) == 0)
{
lt::time_point now = lt::clock_type::now();
int since_start = total_seconds(now - start_time) - 1;
torrent_status st = handle.status();
TEST_EQUAL(st.active_time, since_start);
TEST_EQUAL(st.seeding_time, since_start);
TEST_EQUAL(st.finished_time, since_start);
TEST_EQUAL(st.last_scrape, -1);
TEST_EQUAL(st.time_since_upload, -1);
// checking the torrent counts as downloading
// eventually though, we've forgotten about it and go back to -1
if (since_start > 65000)
{
TEST_EQUAL(st.time_since_download, -1);
}
else
{
TEST_EQUAL(st.time_since_download, since_start);
}
}
return false;
});
}

View File

@ -33,11 +33,11 @@ POSSIBILITY OF SUCH DAMAGE.
#include "test.hpp"
#include "settings.hpp"
#include "setup_swarm.hpp"
#include "swarm_config.hpp"
#include "simulator/simulator.hpp"
#include "simulator/http_server.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/announce_entry.hpp"
#include "libtorrent/session.hpp"
using namespace libtorrent;
using namespace sim;
@ -48,82 +48,6 @@ using chrono::duration_cast;
// seconds
const int duration = 10000;
struct test_swarm_config : swarm_config
{
test_swarm_config(int num_torrents, std::vector<std::string>* announces=NULL)
: swarm_config()
, m_announces(announces)
, m_num_torrents(num_torrents)
{}
void on_session_added(int idx, session& ses) override
{
}
virtual libtorrent::add_torrent_params add_torrent(int idx) override
{
add_torrent_params p = swarm_config::add_torrent(idx);
// add the tracker to the last torrent (if there's only one, it will be a
// seed from the start, if there are more than one, it will be a torrent
// that downloads the torrent and turns into a seed)
if (m_num_torrents - 1 == idx)
{
p.trackers.push_back("http://2.2.2.2:8080/announce");
}
return p;
}
bool on_alert(libtorrent::alert const* alert
, int session_idx
, std::vector<libtorrent::torrent_handle> const& handles
, libtorrent::session& ses) override
{
if (m_announces == NULL) return false;
char type = 0;
if (lt::alert_cast<lt::tracker_announce_alert>(alert))
{
type = 'A';
}
else if (lt::alert_cast<lt::tracker_error_alert>(alert))
{
type = 'E';
}
else if (lt::alert_cast<lt::tracker_warning_alert>(alert))
{
type = 'W';
}
else if (lt::alert_cast<lt::tracker_reply_alert>(alert))
{
type = 'R';
}
else
{
return false;
}
char msg[500];
snprintf(msg, sizeof(msg), "%c: %d %s", type
, int(duration_cast<chrono::seconds>(alert->timestamp().time_since_epoch()).count())
, alert->message().c_str());
m_announces->push_back(msg);
return false;
}
virtual void on_exit(std::vector<torrent_handle> const& torrents) override
{
}
virtual bool tick(int t) override { return t > duration; }
private:
std::vector<std::string>* m_announces;
int m_num_torrents;
};
void test_interval(int interval)
{
using sim::asio::ip::address_v4;
@ -152,19 +76,42 @@ void test_interval(int interval)
return sim::send_response(200, "OK", size) + response;
});
std::vector<std::string> announce_alerts;
test_swarm_config cfg(1, &announce_alerts);
setup_swarm(1, sim, cfg);
std::vector<int> announce_alerts;
lt::settings_pack default_settings = settings();
lt::add_torrent_params default_add_torrent;
setup_swarm(1, swarm_test::upload, sim, default_settings, default_add_torrent
// add session
, [](lt::settings_pack& pack) { }
// add torrent
, [](lt::add_torrent_params& params) {
params.trackers.push_back("http://2.2.2.2:8080/announce");
}
// on alert
, [&](lt::alert const* a, lt::session& ses) {
if (lt::alert_cast<lt::tracker_announce_alert>(a))
{
boost::uint32_t seconds = chrono::duration_cast<lt::seconds>(
a->timestamp() - start).count();
announce_alerts.push_back(seconds);
}
}
// terminate
, [](int ticks, lt::session& ses) -> bool { return ticks > duration; });
TEST_EQUAL(announce_alerts.size(), announces.size());
int counter = 0;
for (int i = 0; i < int(announces.size()); ++i)
{
TEST_EQUAL(announces[i], counter);
TEST_EQUAL(announce_alerts[i], counter);
counter += interval;
if (counter > duration + 1) counter = duration + 1;
}
// TODO: verify that announce_alerts seem right as well
}
TORRENT_TEST(event_completed)
@ -173,38 +120,64 @@ TORRENT_TEST(event_completed)
sim::default_config network_cfg;
sim::simulation sim{network_cfg};
lt::time_point start = lt::clock_type::now();
sim::asio::io_service web_server(sim, address_v4::from_string("2.2.2.2"));
// listen on port 8080
sim::http_server http(web_server, 8080);
// the timestamps (in seconds) of all announces
std::vector<std::string> announces;
// the request strings of all announces
std::vector<std::pair<int, std::string>> announces;
const int interval = 500;
lt::time_point start = lt::clock_type::now();
http.register_handler("/announce"
, [&announces,interval,start](std::string method, std::string req
, [&](std::string method, std::string req
, std::map<std::string, std::string>&)
{
TEST_EQUAL(method, "GET");
announces.push_back(req);
int timestamp = chrono::duration_cast<lt::seconds>(
lt::clock_type::now() - start).count();
announces.push_back({timestamp, req});
char response[500];
int size = snprintf(response, sizeof(response), "d8:intervali%de5:peers0:e", interval);
return sim::send_response(200, "OK", size) + response;
});
test_swarm_config cfg(2);
setup_swarm(2, sim, cfg);
lt::settings_pack default_settings = settings();
lt::add_torrent_params default_add_torrent;
int completion = -1;
setup_swarm(2, swarm_test::download, sim, default_settings, default_add_torrent
// add session
, [](lt::settings_pack& pack) { }
// add torrent
, [](lt::add_torrent_params& params) {
params.trackers.push_back("http://2.2.2.2:8080/announce");
}
// on alert
, [&](lt::alert const* a, lt::session& ses) {}
// terminate
, [&](int ticks, lt::session& ses) -> bool
{
if (completion == -1 && is_seed(ses))
{
completion = chrono::duration_cast<lt::seconds>(
lt::clock_type::now() - start).count();
}
return ticks > duration;
});
// the first announce should be event=started, the second should be
// event=completed, then all but the last should have no event and the last
// be event=stopped.
for (int i = 0; i < int(announces.size()); ++i)
{
std::string const& str = announces[i];
std::string const& str = announces[i].second;
int timestamp = announces[i].first;
const bool has_start = str.find("&event=started")
!= std::string::npos;
const bool has_completed = str.find("&event=completed")
@ -217,6 +190,7 @@ TORRENT_TEST(event_completed)
fprintf(stderr, "- %s\n", str.c_str());
// there is exactly 0 or 1 events.
TEST_EQUAL(int(has_start) + int(has_completed) + int(has_stopped)
, int(has_event));
@ -226,8 +200,13 @@ TORRENT_TEST(event_completed)
TEST_CHECK(has_start);
break;
case 1:
{
// the announce should have come approximately the same time we
// completed
TEST_CHECK(abs(completion - timestamp) <= 1);
TEST_CHECK(has_completed);
break;
}
default:
if (i == int(announces.size()) - 1)
{

View File

@ -31,98 +31,126 @@ POSSIBILITY OF SUCH DAMAGE.
*/
#include "setup_swarm.hpp"
#include "swarm_config.hpp"
#include "libtorrent/alert.hpp"
#include "libtorrent/announce_entry.hpp"
#include "libtorrent/settings_pack.hpp"
#include "libtorrent/add_torrent_params.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/extensions/lt_trackers.hpp"
#include "libtorrent/session.hpp"
#include "test.hpp"
#include "settings.hpp"
enum flags
{
no_metadata = 1
};
struct test_swarm_config : swarm_config
{
test_swarm_config(int flags)
: swarm_config()
, m_flags(flags)
{}
void on_session_added(int idx, session& ses) override
{
ses.add_extension(create_lt_trackers_plugin);
}
virtual libtorrent::add_torrent_params add_torrent(int idx) override
{
add_torrent_params p = swarm_config::add_torrent(idx);
if (m_flags & no_metadata)
{
p.info_hash = sha1_hash("aaaaaaaaaaaaaaaaaaaa");
p.ti.reset();
}
// make sure neither peer has any content
// TODO: it would be more efficient to not create the content in the first
// place
p.save_path = save_path(1);
if (idx == 1)
{
p.trackers.push_back("http://test.non-existent.com/announce");
}
return p;
}
bool on_alert(libtorrent::alert const* alert
, int session_idx
, std::vector<libtorrent::torrent_handle> const& handles
, libtorrent::session& ses) override
{
if ((m_flags & no_metadata) == 0)
{
if (handles[0].trackers().size() == 1
&& handles[1].trackers().size() == 1)
return true;
}
return false;
}
virtual void on_exit(std::vector<torrent_handle> const& torrents) override
{
TEST_CHECK(torrents.size() > 0);
// a peer that does not have metadata should not exchange trackers, since
// it may be a private torrent
if (m_flags & no_metadata)
{
TEST_EQUAL(torrents[0].trackers().size(), 0);
TEST_EQUAL(torrents[1].trackers().size(), 1);
}
else
{
TEST_EQUAL(torrents[0].trackers().size(), 1);
TEST_EQUAL(torrents[1].trackers().size(), 1);
}
}
private:
int m_flags;
};
using namespace libtorrent;
TORRENT_TEST(plain)
{
test_swarm_config cfg(0);
setup_swarm(2, cfg);
dsl_config network_cfg;
sim::simulation sim{network_cfg};
settings_pack pack = settings();
add_torrent_params p;
p.flags &= ~lt::add_torrent_params::flag_paused;
p.flags &= ~lt::add_torrent_params::flag_auto_managed;
// the default torrent has one tracker
// we remove this from session 0 (the one under test)
p.trackers.push_back("http://test.non-existent.com/announce");
bool connected = false;
setup_swarm(2, swarm_test::upload
, sim , pack, p
// init session
, [](lt::session& ses) {
ses.add_extension(&create_lt_trackers_plugin);
}
// add session
, [](lt::settings_pack& pack) {}
// add torrent
, [](lt::add_torrent_params& params) {
// make sure neither peer has any content
// TODO: it would be more efficient to not create the content in the first
// place
params.save_path = save_path(test_counter(), 1);
// the test is whether this peer will receive the tracker or not
params.trackers.clear();
}
// on alert
, [&](lt::alert const* a, lt::session& ses) {
if (alert_cast<lt::peer_connect_alert>(a))
connected = true;
}
// terminate
, [&](int ticks, lt::session& ses) -> bool {
if (ticks > 10)
{
TEST_ERROR("timeout");
return true;
}
return connected && ses.get_torrents()[0].trackers().size() > 0;
});
TEST_EQUAL(connected, true);
}
TORRENT_TEST(no_metadata)
{
test_swarm_config cfg(no_metadata);
setup_swarm(2, cfg);
dsl_config network_cfg;
sim::simulation sim{network_cfg};
settings_pack pack = settings();
add_torrent_params p;
p.flags &= ~lt::add_torrent_params::flag_paused;
p.flags &= ~lt::add_torrent_params::flag_auto_managed;
// the default torrent has one tracker
// we remove this from session 0 (the one under test)
p.trackers.push_back("http://test.non-existent.com/announce");
bool connected = false;
setup_swarm(2, swarm_test::upload
, sim , pack, p
// init session
, [](lt::session& ses) {
ses.add_extension(&create_lt_trackers_plugin);
}
// add session
, [](lt::settings_pack& pack) {}
// add torrent
, [](lt::add_torrent_params& params) {
// make sure neither peer has any content
// TODO: it would be more efficient to not create the content in the first
// place
params.save_path = save_path(test_counter(), 1);
// the test is whether this peer will receive the tracker or not
params.trackers.clear();
// if we don't have metadata, the other peer should not send the
// tracker to us
params.info_hash = sha1_hash("aaaaaaaaaaaaaaaaaaaa");
params.ti.reset();
}
// on alert
, [&](lt::alert const* a, lt::session& ses) {
if (alert_cast<lt::peer_connect_alert>(a))
connected = true;
}
// terminate
, [](int ticks, lt::session& ses) -> bool {
if (ticks < 10)
return false;
TEST_EQUAL(ses.get_torrents()[0].trackers().size(), 0);
return true;
});
TEST_EQUAL(connected, true);
}

View File

@ -43,6 +43,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/settings_pack.hpp"
#include "simulator/simulator.hpp"
#include "simulator/socks_server.hpp"
#include "setup_swarm.hpp"
using namespace sim;
@ -53,11 +54,6 @@ enum flags_t
ipv6 = 1,
};
asio::ip::address addr(char const* str)
{
return asio::ip::address::from_string(str);
}
template <typename Setup, typename HandleAlerts, typename Test>
void run_test(
Setup const& setup
@ -173,14 +169,11 @@ void run_test(
sim.run();
}
void enable_utp(lt::session& ses)
void utp_only(lt::session& ses)
{
using namespace libtorrent;
settings_pack p;
p.set_bool(settings_pack::enable_outgoing_tcp, false);
p.set_bool(settings_pack::enable_incoming_tcp, false);
p.set_bool(settings_pack::enable_outgoing_utp, true);
p.set_bool(settings_pack::enable_incoming_utp, true);
utp_only(p);
ses.apply_settings(p);
}
@ -188,10 +181,7 @@ void enable_enc(lt::session& ses)
{
using namespace libtorrent;
settings_pack p;
p.set_bool(settings_pack::prefer_rc4, true);
p.set_int(settings_pack::in_enc_policy, settings_pack::pe_forced);
p.set_int(settings_pack::out_enc_policy, settings_pack::pe_forced);
p.set_int(settings_pack::allowed_enc_level, settings_pack::pe_both);
enable_enc(p);
ses.apply_settings(p);
}
@ -225,12 +215,6 @@ void set_proxy(lt::session& ses, int proxy_type, int flags = 0, bool proxy_peer_
ses.apply_settings(p);
}
bool is_seed(lt::session& ses)
{
lt::torrent_handle h = ses.get_torrents()[0];
return h.status().is_seeding;
}
TORRENT_TEST(socks4_tcp)
{
using namespace libtorrent;
@ -346,10 +330,6 @@ TORRENT_TEST(no_proxy_utp)
);
}
// TODO: test allow-fast
// TODO: test the different storage allocation modes
// TODO: test contiguous buffers
// TODO: the socks server does not support UDP yet
/*
@ -358,7 +338,7 @@ TORRENT_TEST(encryption_utp)
using namespace libtorrent;
run_test(
[](lt::session& ses0, lt::session& ses1)
{ enable_enc(ses0); enable_enc(ses1); enable_utp(ses0); },
{ enable_enc(ses0); enable_enc(ses1); utp_only(ses0); },
[](lt::session& ses, lt::alert const* alert) {},
[](std::shared_ptr<lt::session> ses[2]) {
TEST_EQUAL(is_seed(*ses[0]), true);
@ -373,7 +353,7 @@ TORRENT_TEST(socks5_utp)
[](lt::session& ses0, lt::session& ses1)
{
set_proxy(ses0, settings_pack::socks5);
enable_utp(ses0);
utp_only(ses0);
filter_ips(ses1);
},
[](lt::session& ses, lt::alert const* alert) {},

View File

@ -38,7 +38,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "test.hpp"
#include "setup_swarm.hpp"
#include "setup_transfer.hpp" // for create_torrent (factor this out!)
#include "settings.hpp"
#include <fstream>
#include <iostream>
@ -46,144 +45,45 @@ POSSIBILITY OF SUCH DAMAGE.
using namespace libtorrent;
namespace lt = libtorrent;
// TODO: a lot of this class is boiler plate. Factor out into a reusable unit
struct swarm_config : swarm_setup_provider
{
swarm_config(int num_peers)
: m_swarm_id(std::rand())
, m_start_time(lt::clock_type::now())
{
// in case the previous run did not exit gracefully
clear_download_directories(num_peers);
error_code ec;
char save_path[200];
snprintf(save_path, sizeof(save_path), "swarm-%04d-peer-%02d"
, m_swarm_id, 0);
create_directory(save_path, ec);
std::ofstream file(combine_path(save_path, "temporary").c_str());
m_ti = ::create_torrent(&file, "temporary", 0x4000, 90, false);
file.close();
}
virtual void on_exit(std::vector<torrent_handle> const& torrents)
{
TEST_CHECK(torrents.size() > 0);
for (int i = 0; i < int(torrents.size()); ++i)
{
torrent_status st = torrents[i].status();
TEST_CHECK(st.is_seeding);
TEST_CHECK(st.total_upload > 0 || st.total_download > 0);
}
// if this check fails, there is a performance regression in the protocol,
// either uTP or bittorrent itself. Be careful with the download request
// queue size (and make sure it can grow fast enough, to keep up with
// slow-start) and the send buffer watermark
TEST_CHECK(lt::clock_type::now() < m_start_time + lt::milliseconds(8500));
// clean up the download directories to make the next run start from
// scratch.
clear_download_directories(int(torrents.size()));
}
void clear_download_directories(int num_peers)
{
for (int i = 0; i < num_peers; ++i)
{
error_code ec;
char save_path[200];
snprintf(save_path, sizeof(save_path), "swarm-%04d-peer-%02d"
, m_swarm_id, i);
// in case the previous run did not exit gracefully
remove_all(save_path, ec);
}
}
// called for every alert. if the simulation is done, return true
virtual bool on_alert(libtorrent::alert const* alert
, int session_idx
, std::vector<libtorrent::torrent_handle> const& torrents
, libtorrent::session& ses) override
{
if (torrents.empty()) return false;
bool all_are_seeding = true;
for (int i = 0; i < int(torrents.size()); ++i)
{
if (torrents[i].status().is_seeding)
continue;
all_are_seeding = false;
break;
}
// if all torrents are seeds, terminate the simulation, we're done
return all_are_seeding;
}
// called for every torrent that's added (and every session that's started).
// this is useful to give every session a unique save path and to make some
// sessions seeds and others downloaders
virtual libtorrent::add_torrent_params add_torrent(int idx) override
{
add_torrent_params p;
p.flags &= ~add_torrent_params::flag_paused;
p.flags &= ~add_torrent_params::flag_auto_managed;
if (idx == 0)
{
// skip checking up front, and get started with the transfer sooner
p.flags |= add_torrent_params::flag_seed_mode;
}
p.ti = m_ti;
char save_path[200];
snprintf(save_path, sizeof(save_path), "swarm-%04d-peer-%02d"
, m_swarm_id, idx);
p.save_path = save_path;
return p;
}
// called for every session that's added
virtual libtorrent::settings_pack add_session(int idx) override
{
settings_pack pack = settings();
// force uTP connection
pack.set_bool(settings_pack::enable_incoming_utp, true);
pack.set_bool(settings_pack::enable_outgoing_utp, true);
pack.set_bool(settings_pack::enable_incoming_tcp, false);
pack.set_bool(settings_pack::enable_outgoing_tcp, false);
// the encryption handshake adds several round-trips to the bittorrent
// handshake, and slows it down significantly
pack.set_int(settings_pack::out_enc_policy, settings_pack::pe_disabled);
pack.set_int(settings_pack::in_enc_policy, settings_pack::pe_disabled);
// make sure the sessions have different peer ids
lt::peer_id pid;
std::generate(&pid[0], &pid[0] + 20, &random_byte);
pack.set_str(lt::settings_pack::peer_fingerprint, pid.to_string());
return pack;
}
private:
int m_swarm_id;
lt::time_point m_start_time;
boost::shared_ptr<libtorrent::torrent_info> m_ti;
};
TORRENT_TEST(utp)
{
// TODO: 3 simulate packet loss
// TODO: 3 simulate unpredictible latencies
// TODO: 3 simulate proper (taildrop) queues (perhaps even RED and BLUE)
swarm_config cfg(2);
setup_swarm(2, cfg);
lt::time_point start_time = lt::clock_type::now();
sim::default_config cfg;
sim::simulation sim{cfg};
setup_swarm(2, swarm_test::upload, sim
// add session
, [](lt::settings_pack& pack) {
// force uTP connection
utp_only(pack);
}
// add torrent
, [](lt::add_torrent_params& params) {
params.flags |= add_torrent_params::flag_seed_mode;
}
// on alert
, [&](lt::alert const* a, lt::session& ses) {
if (!is_seed(ses)) return;
// if this check fails, there is a performance regression in the protocol,
// either uTP or bittorrent itself. Be careful with the download request
// queue size (and make sure it can grow fast enough, to keep up with
// slow-start) and the send buffer watermark
TEST_CHECK(lt::clock_type::now() < start_time + lt::milliseconds(8500));
}
// terminate
, [](int ticks, lt::session& ses) -> bool
{
if (ticks > 100)
{
TEST_ERROR("timeout");
return true;
}
return false;
});
}

View File

@ -259,15 +259,14 @@ namespace libtorrent { namespace
if (m_tp.num_tex_trackers() >= 50)
break;
#ifndef TORRENT_DISABLE_LOGGING
m_pc.peer_log(peer_log_alert::info, "LT_TEX", "adding: %s", e.url.c_str());
#endif
e.fail_limit = 1;
e.send_stats = false;
e.source = announce_entry::source_tex;
if (m_torrent.add_tracker(e))
m_tp.increment_tracker_counter();
#ifndef TORRENT_DISABLE_LOGGING
m_pc.peer_log(peer_log_alert::info, "LT_TEX", "added: %s", e.url.c_str());
#endif
}
return true;
}

View File

@ -4092,7 +4092,10 @@ namespace libtorrent
// we cannot do this in a constructor
TORRENT_ASSERT(m_in_constructor == false);
if (error > 0) m_failed = true;
if (error > 0)
{
m_failed = true;
}
if (m_connected)
m_counters.inc_stats_counter(counters::num_peers_connected, -1);
@ -4144,7 +4147,7 @@ namespace libtorrent
if (ec == error_code(errors::timed_out)
|| ec == error::timed_out)
m_counters.inc_stats_counter(counters::transport_timeout_peers);
if (ec == error_code(errors::timed_out_inactivity)
|| ec == error_code(errors::timed_out_no_request)
|| ec == error_code(errors::timed_out_no_interest))

View File

@ -9610,20 +9610,13 @@ namespace libtorrent
TORRENT_ASSERT(is_single_thread());
INVARIANT_CHECK;
if (!m_allow_peers) return;
set_allow_peers(graceful ? true : false, graceful);
if (m_allow_peers)
{
// we need to save this new state
set_need_save_resume();
}
m_announce_to_dht = false;
m_announce_to_trackers = false;
m_announce_to_lsd = false;
// we need to save this new state
set_need_save_resume();
state_updated();
update_gauge();
update_want_peers();
update_want_scrape();
set_allow_peers(false, graceful);
}
void torrent::do_pause()
@ -9699,20 +9692,14 @@ namespace libtorrent
// disconnect all peers with no outstanding data to receive
// and choke all remaining peers to prevent responding to new
// requests
bool update_ticks = false;
std::vector<peer_connection*> to_disconnect;
for (peer_iterator i = m_connections.begin();
i != m_connections.end();)
i != m_connections.end(); ++i)
{
peer_iterator j = i++;
boost::shared_ptr<peer_connection> p = (*j)->self();
peer_connection* p = *i;
TORRENT_ASSERT(p->associated_torrent().lock().get() == this);
if (p->is_disconnecting())
{
i = m_connections.erase(j);
update_ticks = true;
continue;
}
if (p->is_disconnecting()) continue;
if (p->outstanding_bytes() > 0)
{
@ -9726,21 +9713,19 @@ namespace libtorrent
continue;
}
to_disconnect.push_back(p);
}
for (peer_iterator i = to_disconnect.begin(); i != to_disconnect.end(); ++i)
{
peer_connection* p = *i;
// since we're currently in graceful pause mode, the last peer to
// disconnect (assuming all peers end up begin disconnected here)
// will post the torrent_paused_alert
#ifndef TORRENT_DISABLE_LOGGING
p->peer_log(peer_log_alert::info, "CLOSING_CONNECTION", "torrent_paused");
#endif
p->disconnect(errors::torrent_paused, op_bittorrent);
i = j;
}
if (m_connections.empty())
{
if (alerts().should_post<torrent_paused_alert>())
alerts().emplace_alert<torrent_paused_alert>(get_handle());
}
if (update_ticks)
{
update_want_peers();
update_want_tick();
}
}
@ -9792,12 +9777,20 @@ namespace libtorrent
{
TORRENT_ASSERT(is_single_thread());
// if there are no peers, there is no point in a graceful pause mode. In
// fact, the promise to post the torrent_paused_alert exactly once is
// maintained by the last peer to be disconnected in graceful pause mode,
// if there are no peers, we must not enter graceful pause mode, and post
// the torrent_paused_alert immediately instead.
if (m_connections.empty())
graceful = false;
if (m_allow_peers == b)
{
// there is one special case here. If we are
// currently in graceful pause mode, and we just turned into regular
// paused mode, we need to actually pause the torrent properly
if (m_allow_peers == true
if (m_allow_peers == false
&& m_graceful_pause_mode == true
&& graceful == false)
{
@ -9812,21 +9805,28 @@ namespace libtorrent
if (!m_ses.is_paused())
m_graceful_pause_mode = graceful;
update_gauge();
update_want_scrape();
update_state_list();
if (!b)
{
m_announce_to_dht = false;
m_announce_to_trackers = false;
m_announce_to_lsd = false;
}
update_gauge();
update_want_scrape();
update_want_peers();
update_state_list();
state_updated();
if (!b)
{
do_pause();
}
else
{
do_resume();
}
}
void torrent::resume()

View File

@ -50,6 +50,11 @@ libtorrent::settings_pack settings()
pack.set_bool(settings_pack::enable_upnp, false);
pack.set_bool(settings_pack::enable_dht, false);
pack.set_bool(settings_pack::prefer_rc4, false);
pack.set_int(settings_pack::in_enc_policy, settings_pack::pe_disabled);
pack.set_int(settings_pack::out_enc_policy, settings_pack::pe_disabled);
pack.set_int(settings_pack::allowed_enc_level, settings_pack::pe_both);
pack.set_int(settings_pack::alert_mask, mask);
#ifndef TORRENT_BUILD_SIMULATOR