1 /*
2 
3 Copyright (c) 2013, 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 <functional>
35 #include <thread>
36 
37 #include "test.hpp"
38 #include "setup_transfer.hpp"
39 #include "libtorrent/alert_types.hpp"
40 #include "libtorrent/session_stats.hpp"
41 #include "libtorrent/performance_counters.hpp"
42 #include "libtorrent/bdecode.hpp"
43 #include "libtorrent/bencode.hpp"
44 #include "libtorrent/torrent_info.hpp"
45 #include "libtorrent/session_stats.hpp"
46 #include "settings.hpp"
47 
48 #include <fstream>
49 
50 using namespace std::placeholders;
51 using namespace lt;
52 
TORRENT_TEST(session)53 TORRENT_TEST(session)
54 {
55 	settings_pack p = settings();
56 	p.set_int(settings_pack::alert_mask, ~0);
57 	lt::session ses(p);
58 
59 	settings_pack sett = settings();
60 	sett.set_int(settings_pack::num_optimistic_unchoke_slots, 10);
61 	sett.set_int(settings_pack::unchoke_slots_limit, 10);
62 	sett.set_int(settings_pack::resolver_cache_timeout, 1000);
63 
64 	ses.apply_settings(sett);
65 
66 	// verify that we get the appropriate performance warning
67 
68 	alert const* a;
69 	for (;;)
70 	{
71 		a = wait_for_alert(ses, performance_alert::alert_type, "ses1");
72 
73 		if (a == nullptr) break;
74 		TEST_EQUAL(a->type(), performance_alert::alert_type);
75 
76 		if (alert_cast<performance_alert>(a)->warning_code
77 			== performance_alert::too_many_optimistic_unchoke_slots)
78 			break;
79 	}
80 
81 	TEST_CHECK(a);
82 
83 	sett.set_int(settings_pack::unchoke_slots_limit, 0);
84 	ses.apply_settings(sett);
85 	TEST_CHECK(ses.get_settings().get_int(settings_pack::unchoke_slots_limit) == 0);
86 
87 	sett.set_int(settings_pack::unchoke_slots_limit, -1);
88 	ses.apply_settings(sett);
89 	TEST_CHECK(ses.get_settings().get_int(settings_pack::unchoke_slots_limit) == -1);
90 
91 	sett.set_int(settings_pack::unchoke_slots_limit, 8);
92 	ses.apply_settings(sett);
93 	TEST_CHECK(ses.get_settings().get_int(settings_pack::unchoke_slots_limit) == 8);
94 
95 	TEST_EQUAL(ses.get_settings().get_int(settings_pack::resolver_cache_timeout), 1000);
96 	sett.set_int(settings_pack::resolver_cache_timeout, 1001);
97 	ses.apply_settings(sett);
98 	TEST_EQUAL(ses.get_settings().get_int(settings_pack::resolver_cache_timeout), 1001);
99 
100 	// make sure the destructor waits properly
101 	// for the asynchronous call to set the alert
102 	// mask completes, before it goes on to destruct
103 	// the session object
104 }
105 
TORRENT_TEST(async_add_torrent_duplicate_error)106 TORRENT_TEST(async_add_torrent_duplicate_error)
107 {
108 	settings_pack p = settings();
109 	p.set_int(settings_pack::alert_mask, ~0);
110 	lt::session ses(p);
111 
112 	add_torrent_params atp;
113 	atp.info_hash.assign("abababababababababab");
114 	atp.save_path = ".";
115 	ses.async_add_torrent(atp);
116 
117 	auto* a = alert_cast<add_torrent_alert>(wait_for_alert(ses, add_torrent_alert::alert_type, "ses"));
118 	TEST_CHECK(a);
119 	if (a == nullptr) return;
120 
121 	atp.flags |= torrent_flags::duplicate_is_error;
122 	ses.async_add_torrent(atp);
123 	a = alert_cast<add_torrent_alert>(wait_for_alert(ses, add_torrent_alert::alert_type, "ses"));
124 	TEST_CHECK(a);
125 	if (a == nullptr) return;
126 	TEST_CHECK(!a->handle.is_valid());
127 	TEST_CHECK(a->error);
128 }
129 
TORRENT_TEST(async_add_torrent_duplicate)130 TORRENT_TEST(async_add_torrent_duplicate)
131 {
132 	settings_pack p = settings();
133 	p.set_int(settings_pack::alert_mask, ~0);
134 	lt::session ses(p);
135 
136 	add_torrent_params atp;
137 	atp.info_hash.assign("abababababababababab");
138 	atp.save_path = ".";
139 	ses.async_add_torrent(atp);
140 
141 	auto* a = alert_cast<add_torrent_alert>(wait_for_alert(ses, add_torrent_alert::alert_type, "ses"));
142 	TEST_CHECK(a);
143 	if (a == nullptr) return;
144 	torrent_handle h = a->handle;
145 	TEST_CHECK(!a->error);
146 
147 	atp.flags &= ~torrent_flags::duplicate_is_error;
148 	ses.async_add_torrent(atp);
149 	a = alert_cast<add_torrent_alert>(wait_for_alert(ses, add_torrent_alert::alert_type, "ses"));
150 	TEST_CHECK(a);
151 	if (a == nullptr) return;
152 	TEST_CHECK(a->handle == h);
153 	TEST_CHECK(!a->error);
154 }
155 
TORRENT_TEST(async_add_torrent_duplicate_back_to_back)156 TORRENT_TEST(async_add_torrent_duplicate_back_to_back)
157 {
158 	settings_pack p = settings();
159 	p.set_int(settings_pack::alert_mask, ~0);
160 	lt::session ses(p);
161 
162 	add_torrent_params atp;
163 	atp.info_hash.assign("abababababababababab");
164 	atp.save_path = ".";
165 	atp.flags |= torrent_flags::paused;
166 	atp.flags &= ~torrent_flags::apply_ip_filter;
167 	atp.flags &= ~torrent_flags::auto_managed;
168 	ses.async_add_torrent(atp);
169 
170 	atp.flags &= ~torrent_flags::duplicate_is_error;
171 	ses.async_add_torrent(atp);
172 
173 	auto* a = alert_cast<add_torrent_alert>(wait_for_alert(ses
174 			, add_torrent_alert::alert_type, "ses", pop_alerts::cache_alerts));
175 	TEST_CHECK(a);
176 	if (a == nullptr) return;
177 	torrent_handle h = a->handle;
178 	TEST_CHECK(!a->error);
179 
180 	a = alert_cast<add_torrent_alert>(wait_for_alert(ses
181 		, add_torrent_alert::alert_type, "ses", pop_alerts::cache_alerts));
182 	TEST_CHECK(a);
183 	if (a == nullptr) return;
184 	TEST_CHECK(a->handle == h);
185 	TEST_CHECK(!a->error);
186 
187 	torrent_status st = h.status();
188 	TEST_CHECK(st.flags & torrent_flags::paused);
189 	TEST_CHECK(!(st.flags & torrent_flags::apply_ip_filter));
190 	TEST_CHECK(!(st.flags & torrent_flags::auto_managed));
191 }
192 
TORRENT_TEST(load_empty_file)193 TORRENT_TEST(load_empty_file)
194 {
195 	settings_pack p = settings();
196 	p.set_int(settings_pack::alert_mask, ~0);
197 	lt::session ses(p);
198 
199 	add_torrent_params atp;
200 	error_code ignore_errors;
201 	atp.ti = std::make_shared<torrent_info>("", std::ref(ignore_errors), from_span);
202 	atp.save_path = ".";
203 	error_code ec;
204 	torrent_handle h = ses.add_torrent(std::move(atp), ec);
205 
206 	TEST_CHECK(!h.is_valid());
207 	TEST_CHECK(ec == error_code(errors::no_metadata));
208 }
209 
TORRENT_TEST(session_stats)210 TORRENT_TEST(session_stats)
211 {
212 	std::vector<stats_metric> stats = session_stats_metrics();
213 	std::sort(stats.begin(), stats.end()
214 		, [](stats_metric const& lhs, stats_metric const& rhs)
215 		{ return lhs.value_index < rhs.value_index; });
216 
217 	TEST_EQUAL(stats.size(), lt::counters::num_counters);
218 	// make sure every stat index is represented in the stats_metric vector
219 	for (int i = 0; i < int(stats.size()); ++i)
220 	{
221 		TEST_EQUAL(stats[std::size_t(i)].value_index, i);
222 	}
223 
224 	TEST_EQUAL(lt::find_metric_idx("peer.incoming_connections")
225 		, lt::counters::incoming_connections);
226 }
227 
TORRENT_TEST(paused_session)228 TORRENT_TEST(paused_session)
229 {
230 	lt::session s(settings());
231 	s.pause();
232 
233 	lt::add_torrent_params ps;
234 	std::ofstream file("temporary");
235 	ps.ti = ::create_torrent(&file, "temporary", 16 * 1024, 13, false);
236 	ps.flags = lt::torrent_flags::paused;
237 	ps.save_path = ".";
238 
239 	torrent_handle h = s.add_torrent(std::move(ps));
240 
241 	std::this_thread::sleep_for(lt::milliseconds(2000));
242 	h.resume();
243 	std::this_thread::sleep_for(lt::milliseconds(1000));
244 
245 	TEST_CHECK(!(h.flags() & torrent_flags::paused));
246 }
247 
TORRENT_TEST(get_cache_info)248 TORRENT_TEST(get_cache_info)
249 {
250 	lt::session s(settings());
251 	lt::cache_status ret;
252 	s.get_cache_info(&ret);
253 
254 	TEST_CHECK(ret.pieces.empty());
255 #if TORRENT_ABI_VERSION == 1
256 	TEST_EQUAL(ret.blocks_written, 0);
257 	TEST_EQUAL(ret.writes, 0);
258 	TEST_EQUAL(ret.blocks_read, 0);
259 	TEST_EQUAL(ret.blocks_read_hit, 0);
260 	TEST_EQUAL(ret.reads, 0);
261 	TEST_EQUAL(ret.queued_bytes, 0);
262 	TEST_EQUAL(ret.cache_size, 0);
263 	TEST_EQUAL(ret.write_cache_size, 0);
264 	TEST_EQUAL(ret.read_cache_size, 0);
265 	TEST_EQUAL(ret.pinned_blocks, 0);
266 	TEST_EQUAL(ret.total_used_buffers, 0);
267 	TEST_EQUAL(ret.average_read_time, 0);
268 	TEST_EQUAL(ret.average_write_time, 0);
269 	TEST_EQUAL(ret.average_hash_time, 0);
270 	TEST_EQUAL(ret.average_job_time, 0);
271 	TEST_EQUAL(ret.cumulative_job_time, 0);
272 	TEST_EQUAL(ret.cumulative_read_time, 0);
273 	TEST_EQUAL(ret.cumulative_write_time, 0);
274 	TEST_EQUAL(ret.cumulative_hash_time, 0);
275 	TEST_EQUAL(ret.total_read_back, 0);
276 	TEST_EQUAL(ret.read_queue_size, 0);
277 	TEST_EQUAL(ret.blocked_jobs, 0);
278 	TEST_EQUAL(ret.queued_jobs, 0);
279 	TEST_EQUAL(ret.peak_queued, 0);
280 	TEST_EQUAL(ret.pending_jobs, 0);
281 	TEST_EQUAL(ret.num_jobs, 0);
282 	TEST_EQUAL(ret.num_read_jobs, 0);
283 	TEST_EQUAL(ret.num_write_jobs, 0);
284 	TEST_EQUAL(ret.arc_mru_size, 0);
285 	TEST_EQUAL(ret.arc_mru_ghost_size, 0);
286 	TEST_EQUAL(ret.arc_mfu_size, 0);
287 	TEST_EQUAL(ret.arc_mfu_ghost_size, 0);
288 	TEST_EQUAL(ret.arc_write_size, 0);
289 	TEST_EQUAL(ret.arc_volatile_size, 0);
290 	TEST_EQUAL(ret.num_writing_threads, 0);
291 #endif
292 }
293 
294 template <typename Set, typename Save, typename Default, typename Load>
test_save_restore(Set setup,Save s,Default d,Load l)295 void test_save_restore(Set setup, Save s, Default d, Load l)
296 {
297 	entry st;
298 	{
299 		settings_pack p = settings();
300 		setup(p);
301 		lt::session ses(p);
302 		s(ses, st);
303 	}
304 
305 	{
306 		settings_pack p = settings();
307 		d(p);
308 		lt::session ses(p);
309 		// the loading function takes a bdecode_node, so we have to transform the
310 		// entry
311 		std::printf("%s\n", st.to_string().c_str());
312 		std::vector<char> buf;
313 		bencode(std::back_inserter(buf), st);
314 		bdecode_node state;
315 		error_code ec;
316 		int ret = bdecode(buf.data(), buf.data() + buf.size()
317 			, state, ec, nullptr, 100, 1000);
318 		TEST_EQUAL(ret, 0);
319 		if (ec)
320 		{
321 			std::printf("bdecode: %s\n", ec.message().c_str());
322 			std::printf("%s\n", std::string(buf.data(), buf.size()).c_str());
323 		}
324 		TEST_CHECK(!ec);
325 		l(ses, state);
326 	}
327 }
328 
TORRENT_TEST(save_restore_state)329 TORRENT_TEST(save_restore_state)
330 {
331 	test_save_restore(
332 		[](settings_pack& p) {
333 			// set the cache size
334 			p.set_int(settings_pack::request_queue_time, 1337);
335 		},
336 		[](lt::session& ses, entry& st) {
337 			ses.save_state(st);
338 		},
339 		[](settings_pack& p) {
340 			p.set_int(settings_pack::request_queue_time, 90);
341 		},
342 		[](lt::session& ses, bdecode_node& st) {
343 			ses.load_state(st);
344 			// make sure we loaded the cache size correctly
345 			settings_pack sett = ses.get_settings();
346 			TEST_EQUAL(sett.get_int(settings_pack::request_queue_time), 1337);
347 		});
348 }
349 
TORRENT_TEST(save_restore_state_save_filter)350 TORRENT_TEST(save_restore_state_save_filter)
351 {
352 	test_save_restore(
353 		[](settings_pack& p) {
354 			// set the cache size
355 			p.set_int(settings_pack::request_queue_time, 1337);
356 		},
357 		[](lt::session& ses, entry& st) {
358 			// save everything _but_ the settings
359 			ses.save_state(st, ~session::save_settings);
360 		},
361 		[](settings_pack& p) {
362 			p.set_int(settings_pack::request_queue_time, 90);
363 		},
364 		[](lt::session& ses, bdecode_node& st) {
365 			ses.load_state(st);
366 			// make sure whatever we loaded did not include the cache size
367 			settings_pack sett = ses.get_settings();
368 			TEST_EQUAL(sett.get_int(settings_pack::request_queue_time), 90);
369 		});
370 }
371 
TORRENT_TEST(save_restore_state_load_filter)372 TORRENT_TEST(save_restore_state_load_filter)
373 {
374 	test_save_restore(
375 		[](settings_pack& p) {
376 			// set the cache size
377 			p.set_int(settings_pack::request_queue_time, 1337);
378 		},
379 		[](lt::session& ses, entry& st) {
380 			// save everything
381 			ses.save_state(st);
382 		},
383 		[](settings_pack& p) {
384 			p.set_int(settings_pack::request_queue_time, 90);
385 		},
386 		[](lt::session& ses, bdecode_node& st) {
387 			// load everything _but_ the settings
388 			ses.load_state(st, ~session::save_settings);
389 			settings_pack sett = ses.get_settings();
390 			TEST_EQUAL(sett.get_int(settings_pack::request_queue_time), 90);
391 		});
392 }
393 
TORRENT_TEST(session_shutdown)394 TORRENT_TEST(session_shutdown)
395 {
396 	lt::settings_pack pack;
397 	lt::session ses(pack);
398 }
399 
400 // make sure we don't restore peer_id from session state
TORRENT_TEST(save_state_peer_id)401 TORRENT_TEST(save_state_peer_id)
402 {
403 	lt::settings_pack pack;
404 	pack.set_str(settings_pack::peer_fingerprint, "AAA");
405 	lt::session ses(pack);
406 	TEST_EQUAL(ses.get_settings().get_str(settings_pack::peer_fingerprint), "AAA");
407 
408 	lt::entry st;
409 	ses.save_state(st);
410 
411 	pack.set_str(settings_pack::peer_fingerprint, "foobar");
412 	ses.apply_settings(pack);
413 
414 	TEST_EQUAL(ses.get_settings().get_str(settings_pack::peer_fingerprint), "foobar");
415 
416 	std::vector<char> buf;
417 	bencode(std::back_inserter(buf), st);
418 	bdecode_node state;
419 	error_code ec;
420 	int ret = bdecode(buf.data(), buf.data() + buf.size()
421 		, state, ec, nullptr, 100, 1000);
422 	TEST_EQUAL(ret, 0);
423 	ses.load_state(state);
424 
425 	TEST_EQUAL(ses.get_settings().get_str(settings_pack::peer_fingerprint), "foobar");
426 }
427 
TORRENT_TEST(pop_alert_clear)428 TORRENT_TEST(pop_alert_clear)
429 {
430 	session s;
431 
432 	// make sure the vector is cleared if there are no alerts to be popped
433 	std::vector<alert*> alerts(100);
434 
435 	for (int i = 0; i < 10; ++i)
436 	{
437 		alerts.resize(100);
438 		s.pop_alerts(&alerts);
439 		if (alerts.empty()) break;
440 	}
441 	TEST_CHECK(alerts.empty());
442 }
443 
444 namespace {
445 
test_move_session(lt::session ses)446 lt::session_proxy test_move_session(lt::session ses)
447 {
448 	std::this_thread::sleep_for(lt::milliseconds(100));
449 	auto t = ses.get_torrents();
450 	return ses.abort();
451 }
452 }
453 
TORRENT_TEST(move_session)454 TORRENT_TEST(move_session)
455 {
456 	lt::settings_pack pack;
457 	lt::session ses(pack);
458 	lt::session_proxy p = test_move_session(std::move(ses));
459 }
460 
461 #if !defined TORRENT_DISABLE_LOGGING
462 #if !defined TORRENT_DISABLE_ALERT_MSG
463 
464 #if !defined TORRENT_DISABLE_DHT
465 
466 auto const count_dht_inits = [](session& ses)
__anon94d223830f02(session& ses) 467 {
468 	int count = 0;
469 	int num = 200; // this number is adjusted per version, an estimate
470 	time_point const end_time = clock_type::now() + seconds(15);
471 	while (true)
472 	{
473 		time_point const now = clock_type::now();
474 		if (now > end_time) return count;
475 
476 		ses.wait_for_alert(end_time - now);
477 		std::vector<alert*> alerts;
478 		ses.pop_alerts(&alerts);
479 		for (auto a : alerts)
480 		{
481 			std::printf("%d: [%s] %s\n", num, a->what(), a->message().c_str());
482 			if (a->type() == log_alert::alert_type)
483 			{
484 				std::string const msg = a->message();
485 				if (msg.find("starting DHT, running: ") != std::string::npos)
486 					count++;
487 			}
488 			num--;
489 		}
490 		if (num <= 0) return count;
491 	}
492 };
493 
TORRENT_TEST(init_dht_default_bootstrap)494 TORRENT_TEST(init_dht_default_bootstrap)
495 {
496 	settings_pack p = settings();
497 	p.set_bool(settings_pack::enable_dht, true);
498 	p.set_int(settings_pack::alert_mask, alert_category::all);
499 	// default value
500 	p.set_str(settings_pack::dht_bootstrap_nodes, "dht.libtorrent.org:25401");
501 
502 	lt::session s(p);
503 
504 	int const count = count_dht_inits(s);
505 	TEST_EQUAL(count, 1);
506 }
507 
TORRENT_TEST(init_dht_invalid_bootstrap)508 TORRENT_TEST(init_dht_invalid_bootstrap)
509 {
510 	settings_pack p = settings();
511 	p.set_bool(settings_pack::enable_dht, true);
512 	p.set_int(settings_pack::alert_mask, alert_category::all);
513 	// no default value
514 	p.set_str(settings_pack::dht_bootstrap_nodes, "test.libtorrent.org:25401:8888");
515 
516 	lt::session s(p);
517 
518 	int const count = count_dht_inits(s);
519 	TEST_EQUAL(count, 1);
520 }
521 
TORRENT_TEST(init_dht_empty_bootstrap)522 TORRENT_TEST(init_dht_empty_bootstrap)
523 {
524 	settings_pack p = settings();
525 	p.set_bool(settings_pack::enable_dht, true);
526 	p.set_int(settings_pack::alert_mask, alert_category::all);
527 	// empty value
528 	p.set_str(settings_pack::dht_bootstrap_nodes, "");
529 
530 	lt::session s(p);
531 
532 	int const count = count_dht_inits(s);
533 	TEST_EQUAL(count, 1);
534 }
535 
TORRENT_TEST(dht_upload_rate_overflow_pack)536 TORRENT_TEST(dht_upload_rate_overflow_pack)
537 {
538 	settings_pack p = settings();
539 	// make sure this doesn't cause an overflow
540 	p.set_int(settings_pack::dht_upload_rate_limit, std::numeric_limits<int>::max());
541 	p.set_int(settings_pack::alert_mask, alert_category_t(std::uint32_t(p.get_int(settings_pack::alert_mask)))
542 		| alert_category::dht_log);
543 	p.set_bool(settings_pack::enable_dht, true);
544 	lt::session s(p);
545 
546 	p = s.get_settings();
547 	TEST_EQUAL(p.get_int(settings_pack::dht_upload_rate_limit), std::numeric_limits<int>::max() / 3);
548 
549 	int const count = count_dht_inits(s);
550 	TEST_EQUAL(count, 1);
551 }
552 
TORRENT_TEST(dht_upload_rate_overflow)553 TORRENT_TEST(dht_upload_rate_overflow)
554 {
555 	settings_pack p = settings();
556 	p.set_bool(settings_pack::enable_dht, true);
557 	p.set_int(settings_pack::alert_mask, alert_category_t(std::uint32_t(p.get_int(settings_pack::alert_mask)))
558 		| alert_category::dht_log);
559 	lt::session s(p);
560 
561 	// make sure this doesn't cause an overflow
562 	dht::dht_settings sett;
563 	sett.upload_rate_limit = std::numeric_limits<int>::max();
564 	s.set_dht_settings(sett);
565 
566 	p = s.get_settings();
567 	TEST_EQUAL(p.get_int(settings_pack::dht_upload_rate_limit), std::numeric_limits<int>::max() / 3);
568 
569 	int const count = count_dht_inits(s);
570 	TEST_EQUAL(count, 1);
571 }
572 
573 #endif // TORRENT_DISABLE_DHT
574 
TORRENT_TEST(reopen_network_sockets)575 TORRENT_TEST(reopen_network_sockets)
576 {
577 	auto count_alerts = [](session& ses, int const listen, int const portmap)
578 	{
579 		int count_listen = 0;
580 		int count_portmap = 0;
581 		int num = 50; // this number is adjusted per version, an estimate
582 		time_point const end_time = clock_type::now() + seconds(1);
583 		while (true)
584 		{
585 			time_point const now = clock_type::now();
586 			if (now > end_time)
587 				break;
588 
589 			ses.wait_for_alert(end_time - now);
590 			std::vector<alert*> alerts;
591 			ses.pop_alerts(&alerts);
592 			for (auto a : alerts)
593 			{
594 				std::printf("%d: [%s] %s\n", num, a->what(), a->message().c_str());
595 				std::string const msg = a->message();
596 				if (msg.find("successfully listening on") != std::string::npos)
597 					count_listen++;
598 				// upnp
599 				if (msg.find("adding port map:") != std::string::npos)
600 					count_portmap++;
601 				// natpmp
602 				if (msg.find("add-mapping: proto:") != std::string::npos)
603 					count_portmap++;
604 				num--;
605 			}
606 			if (num <= 0)
607 				break;
608 		}
609 
610 		std::printf("count_listen: %d, count_portmap: %d\n", count_listen, count_portmap);
611 		return count_listen == listen && count_portmap == portmap;
612 	};
613 
614 	settings_pack p = settings();
615 	p.set_int(settings_pack::alert_mask, alert_category::all);
616 	p.set_str(settings_pack::listen_interfaces, "127.0.0.1:6881l");
617 
618 	p.set_bool(settings_pack::enable_upnp, true);
619 	p.set_bool(settings_pack::enable_natpmp, true);
620 
621 	lt::session s(p);
622 
623 	// NAT-PMP nad UPnP will be disabled when we only listen on loopback
624 	TEST_CHECK(count_alerts(s, 2, 0));
625 
626 	// this is a bit of a pointless test now, since neither UPnP nor NAT-PMP are
627 	// enabled for loopback
628 	s.reopen_network_sockets(session_handle::reopen_map_ports);
629 
630 	TEST_CHECK(count_alerts(s, 0, 0));
631 
632 	s.reopen_network_sockets({});
633 
634 	TEST_CHECK(count_alerts(s, 0, 0));
635 
636 	p.set_bool(settings_pack::enable_upnp, false);
637 	p.set_bool(settings_pack::enable_natpmp, false);
638 	s.apply_settings(p);
639 
640 	s.reopen_network_sockets(session_handle::reopen_map_ports);
641 
642 	TEST_CHECK(count_alerts(s, 0, 0));
643 }
644 #endif // TORRENT_DISABLE_ALERT_MSG
645 #endif
646 
647