1 /*
2 
3 Copyright (c) 2015, 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 "test.hpp"
34 #include "libtorrent/alert_manager.hpp"
35 #include "libtorrent/torrent_handle.hpp"
36 #include "libtorrent/alert_types.hpp"
37 #include "libtorrent/extensions.hpp"
38 #include "setup_transfer.hpp"
39 
40 #include <functional>
41 #include <thread>
42 
43 using namespace lt;
44 
TORRENT_TEST(limit)45 TORRENT_TEST(limit)
46 {
47 	alert_manager mgr(500, alert_category::all);
48 
49 	TEST_EQUAL(mgr.alert_queue_size_limit(), 500);
50 	TEST_EQUAL(mgr.pending(), false);
51 
52 	// try add 600 torrent_add_alert to make sure we honor the limit of 500
53 	// alerts.
54 	for (piece_index_t i{0}; i < piece_index_t{600}; ++i)
55 		mgr.emplace_alert<piece_finished_alert>(torrent_handle(), i);
56 
57 	TEST_EQUAL(mgr.pending(), true);
58 
59 	std::vector<alert*> alerts;
60 	mgr.get_all(alerts);
61 
62 	// even though we posted 600, the limit was 500
63 	// +1 for the alerts_dropped_alert
64 	TEST_EQUAL(alerts.size(), 501);
65 
66 	TEST_EQUAL(mgr.pending(), false);
67 
68 	// now, try lowering the limit and do the same thing again
69 	mgr.set_alert_queue_size_limit(200);
70 
71 	for (piece_index_t i{0}; i < piece_index_t{600}; ++i)
72 		mgr.emplace_alert<piece_finished_alert>(torrent_handle(), i);
73 
74 	TEST_EQUAL(mgr.pending(), true);
75 
76 	mgr.get_all(alerts);
77 
78 	// even though we posted 600, the limit was 200
79 	// +1 for the alerts_dropped_alert
80 	TEST_EQUAL(alerts.size(), 201);
81 }
82 
TORRENT_TEST(limit_int_max)83 TORRENT_TEST(limit_int_max)
84 {
85 	int const inf = std::numeric_limits<int>::max();
86 	alert_manager mgr(inf, alert_category::all);
87 
88 	TEST_EQUAL(mgr.alert_queue_size_limit(), inf);
89 
90 	for (piece_index_t i{0}; i < piece_index_t{600}; ++i)
91 		mgr.emplace_alert<piece_finished_alert>(torrent_handle(), i);
92 
93 	for (piece_index_t i{0}; i < piece_index_t{600}; ++i)
94 		mgr.emplace_alert<torrent_removed_alert>(torrent_handle(), sha1_hash());
95 
96 	std::vector<alert*> alerts;
97 	mgr.get_all(alerts);
98 
99 	TEST_EQUAL(alerts.size(), 1200);
100 }
101 
TORRENT_TEST(priority_limit)102 TORRENT_TEST(priority_limit)
103 {
104 	alert_manager mgr(100, alert_category::all);
105 
106 	TEST_EQUAL(mgr.alert_queue_size_limit(), 100);
107 
108 	// this should only add 100 because of the limit
109 	for (piece_index_t i{0}; i < piece_index_t{200}; ++i)
110 		mgr.emplace_alert<piece_finished_alert>(torrent_handle(), i);
111 
112 	// the limit is twice as high for priority alerts
113 	for (file_index_t i(0); i < file_index_t(300); ++i)
114 		mgr.emplace_alert<file_rename_failed_alert>(torrent_handle(), i, error_code());
115 
116 	std::vector<alert*> alerts;
117 	mgr.get_all(alerts);
118 
119 	// even though we posted 500, the limit was 100 for half of them and
120 	// 100 + 200 for the other half, meaning we should have 300 alerts now
121 	// +1 for the alerts_dropped_alert
122 	TEST_EQUAL(alerts.size(), 301);
123 }
124 
125 namespace {
test_notify_fun(int & cnt)126 void test_notify_fun(int& cnt)
127 {
128 	++cnt;
129 }
130 } // anonymous namespace
131 
TORRENT_TEST(notify_function)132 TORRENT_TEST(notify_function)
133 {
134 	int cnt = 0;
135 	alert_manager mgr(100, alert_category::all);
136 
137 	TEST_EQUAL(mgr.alert_queue_size_limit(), 100);
138 	TEST_EQUAL(mgr.pending(), false);
139 
140 	for (int i = 0; i < 20; ++i)
141 		mgr.emplace_alert<add_torrent_alert>(torrent_handle(), add_torrent_params(), error_code());
142 
143 	TEST_EQUAL(mgr.pending(), true);
144 
145 	// if there are queued alerts when we set the notify function,
146 	// that counts as an edge and it's called
147 	mgr.set_notify_function(std::bind(&test_notify_fun, std::ref(cnt)));
148 
149 	TEST_EQUAL(mgr.pending(), true);
150 	TEST_EQUAL(cnt, 1);
151 
152 	// subsequent posted alerts will not cause an edge (because there are
153 	// already alerts queued)
154 	for (int i = 0; i < 20; ++i)
155 		mgr.emplace_alert<add_torrent_alert>(torrent_handle(), add_torrent_params(), error_code());
156 
157 	TEST_EQUAL(mgr.pending(), true);
158 	TEST_EQUAL(cnt, 1);
159 
160 	// however, if we pop all the alerts and post new ones, there will be
161 	// and edge triggering the notify call
162 	std::vector<alert*> alerts;
163 	mgr.get_all(alerts);
164 
165 	TEST_EQUAL(mgr.pending(), false);
166 
167 	for (int i = 0; i < 20; ++i)
168 		mgr.emplace_alert<add_torrent_alert>(torrent_handle(), add_torrent_params(), error_code());
169 
170 	TEST_EQUAL(mgr.pending(), true);
171 	TEST_EQUAL(cnt, 2);
172 }
173 
174 #ifndef TORRENT_DISABLE_EXTENSIONS
175 namespace {
176 int plugin_alerts[3] = { 0, 0, 0 };
177 
178 struct test_plugin : lt::plugin
179 {
test_plugin__anon4059b60f0211::test_plugin180 	explicit test_plugin(int index) : m_index(index) {}
on_alert__anon4059b60f0211::test_plugin181 	void on_alert(alert const*) override
182 	{
183 		++plugin_alerts[m_index];
184 	}
185 	int m_index;
186 };
187 } // anonymous namespace
188 #endif
189 
TORRENT_TEST(extensions)190 TORRENT_TEST(extensions)
191 {
192 #ifndef TORRENT_DISABLE_EXTENSIONS
193 	memset(plugin_alerts, 0, sizeof(plugin_alerts));
194 	alert_manager mgr(100, alert_category::all);
195 
196 	mgr.add_extension(std::make_shared<test_plugin>(0));
197 	mgr.add_extension(std::make_shared<test_plugin>(1));
198 	mgr.add_extension(std::make_shared<test_plugin>(2));
199 
200 	for (int i = 0; i < 53; ++i)
201 		mgr.emplace_alert<add_torrent_alert>(torrent_handle(), add_torrent_params(), error_code());
202 
203 	TEST_EQUAL(plugin_alerts[0], 53);
204 	TEST_EQUAL(plugin_alerts[1], 53);
205 	TEST_EQUAL(plugin_alerts[2], 53);
206 
207 	for (int i = 0; i < 17; ++i)
208 		mgr.emplace_alert<add_torrent_alert>(torrent_handle(), add_torrent_params(), error_code());
209 
210 	TEST_EQUAL(plugin_alerts[0], 70);
211 	TEST_EQUAL(plugin_alerts[1], 70);
212 	TEST_EQUAL(plugin_alerts[2], 70);
213 #endif
214 }
215 
216 /*
217 namespace {
218 
219 void post_torrent_added(alert_manager* mgr)
220 {
221 	std::this_thread::sleep_for(lt::milliseconds(10));
222 	mgr->emplace_alert<add_torrent_alert>(torrent_handle(), add_torrent_params(), error_code());
223 }
224 
225 } // anonymous namespace
226 
227 // this test is too flaky
228 
229 TORRENT_TEST(wait_for_alert)
230 {
231 	alert_manager mgr(100, alert_category::all);
232 
233 	time_point start = clock_type::now();
234 
235 	alert* a = mgr.wait_for_alert(seconds(1));
236 
237 	time_point end = clock_type::now();
238 	TEST_EQUAL(a, static_cast<alert*>(nullptr));
239 	std::printf("delay: %d ms (expected 1 second)\n"
240 		, int(total_milliseconds(end - start)));
241 	TEST_CHECK(end - start > milliseconds(900));
242 	TEST_CHECK(end - start < milliseconds(1100));
243 
244 	mgr.emplace_alert<add_torrent_alert>(torrent_handle(), add_torrent_params(), error_code());
245 
246 	start = clock_type::now();
247 	a = mgr.wait_for_alert(seconds(1));
248 	end = clock_type::now();
249 
250 	std::printf("delay: %d ms\n", int(total_milliseconds(end - start)));
251 	TEST_CHECK(end - start < milliseconds(1));
252 	TEST_CHECK(a->type() == add_torrent_alert::alert_type);
253 
254 	std::vector<alert*> alerts;
255 	mgr.get_all(alerts);
256 
257 	start = clock_type::now();
258 	std::thread posting_thread(&post_torrent_added, &mgr);
259 
260 	a = mgr.wait_for_alert(seconds(10));
261 	end = clock_type::now();
262 
263 	std::printf("delay: %d ms\n", int(total_milliseconds(end - start)));
264 	TEST_CHECK(end - start < milliseconds(500));
265 	TEST_CHECK(a->type() == add_torrent_alert::alert_type);
266 
267 	posting_thread.join();
268 }
269 */
270 
TORRENT_TEST(alert_mask)271 TORRENT_TEST(alert_mask)
272 {
273 	alert_manager mgr(100, alert_category::all);
274 
275 	TEST_CHECK(mgr.should_post<add_torrent_alert>());
276 	TEST_CHECK(mgr.should_post<torrent_paused_alert>());
277 
278 	mgr.set_alert_mask({});
279 
280 	TEST_CHECK(!mgr.should_post<add_torrent_alert>());
281 	TEST_CHECK(!mgr.should_post<torrent_paused_alert>());
282 }
283 
TORRENT_TEST(get_all_empty)284 TORRENT_TEST(get_all_empty)
285 {
286 	alert_manager mgr(100, alert_category::all);
287 	std::vector<alert*> alerts(10);
288 
289 	mgr.get_all(alerts);
290 
291 	TEST_CHECK(alerts.empty());
292 }
293 
TORRENT_TEST(dropped_alerts)294 TORRENT_TEST(dropped_alerts)
295 {
296 	alert_manager mgr(1, alert_category::all);
297 
298 	// nothing has dropped yet
299 	mgr.emplace_alert<torrent_finished_alert>(torrent_handle());
300 	// still nothing, there's space for one alert
301 	mgr.emplace_alert<torrent_finished_alert>(torrent_handle());
302 	// still nothing, there's space for one alert
303 	mgr.emplace_alert<torrent_finished_alert>(torrent_handle());
304 	// that last alert got dropped though, since it would have brought the queue
305 	// size to 3
306 	std::vector<alert*> alerts;
307 	mgr.get_all(alerts);
308 	auto const d = alert_cast<alerts_dropped_alert>(alerts.back())->dropped_alerts;
309 	TEST_EQUAL(d.count(), 1);
310 	TEST_CHECK(d.test(torrent_finished_alert::alert_type));
311 }
312 
TORRENT_TEST(alerts_dropped_alert)313 TORRENT_TEST(alerts_dropped_alert)
314 {
315 	alert_manager mgr(1, alert_category::all);
316 
317 	mgr.emplace_alert<torrent_finished_alert>(torrent_handle());
318 	mgr.emplace_alert<torrent_finished_alert>(torrent_handle());
319 	mgr.emplace_alert<torrent_finished_alert>(torrent_handle());
320 	// that last alert got dropped though, since it would have brought the queue
321 	// size to 3
322 	std::vector<alert*> alerts;
323 	mgr.get_all(alerts);
324 
325 #ifndef TORRENT_DISABLE_ALERT_MSG
326 	TEST_EQUAL(alerts.back()->message(), "dropped alerts: torrent_finished ");
327 #endif
328 	auto* a = lt::alert_cast<alerts_dropped_alert>(alerts.back());
329 	TEST_CHECK(a);
330 	TEST_CHECK(a->dropped_alerts[torrent_finished_alert::alert_type] == true);
331 }
332 
333 #ifndef TORRENT_DISABLE_EXTENSIONS
334 struct post_plugin : lt::plugin
335 {
post_pluginpost_plugin336 	explicit post_plugin(alert_manager& m) : mgr(m) {}
on_alertpost_plugin337 	void on_alert(alert const*)
338 	{
339 		if (++depth > 10) return;
340 		mgr.emplace_alert<piece_finished_alert>(torrent_handle(), piece_index_t{0});
341 	}
342 
343 	alert_manager& mgr;
344 	int depth = 0;
345 };
346 
347 // make sure the alert manager supports alerts being posted while executing a
348 // plugin handler
TORRENT_TEST(recursive_alerts)349 TORRENT_TEST(recursive_alerts)
350 {
351 	alert_manager mgr(100, alert_category::all);
352 	auto pl = std::make_shared<post_plugin>(mgr);
353 	mgr.add_extension(pl);
354 
355 	mgr.emplace_alert<piece_finished_alert>(torrent_handle(), piece_index_t{0});
356 
357 	TEST_EQUAL(pl->depth, 11);
358 }
359 
360 #endif // TORRENT_DISABLE_EXTENSIONS
361 
362