1 /*
2
3 Copyright (c) 2016, Arvid Norberg
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
30
31 */
32
33 #include "libtorrent/session.hpp"
34 #include "libtorrent/torrent_handle.hpp"
35 #include "libtorrent/settings_pack.hpp"
36 #include "libtorrent/alert_types.hpp"
37 #include "libtorrent/deadline_timer.hpp"
38 #include "settings.hpp"
39 #include "fake_peer.hpp"
40 #include "utils.hpp"
41 #include "create_torrent.hpp"
42 #include "simulator/simulator.hpp"
43 #include "simulator/utils.hpp"
44 #include <iostream>
45
46 using namespace sim;
47 using namespace lt;
48
49 using sim::asio::ip::address_v4;
50
51 // this is the general template for these tests. create the session with custom
52 // settings (Settings), set up the test, by adding torrents with certain
53 // arguments (Setup), run the test and verify the end state (Test)
54 template <typename Setup, typename Torrent, typename Test, typename Check>
run_test(Setup const & setup,Torrent const & torrent,Test const & test,Check const & check)55 void run_test(Setup const& setup, Torrent const& torrent
56 , Test const& test, Check const& check)
57 {
58 // setup the simulation
59 sim::default_config network_cfg;
60 sim::simulation sim{network_cfg};
61 std::unique_ptr<sim::asio::io_service> ios = make_io_service(sim, 0);
62 lt::session_proxy zombie;
63
64 // setup settings pack to use for the session (customization point)
65 lt::settings_pack pack = settings();
66
67 // create session
68 std::shared_ptr<lt::session> ses = std::make_shared<lt::session>(pack, *ios);
69
70 setup(*ses);
71
72 fake_peer p1(sim, "60.0.0.0");
73 fake_peer p2(sim, "60.0.0.1");
74 fake_peer p3(sim, "60.0.0.2");
75 std::array<fake_peer*, 3> test_peers = {{ &p1, &p2, &p3 }};
76
77 // add torrent
78 lt::add_torrent_params params = ::create_torrent(0, false);
79 params.flags &= ~lt::torrent_flags::auto_managed;
80 params.flags &= ~lt::torrent_flags::paused;
81 ses->async_add_torrent(std::move(params));
82
83 lt::torrent_handle h;
84 print_alerts(*ses, [&](lt::session& ses, lt::alert const* a) {
85 auto at = lt::alert_cast<add_torrent_alert>(a);
86 if (at == nullptr) return;
87 h = at->handle;
88
89 // disable the print_alert object from polling any more alerts
90 ses.set_alert_notify([]{});
91
92 torrent(ses, h, test_peers);
93 });
94
95 sim::timer t1(sim, lt::seconds(5)
96 , [&](boost::system::error_code const&)
97 {
98 test(*ses, h, test_peers);
99 });
100
101 // set up a timer to fire later, to verify everything we expected to happen
102 // happened
103 sim::timer t2(sim, lt::seconds(10)
104 , [&](boost::system::error_code const&)
105 {
106 check(*ses, h, test_peers);
107
108 // shut down
109 zombie = ses->abort();
110 ses.reset();
111 });
112
113 sim.run();
114 }
115
116 // make sure the torrent disconnects all its peers when it's paused
TORRENT_TEST(torrent_paused_disconnect)117 TORRENT_TEST(torrent_paused_disconnect)
118 {
119 run_test(
120 [](lt::session&) {},
121 [](lt::session&, lt::torrent_handle h, std::array<fake_peer*, 3>&) {
122 add_fake_peers(h, 3);
123 },
124
125 [](lt::session&, lt::torrent_handle h, std::array<fake_peer*, 3>& test_peers) {
126 check_accepted(test_peers, {{true, true, true}});
127 check_connected(test_peers, {{true, true, true}});
128 check_disconnected(test_peers, {{false, false, false}});
129 h.pause();
130 },
131
132 [](lt::session&, lt::torrent_handle h, std::array<fake_peer*, 3>& test_peers) {
133 check_disconnected(test_peers, {{true, true, true}});
134 TEST_CHECK(h.status().flags & torrent_flags::paused);
135 });
136 }
137
138 // make sure the torrent disconnects all its peers when the session is paused
TORRENT_TEST(session_paused_disconnect)139 TORRENT_TEST(session_paused_disconnect)
140 {
141 run_test(
142 [](lt::session&) {},
143 [](lt::session&, lt::torrent_handle h, std::array<fake_peer*, 3>&) {
144 add_fake_peers(h, 3);
145 },
146
147 [](lt::session& ses, lt::torrent_handle h, std::array<fake_peer*, 3>& test_peers) {
148 check_accepted(test_peers, {{true, true, true}});
149 check_connected(test_peers, {{true, true, true}});
150 check_disconnected(test_peers, {{false, false, false}});
151 ses.pause();
152 },
153
154 [](lt::session&, lt::torrent_handle h, std::array<fake_peer*, 3>& test_peers) {
155 check_disconnected(test_peers, {{true, true, true}});
156
157 // the torrent isn't paused, the session is
158 TEST_CHECK(!(h.status().flags & torrent_flags::paused));
159 });
160 }
161
162 // make sure a torrent is not connecting to any peers when added to a paused
163 // session
TORRENT_TEST(paused_session_add_torrent)164 TORRENT_TEST(paused_session_add_torrent)
165 {
166 run_test(
167 [](lt::session& ses) { ses.pause(); },
168 [](lt::session&, lt::torrent_handle h, std::array<fake_peer*, 3>&) {
169 add_fake_peers(h, 3);
170 },
171
172 [](lt::session&, lt::torrent_handle, std::array<fake_peer*, 3>& test_peers) {
173 check_accepted(test_peers, {{false, false, false}});
174 },
175
176 [](lt::session&, lt::torrent_handle h, std::array<fake_peer*, 3>&) {
177 // the torrent isn't paused, the session is
178 TEST_CHECK(!(h.status().flags & torrent_flags::paused));
179 });
180 }
181
182 // make sure the torrent isn't connecting to peers when it's paused
TORRENT_TEST(paused_torrent_add_peers)183 TORRENT_TEST(paused_torrent_add_peers)
184 {
185 run_test(
186 [](lt::session&) {},
187 [](lt::session&, lt::torrent_handle h, std::array<fake_peer*, 3>&) {
188 h.pause();
189
190 add_fake_peers(h, 3);
191 },
192
193 [](lt::session&, lt::torrent_handle, std::array<fake_peer*, 3>& test_peers) {
194 check_accepted(test_peers, {{false, false, false}});
195 },
196
197 [](lt::session&, lt::torrent_handle h, std::array<fake_peer*, 3>&) {
198 TEST_CHECK(h.status().flags & torrent_flags::paused);
199 });
200 }
201
202 // make sure we post the torrent_paused alert when pausing a torrent
TORRENT_TEST(torrent_paused_alert)203 TORRENT_TEST(torrent_paused_alert)
204 {
205 run_test(
206 [](lt::session&) {},
207 [](lt::session&, lt::torrent_handle, std::array<fake_peer*, 3>&) {},
208
209 [](lt::session&, lt::torrent_handle h, std::array<fake_peer*, 3>&) {
210 TEST_CHECK(!(h.status().flags & torrent_flags::paused));
211 h.pause();
212 },
213
214 [](lt::session& ses, lt::torrent_handle h, std::array<fake_peer*, 3>&) {
215 TEST_CHECK(h.status().flags & torrent_flags::paused);
216
217 std::vector<lt::alert*> alerts;
218 ses.pop_alerts(&alerts);
219
220 lt::time_point start_time = alerts[0]->timestamp();
221
222 int num_resume = 0;
223 int num_paused = 0;
224 for (alert* a : alerts)
225 {
226 std::printf("%-3d %s\n", int(duration_cast<lt::seconds>(a->timestamp()
227 - start_time).count()), a->message().c_str());
228 if (lt::alert_cast<torrent_resumed_alert>(a)) ++num_resume;
229 if (lt::alert_cast<torrent_paused_alert>(a)) ++num_paused;
230 }
231
232 TEST_EQUAL(num_resume, 0);
233 TEST_EQUAL(num_paused, 1);
234 });
235 }
236
237 // make sure we post the torrent_paused alert when pausing the session
TORRENT_TEST(session_paused_alert)238 TORRENT_TEST(session_paused_alert)
239 {
240 run_test(
241 [](lt::session&) {},
242 [](lt::session&, lt::torrent_handle, std::array<fake_peer*, 3>&) {},
243
244 [](lt::session& ses, lt::torrent_handle h, std::array<fake_peer*, 3>&) {
245 TEST_CHECK(!(h.status().flags & torrent_flags::paused));
246 ses.pause();
247 },
248
249 [](lt::session& ses, lt::torrent_handle h, std::array<fake_peer*, 3>&) {
250 TEST_CHECK(!(h.status().flags & torrent_flags::paused));
251
252 std::vector<lt::alert*> alerts;
253 ses.pop_alerts(&alerts);
254
255 lt::time_point start_time = alerts[0]->timestamp();
256
257 int num_resume = 0;
258 int num_paused = 0;
259 for (alert* a : alerts)
260 {
261 std::printf("%-3d %s\n", int(duration_cast<lt::seconds>(a->timestamp()
262 - start_time).count()), a->message().c_str());
263 if (lt::alert_cast<torrent_resumed_alert>(a)) ++num_resume;
264 if (lt::alert_cast<torrent_paused_alert>(a)) ++num_paused;
265 }
266
267 TEST_EQUAL(num_resume, 0);
268 TEST_EQUAL(num_paused, 1);
269 });
270 }
271
272 // make sure we post both the paused and resumed alert when pausing and resuming
273 // the session.
TORRENT_TEST(session_pause_resume)274 TORRENT_TEST(session_pause_resume)
275 {
276 run_test(
277 [](lt::session&) {},
278 [](lt::session& ses, lt::torrent_handle h, std::array<fake_peer*, 3>&) {
279 TEST_CHECK(!(h.status().flags & torrent_flags::paused));
280 ses.pause();
281 },
282
283 [](lt::session& ses, lt::torrent_handle h, std::array<fake_peer*, 3>&) {
284 TEST_CHECK(!(h.status().flags & torrent_flags::paused));
285 ses.resume();
286 },
287
288 [](lt::session& ses, lt::torrent_handle h, std::array<fake_peer*, 3>&) {
289 TEST_CHECK(!(h.status().flags & torrent_flags::paused));
290
291 std::vector<lt::alert*> alerts;
292 ses.pop_alerts(&alerts);
293
294 lt::time_point start_time = alerts[0]->timestamp();
295
296 int num_resume = 0;
297 int num_paused = 0;
298 for (alert* a : alerts)
299 {
300 std::printf("%-3d %s\n", int(duration_cast<lt::seconds>(a->timestamp()
301 - start_time).count()), a->message().c_str());
302 if (lt::alert_cast<torrent_resumed_alert>(a)) ++num_resume;
303 if (lt::alert_cast<torrent_paused_alert>(a)) ++num_paused;
304 }
305
306 TEST_EQUAL(num_resume, 1);
307 TEST_EQUAL(num_paused, 1);
308 });
309 }
310
311 // make sure peers added to a (non-paused) torrent in a paused session are
312 // connected once the session is resumed
TORRENT_TEST(session_pause_resume_connect)313 TORRENT_TEST(session_pause_resume_connect)
314 {
315 run_test(
316 [](lt::session&) {},
317 [](lt::session& ses, lt::torrent_handle h, std::array<fake_peer*, 3>&) {
318 TEST_CHECK(!(h.status().flags & torrent_flags::paused));
319 ses.pause();
320 add_fake_peers(h, 3);
321 },
322
323 [](lt::session& ses, lt::torrent_handle h, std::array<fake_peer*, 3>& test_peers) {
324 TEST_CHECK(!(h.status().flags & torrent_flags::paused));
325 check_accepted(test_peers, {{false, false, false}});
326 ses.resume();
327 },
328
329 [](lt::session&, lt::torrent_handle h, std::array<fake_peer*, 3>& test_peers) {
330 TEST_CHECK(!(h.status().flags & torrent_flags::paused));
331
332 check_accepted(test_peers, {{true, true, true}});
333 });
334 }
335
336