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