clean up setup-swarm simulation utility to be more flexible to make it easier to add new tests. remove swarm_suite layer as it's not necessary anymore

This commit is contained in:
arvidn 2015-12-06 00:14:30 -05:00
parent 5187b4279d
commit 1f453bf159
9 changed files with 516 additions and 646 deletions

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,291 @@ 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)
std::string save_path(int swarm_id, int idx)
{
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));
char path[200];
snprintf(path, sizeof(path), "swarm-%04d-peer-%02d"
, swarm_id, idx);
return path;
}
void on_tick(lt::error_code const& ec)
int transfer_rate(lt::address ip)
{
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));
// 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_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);
}
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(
lt::address_v4::from_string(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 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 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();
asio::io_service ios(sim, asio::ip::address_v4::from_string("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), asio::ip::address_v4::from_string(ep)));
lt::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());
if (i == 0) new_session(pack);
boost::shared_ptr<lt::session> ses =
boost::make_shared<lt::session>(pack
, boost::ref(*io_service.back()));
nodes.push_back(ses);
if (i > 0)
{
// the other sessions should not talk to each other
lt::ip_filter filter;
filter.add_rule(lt::address::from_string("0.0.0.0")
, lt::address::from_string("255.255.255.255"), lt::ip_filter::blocked);
filter.add_rule(lt::address::from_string("50.0.0.1")
, lt::address::from_string("50.0.0.1"), 0);
ses->set_ip_filter(filter);
}
lt::add_torrent_params p;
p.flags &= ~lt::add_torrent_params::flag_paused;
p.flags &= ~lt::add_torrent_params::flag_auto_managed;
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
for (int k = 0; k < num_nodes; ++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));
}
}
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].get());
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.get()); });
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,52 @@ 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);
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);
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

@ -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,230 @@ 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) {}
// 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);
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)
{
auto h = ses->get_torrents()[0];
h.pause(graceful ? torrent_handle::graceful_pause : 0);
paused_once = true;
}
}
const int timeout = (type == swarm_test::upload) ? 64 : 20;
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_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