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