1 /*
2
3 Copyright (c) 2008, 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 <iostream>
34 #include <boost/config.hpp>
35 #include <fcntl.h>
36 #include <cstdio>
37 #include <cstdlib> // for exit()
38 #include "libtorrent/address.hpp"
39 #include "libtorrent/socket.hpp"
40 #include "setup_transfer.hpp" // for _g_test_failures
41 #include "test.hpp"
42 #include "dht_server.hpp" // for stop_dht
43 #include "peer_server.hpp" // for stop_peer
44 #include "udp_tracker.hpp" // for stop_udp_tracker
45 #include <boost/system/system_error.hpp>
46
47 #include "libtorrent/assert.hpp"
48 #include "libtorrent/aux_/path.hpp"
49 #include "libtorrent/random.hpp"
50 #include "libtorrent/aux_/escape_string.hpp"
51 #include <csignal>
52
53 #ifdef _WIN32
54 #include "libtorrent/aux_/windows.hpp" // fot SetErrorMode
55 #include <io.h> // for _dup and _dup2
56 #include <process.h> // for _getpid
57 #include <crtdbg.h>
58
59 #define dup _dup
60 #define dup2 _dup2
61
62 #else
63
64 #include <unistd.h> // for getpid()
65
66 #endif
67
68 using namespace lt;
69
70 namespace {
71
72 // these are global so we can restore them on abnormal exits and print stuff
73 // out, such as the log
74 int old_stdout = -1;
75 int old_stderr = -1;
76 bool redirect_stdout = true;
77 // sanitizer output will go to stderr and we won't get an opportunity to print
78 // it, so don't redirect stderr by default
79 bool redirect_stderr = false;
80 bool keep_files = false;
81
82 // the current tests file descriptor
83 unit_test_t* current_test = nullptr;
84
output_test_log_to_terminal()85 void output_test_log_to_terminal()
86 {
87 if (current_test == nullptr
88 || current_test->output == nullptr)
89 return;
90
91 fflush(stdout);
92 fflush(stderr);
93 if (old_stdout != -1)
94 {
95 dup2(old_stdout, fileno(stdout));
96 old_stdout = -1;
97 }
98 if (old_stderr != -1)
99 {
100 dup2(old_stderr, fileno(stderr));
101 old_stderr = -1;
102 }
103
104 fseek(current_test->output, 0, SEEK_SET);
105 std::printf("\x1b[1m[%s]\x1b[0m\n\n", current_test->name);
106 char buf[4096];
107 std::size_t size = 0;
108 do {
109 size = fread(buf, 1, sizeof(buf), current_test->output);
110 if (size > 0) fwrite(buf, 1, size, stdout);
111 } while (size > 0);
112 }
113
114 #ifdef _WIN32
seh_exception_handler(LPEXCEPTION_POINTERS p)115 LONG WINAPI seh_exception_handler(LPEXCEPTION_POINTERS p)
116 {
117 char stack_text[10000];
118
119 #if TORRENT_USE_ASSERTS \
120 || defined TORRENT_ASIO_DEBUGGING \
121 || defined TORRENT_PROFILE_CALLS \
122 || defined TORRENT_DEBUG_BUFFERS
123 print_backtrace(stack_text, sizeof(stack_text), 30
124 , p->ContextRecord);
125 #elif defined __FUNCTION__
126 strcpy(stack_text, __FUNCTION__);
127 #else
128 strcpy(stack_text, "<stack traces disabled>");
129 #endif
130
131 int const code = p->ExceptionRecord->ExceptionCode;
132 char const* name = "<unknown exception>";
133 switch (code)
134 {
135 #define EXC(x) case x: name = #x; break
136 EXC(EXCEPTION_ACCESS_VIOLATION);
137 EXC(EXCEPTION_ARRAY_BOUNDS_EXCEEDED);
138 EXC(EXCEPTION_BREAKPOINT);
139 EXC(EXCEPTION_DATATYPE_MISALIGNMENT);
140 EXC(EXCEPTION_FLT_DENORMAL_OPERAND);
141 EXC(EXCEPTION_FLT_DIVIDE_BY_ZERO);
142 EXC(EXCEPTION_FLT_INEXACT_RESULT);
143 EXC(EXCEPTION_FLT_INVALID_OPERATION);
144 EXC(EXCEPTION_FLT_OVERFLOW);
145 EXC(EXCEPTION_FLT_STACK_CHECK);
146 EXC(EXCEPTION_FLT_UNDERFLOW);
147 EXC(EXCEPTION_ILLEGAL_INSTRUCTION);
148 EXC(EXCEPTION_IN_PAGE_ERROR);
149 EXC(EXCEPTION_INT_DIVIDE_BY_ZERO);
150 EXC(EXCEPTION_INT_OVERFLOW);
151 EXC(EXCEPTION_INVALID_DISPOSITION);
152 EXC(EXCEPTION_NONCONTINUABLE_EXCEPTION);
153 EXC(EXCEPTION_PRIV_INSTRUCTION);
154 EXC(EXCEPTION_SINGLE_STEP);
155 EXC(EXCEPTION_STACK_OVERFLOW);
156 #undef EXC
157 };
158
159 std::printf("exception: (0x%x) %s caught:\n%s\n"
160 , code, name, stack_text);
161
162 output_test_log_to_terminal();
163
164 exit(code);
165 }
166
167 #endif
168
sig_handler(int sig)169 [[noreturn]] void sig_handler(int sig)
170 {
171 char stack_text[10000];
172
173 #if TORRENT_USE_ASSERTS \
174 || defined TORRENT_ASIO_DEBUGGING \
175 || defined TORRENT_PROFILE_CALLS \
176 || defined TORRENT_DEBUG_BUFFERS
177 print_backtrace(stack_text, sizeof(stack_text), 30);
178 #elif defined __FUNCTION__
179 strcpy(stack_text, __FUNCTION__);
180 #else
181 strcpy(stack_text, "<stack traces disabled>");
182 #endif
183 char const* name = "<unknown signal>";
184 switch (sig)
185 {
186 #define SIG(x) case x: name = #x; break
187 SIG(SIGSEGV);
188 #ifdef SIGBUS
189 SIG(SIGBUS);
190 #endif
191 SIG(SIGINT);
192 SIG(SIGTERM);
193 SIG(SIGILL);
194 SIG(SIGABRT);
195 SIG(SIGFPE);
196 #ifdef SIGSYS
197 SIG(SIGSYS);
198 #endif
199 #undef SIG
200 }
201 std::printf("signal: (%d) %s caught:\n%s\n", sig, name, stack_text);
202
203 output_test_log_to_terminal();
204
205 std::exit(128 + sig);
206 }
207
term_handler()208 [[noreturn]] void term_handler()
209 {
210 char stack_text[10000];
211 #if TORRENT_USE_ASSERTS \
212 || defined TORRENT_ASIO_DEBUGGING \
213 || defined TORRENT_PROFILE_CALLS \
214 || defined TORRENT_DEBUG_BUFFERS
215 print_backtrace(stack_text, sizeof(stack_text), 30);
216 #elif defined __FUNCTION__
217 strcpy(stack_text, __FUNCTION__);
218 #else
219 strcpy(stack_text, "<stack traces disabled>");
220 #endif
221 std::printf("\n\nterminate called:\n%s\n\n\n", stack_text);
222 std::exit(-1);
223 }
224
print_usage(char const * executable)225 void print_usage(char const* executable)
226 {
227 std::printf("%s [options] [tests...]\n"
228 "\n"
229 "OPTIONS:\n"
230 "-h,--help show this help\n"
231 "-l,--list list the tests available to run\n"
232 "-k,--keep keep files created by the test\n"
233 " regardless of whether it passed or not\n"
234 "-n,--no-redirect don't redirect test output to\n"
235 " temporary file, but let it go straight\n"
236 " to stdout\n"
237 "--stderr-redirect also redirect stderr in addition to stdout\n"
238 "\n"
239 "for tests, specify one or more test names as printed\n"
240 "by -l. If no test is specified, all tests are run\n", executable);
241 }
242
change_directory(std::string const & f,error_code & ec)243 void change_directory(std::string const& f, error_code& ec)
244 {
245 ec.clear();
246
247 native_path_string const n = convert_to_native_path_string(f);
248
249 #ifdef TORRENT_WINDOWS
250 if (SetCurrentDirectoryW(n.c_str()) == 0)
251 ec.assign(GetLastError(), system_category());
252 #else
253 int ret = ::chdir(n.c_str());
254 if (ret != 0)
255 ec.assign(errno, system_category());
256 #endif
257 }
258
259 } // anonymous namespace
260
261 struct unit_directory_guard
262 {
unit_directory_guardunit_directory_guard263 explicit unit_directory_guard(std::string d) : dir(std::move(d)) {}
264 unit_directory_guard(unit_directory_guard const&) = delete;
265 unit_directory_guard& operator=(unit_directory_guard const&) = delete;
~unit_directory_guardunit_directory_guard266 ~unit_directory_guard()
267 {
268 if (keep_files) return;
269 error_code ec;
270 std::string const parent_dir = parent_path(dir);
271 // windows will not allow to remove current dir, so let's change it to root
272 change_directory(parent_dir, ec);
273 if (ec)
274 {
275 TEST_ERROR("Failed to change directory: " + ec.message());
276 return;
277 }
278 remove_all(dir, ec);
279 #ifdef TORRENT_WINDOWS
280 if (ec.value() == ERROR_SHARING_VIOLATION)
281 {
282 // on windows, files are removed in the background, and we may need
283 // to wait a little bit
284 std::this_thread::sleep_for(milliseconds(400));
285 remove_all(dir, ec);
286 }
287 #endif
288 if (ec) std::cerr << "Failed to remove unit test directory: " << ec.message() << "\n";
289 }
290 private:
291 std::string dir;
292 };
293
reset_output()294 void EXPORT reset_output()
295 {
296 if (current_test == nullptr || current_test->output == nullptr) return;
297 fflush(stdout);
298 fflush(stderr);
299 rewind(current_test->output);
300 #ifdef TORRENT_WINDOWS
301 int const r = _chsize(fileno(current_test->output), 0);
302 #else
303 int const r = ftruncate(fileno(current_test->output), 0);
304 #endif
305 if (r != 0)
306 {
307 // this is best effort, it's not the end of the world if we fail
308 std::cerr << "ftruncate of temporary test output file failed: " << strerror(errno) << "\n";
309 }
310 }
311
main(int argc,char const * argv[])312 int EXPORT main(int argc, char const* argv[])
313 {
314 char const* executable = argv[0];
315 // skip executable name
316 ++argv;
317 --argc;
318
319 // pick up options
320 while (argc > 0 && argv[0][0] == '-')
321 {
322 if (argv[0] == "-h"_sv || argv[0] == "--help"_sv)
323 {
324 print_usage(executable);
325 return 0;
326 }
327
328 if (argv[0] == "-l"_sv || argv[0] == "--list"_sv)
329 {
330 std::printf("TESTS:\n");
331 for (int i = 0; i < _g_num_unit_tests; ++i)
332 {
333 std::printf(" - %s\n", _g_unit_tests[i].name);
334 }
335 return 0;
336 }
337
338 if (argv[0] == "-n"_sv || argv[0] == "--no-redirect"_sv)
339 {
340 redirect_stdout = false;
341 redirect_stderr = false;
342 }
343
344 if (argv[0] == "--stderr-redirect"_sv)
345 {
346 redirect_stderr = true;
347 }
348
349 if (argv[0] == "-k"_sv || argv[0] == "--keep"_sv)
350 {
351 keep_files = true;
352 }
353 ++argv;
354 --argc;
355 }
356
357 std::set<std::string> tests_to_run;
358 bool filter = false;
359
360 for (int i = 0; i < argc; ++i)
361 {
362 tests_to_run.insert(argv[i]);
363 filter = true;
364 }
365
366 #ifdef O_NONBLOCK
367 // on darwin, stdout is set to non-blocking mode by default
368 // which sometimes causes tests to fail with EAGAIN just
369 // by printing logs
370 int flags = fcntl(fileno(stdout), F_GETFL, 0);
371 fcntl(fileno(stdout), F_SETFL, flags & ~O_NONBLOCK);
372 flags = fcntl(fileno(stderr), F_GETFL, 0);
373 fcntl(fileno(stderr), F_SETFL, flags & ~O_NONBLOCK);
374 #endif
375
376 #ifdef _WIN32
377 // try to suppress hanging the process by windows displaying
378 // modal dialogs.
379 SetErrorMode(SEM_NOALIGNMENTFAULTEXCEPT
380 | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
381
382 SetUnhandledExceptionFilter(&seh_exception_handler);
383
384 #ifdef _DEBUG
385 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
386 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
387 #endif
388
389 #endif
390
391 std::set_terminate(term_handler);
392
393 signal(SIGSEGV, &sig_handler);
394 #ifdef SIGBUS
395 signal(SIGBUS, &sig_handler);
396 #endif
397 signal(SIGILL, &sig_handler);
398 signal(SIGINT, &sig_handler);
399 signal(SIGABRT, &sig_handler);
400 signal(SIGFPE, &sig_handler);
401 #ifdef SIGSYS
402 signal(SIGSYS, &sig_handler);
403 #endif
404
405 int process_id = -1;
406 #ifdef _WIN32
407 process_id = _getpid();
408 #else
409 process_id = getpid();
410 #endif
411 std::string const root_dir = current_working_directory();
412 std::string const unit_dir_prefix = combine_path(root_dir, "test_tmp_" + std::to_string(process_id) + "_");
413 std::printf("test: %s\ncwd_prefix = \"%s\"\nrnd = %x\n"
414 , executable, unit_dir_prefix.c_str(), lt::random(0xffffffff));
415
416 if (_g_num_unit_tests == 0)
417 {
418 std::printf("\x1b[31mTEST_ERROR: no unit tests registered\x1b[0m\n");
419 return 1;
420 }
421
422 if (redirect_stdout) old_stdout = dup(fileno(stdout));
423 if (redirect_stderr) old_stderr = dup(fileno(stderr));
424
425 int num_run = 0;
426 for (int i = 0; i < _g_num_unit_tests; ++i)
427 {
428 if (filter && tests_to_run.count(_g_unit_tests[i].name) == 0)
429 continue;
430
431 std::string const unit_dir = unit_dir_prefix + std::to_string(i);
432 error_code ec;
433 create_directory(unit_dir, ec);
434 if (ec)
435 {
436 std::printf("Failed to create unit test directory: %s\n", ec.message().c_str());
437 output_test_log_to_terminal();
438 return 1;
439 }
440 unit_directory_guard unit_dir_guard{unit_dir};
441 change_directory(unit_dir, ec);
442 if (ec)
443 {
444 std::printf("Failed to change unit test directory: %s\n", ec.message().c_str());
445 output_test_log_to_terminal();
446 return 1;
447 }
448
449 std::printf("cwd: %s\n", unit_dir.c_str());
450 unit_test_t& t = _g_unit_tests[i];
451
452 if (redirect_stdout || redirect_stderr)
453 {
454 // redirect test output to a temporary file
455 fflush(stdout);
456 fflush(stderr);
457
458 #ifdef TORRENT_MINGW
459 // mingw has a buggy tmpfile() and tmpname() that needs a . prepended
460 // to it (or some other directory)
461 char temp_name[512];
462 FILE* f = nullptr;
463 if (tmpnam_s(temp_name + 1, sizeof(temp_name) - 1) == 0)
464 {
465 temp_name[0] = '.';
466 std::printf("using temporary filename %s\n", temp_name);
467 f = fopen(temp_name, "wb+");
468 }
469 else
470 {
471 std::printf("failed to generate filename for redirecting "
472 "output: (%d) %s\n", errno, strerror(errno));
473 }
474 #else
475 FILE* f = tmpfile();
476 #endif
477 if (f != nullptr)
478 {
479 int ret1 = 0;
480 if (redirect_stdout) ret1 |= dup2(fileno(f), fileno(stdout));
481 if (redirect_stderr) ret1 |= dup2(fileno(f), fileno(stderr));
482 if (ret1 >= 0)
483 {
484 t.output = f;
485 }
486 else
487 {
488 std::printf("failed to redirect output: (%d) %s\n"
489 , errno, strerror(errno));
490 }
491 }
492 else
493 {
494 std::printf("failed to create temporary file for redirecting "
495 "output: (%d) %s\n", errno, strerror(errno));
496 }
497 }
498
499 // get proper interleaving of stderr and stdout
500 setbuf(stdout, nullptr);
501 setbuf(stderr, nullptr);
502
503 _g_test_idx = i;
504 current_test = &t;
505
506 #ifndef BOOST_NO_EXCEPTIONS
507 try
508 {
509 #endif
510
511 #if defined TORRENT_BUILD_SIMULATOR
512 lt::aux::random_engine().seed(0x82daf973);
513 #endif
514
515 _g_test_failures = 0;
516 (*t.fun)();
517 #ifndef BOOST_NO_EXCEPTIONS
518 }
519 catch (boost::system::system_error const& e)
520 {
521 char buf[200];
522 std::snprintf(buf, sizeof(buf), "TEST_ERROR: Terminated with system_error: (%d) [%s] \"%s\""
523 , e.code().value()
524 , e.code().category().name()
525 , e.code().message().c_str());
526 report_failure(buf, __FILE__, __LINE__);
527 }
528 catch (std::exception const& e)
529 {
530 char buf[200];
531 std::snprintf(buf, sizeof(buf), "TEST_ERROR: Terminated with exception: \"%s\"", e.what());
532 report_failure(buf, __FILE__, __LINE__);
533 }
534 catch (...)
535 {
536 report_failure("TEST_ERROR: Terminated with unknown exception", __FILE__, __LINE__);
537 }
538 #endif
539
540 if (!tests_to_run.empty()) tests_to_run.erase(t.name);
541
542 if (_g_test_failures > 0)
543 {
544 output_test_log_to_terminal();
545 }
546
547 t.num_failures = _g_test_failures;
548 t.run = true;
549 ++num_run;
550
551 if (redirect_stdout && t.output)
552 fclose(t.output);
553 }
554
555 if (redirect_stdout && old_stdout != -1) dup2(old_stdout, fileno(stdout));
556 if (redirect_stderr && old_stderr != -1) dup2(old_stderr, fileno(stderr));
557
558 if (!tests_to_run.empty())
559 {
560 std::printf("\x1b[1mUNKONWN tests:\x1b[0m\n");
561 for (std::set<std::string>::iterator i = tests_to_run.begin()
562 , end(tests_to_run.end()); i != end; ++i)
563 {
564 std::printf(" %s\n", i->c_str());
565 }
566 }
567
568 if (num_run == 0)
569 {
570 std::printf("\x1b[31mTEST_ERROR: no unit tests run\x1b[0m\n");
571 output_test_log_to_terminal();
572 return 1;
573 }
574
575 // just in case of premature exits
576 // make sure we try to clean up some
577 stop_udp_tracker();
578 stop_all_proxies();
579 stop_web_server();
580 stop_peer();
581 stop_dht();
582
583 if (redirect_stdout) fflush(stdout);
584 if (redirect_stderr) fflush(stderr);
585
586 return print_failures() ? 333 : 0;
587 }
588
589