00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <config.h>
00024
00025 #include "testsuite.h"
00026
00027 #include "backendmanager.h"
00028 #include "fdtracker.h"
00029 #include "testrunner.h"
00030
00031 #ifdef HAVE_VALGRIND
00032 # include "safeerrno.h"
00033 # include <valgrind/memcheck.h>
00034 # include <sys/types.h>
00035 # include "safefcntl.h"
00036 # include "safeunistd.h"
00037 #endif
00038
00039 #include <algorithm>
00040 #include <iostream>
00041 #include <set>
00042
00043 #include <cfloat>
00044 #include <cmath>
00045 #include <cstdio>
00046 #include <cstdlib>
00047 #include <cstring>
00048
00049 #include "gnu_getopt.h"
00050
00051 #include <setjmp.h>
00052 #include <signal.h>
00053
00054 #include <exception>
00055 #ifdef USE_RTTI
00056 # include <typeinfo>
00057 # ifdef __GNUC__
00058 # include <cxxabi.h>
00059 # endif
00060 #endif
00061
00062 #include <xapian/error.h>
00063 #include "noreturn.h"
00064 #include "stringutils.h"
00065 #include "utils.h"
00066
00067 using namespace std;
00068
00070 bool verbose;
00071
00072 #ifdef HAVE_VALGRIND
00073 static int vg_log_fd = -1;
00074 #endif
00075
00076 #ifdef HAVE_SIGSETJMP
00077 # define SIGSETJMP(ENV, SAVESIGS) sigsetjmp(ENV, SAVESIGS)
00078 # define SIGLONGJMP(ENV, VAL) siglongjmp(ENV, VAL)
00079 # define SIGJMP_BUF sigjmp_buf
00080 #else
00081 # define SIGSETJMP(ENV, SAVESIGS) setjmp(ENV)
00082 # define SIGLONGJMP(ENV, VAL) longjmp(ENV, VAL)
00083 # define SIGJMP_BUF jmp_buf
00084 #endif
00085
00087
00088
00089
00090 const char * expected_exception = NULL;
00091
00093 std::ostringstream tout;
00094
00095 int test_driver::runs = 0;
00096 test_driver::result test_driver::subtotal;
00097 test_driver::result test_driver::total;
00098 string test_driver::argv0;
00099 string test_driver::opt_help;
00100 map<int, string *> test_driver::short_opts;
00101 vector<string> test_driver::test_names;
00102 bool test_driver::abort_on_error = false;
00103 string test_driver::col_red, test_driver::col_green;
00104 string test_driver::col_yellow, test_driver::col_reset;
00105 bool test_driver::use_cr = false;
00106
00107 void
00108 test_driver::write_and_clear_tout()
00109 {
00110 const string & s = tout.str();
00111 if (!s.empty()) {
00112 out << '\n' << s;
00113 tout.str(string());
00114 }
00115 }
00116
00117 string
00118 test_driver::get_srcdir()
00119 {
00120 char *p = getenv("srcdir");
00121 if (p != NULL) return string(p);
00122
00123 #ifdef __WIN32__
00124
00125 const char ARGV0_SEP = '\\';
00126 #else
00127 const char ARGV0_SEP = '/';
00128 #endif
00129
00130 string srcdir(argv0);
00131 string::size_type i = srcdir.find_last_of(ARGV0_SEP);
00132 string srcfile;
00133 if (i != string::npos) {
00134 srcfile = srcdir.substr(i + 1);
00135 srcdir.erase(i);
00136
00137 i = srcdir.find_last_of(ARGV0_SEP);
00138 if (srcdir.substr(i + 1) == ".libs") {
00139 srcdir.erase(i);
00140
00141 if (startswith(srcfile, "lt-")) srcfile.erase(0, 3);
00142 }
00143 } else {
00144
00145
00146
00147 srcfile = srcdir;
00148 srcdir = ".";
00149 }
00150
00151
00152 if (endswith(srcfile, ".exe")) srcfile.resize(srcfile.size() - 4);
00153
00154
00155 if (!file_exists(srcdir + '/' + srcfile + ".cc")) {
00156 cout << argv0
00157 << ": srcdir is not in the environment and I can't guess it!\n"
00158 "Run test programs using the runtest script - see HACKING for details"
00159 << endl;
00160 exit(1);
00161 }
00162 return srcdir;
00163 }
00164
00165 test_driver::test_driver(const test_desc *tests_)
00166 : out(cout.rdbuf()), tests(tests_)
00167 {
00168 }
00169
00170 static SIGJMP_BUF jb;
00171 static int signum = 0;
00172 static void * sigaddr = NULL;
00173
00174
00175 extern "C" {
00176
00177 #if defined HAVE_SIGACTION && defined SA_SIGINFO
00178 XAPIAN_NORETURN(static void handle_sig(int signum_, siginfo_t *si, void *));
00179 static void handle_sig(int signum_, siginfo_t *si, void *)
00180 {
00181
00182
00183 struct sigaction sa;
00184 sa.sa_handler = SIG_DFL;
00185 sigemptyset(&sa.sa_mask);
00186 sa.sa_flags = 0;
00187
00188
00189 if (signum_ != SIGSEGV) sigaction(SIGSEGV, &sa, NULL);
00190 if (signum_ != SIGFPE) sigaction(SIGFPE, &sa, NULL);
00191 if (signum_ != SIGILL) sigaction(SIGILL, &sa, NULL);
00192 # ifdef SIGBUS
00193 if (signum_ != SIGBUS) sigaction(SIGBUS, &sa, NULL);
00194 # endif
00195 # ifdef SIGSTKFLT
00196 if (signum_ != SIGSTKFLT) sigaction(SIGSTKFLT, &sa, NULL);
00197 # endif
00198 signum = signum_;
00199 sigaddr = si->si_addr;
00200 SIGLONGJMP(jb, 1);
00201 }
00202
00203 #else
00204
00205 XAPIAN_NORETURN(static void handle_sig(int signum_));
00206 static void handle_sig(int signum_)
00207 {
00208
00209
00210 signal(SIGSEGV, SIG_DFL);
00211 signal(SIGFPE, SIG_DFL);
00212 signal(SIGILL, SIG_DFL);
00213 #ifdef SIGBUS
00214 signal(SIGBUS, SIG_DFL);
00215 #endif
00216 #ifdef SIGSTKFLT
00217 signal(SIGSTKFLT, SIG_DFL);
00218 #endif
00219 signum = signum_;
00220 SIGLONGJMP(jb, 1);
00221 }
00222 #endif
00223
00224 }
00225
00226 class SignalRedirector {
00227 private:
00228 bool active;
00229 public:
00230 SignalRedirector() : active(false) { }
00231 void activate() {
00232 active = true;
00233 signum = 0;
00234 sigaddr = NULL;
00235
00236 #if defined HAVE_SIGACTION && defined SA_SIGINFO
00237 struct sigaction sa;
00238 sa.sa_sigaction = handle_sig;
00239 sigemptyset(&sa.sa_mask);
00240 sa.sa_flags = SA_RESETHAND|SA_SIGINFO;
00241 sigaction(SIGSEGV, &sa, NULL);
00242 sigaction(SIGFPE, &sa, NULL);
00243 sigaction(SIGILL, &sa, NULL);
00244 # ifdef SIGBUS
00245 sigaction(SIGBUS, &sa, NULL);
00246 # endif
00247 # ifdef SIGSTKFLT
00248 sigaction(SIGSTKFLT, &sa, NULL);
00249 # endif
00250 #else
00251 signal(SIGSEGV, handle_sig);
00252 signal(SIGFPE, handle_sig);
00253 signal(SIGILL, handle_sig);
00254 # ifdef SIGBUS
00255 signal(SIGBUS, handle_sig);
00256 # endif
00257 # ifdef SIGSTKFLT
00258 signal(SIGSTKFLT, handle_sig);
00259 # endif
00260 #endif
00261 }
00262 ~SignalRedirector() {
00263 if (active) {
00264 #if defined HAVE_SIGACTION && defined SA_SIGINFO
00265 struct sigaction sa;
00266 sa.sa_handler = SIG_DFL;
00267 sigemptyset(&sa.sa_mask);
00268 sa.sa_flags = 0;
00269 sigaction(SIGSEGV, &sa, NULL);
00270 sigaction(SIGFPE, &sa, NULL);
00271 sigaction(SIGILL, &sa, NULL);
00272 # ifdef SIGBUS
00273 sigaction(SIGBUS, &sa, NULL);
00274 # endif
00275 # ifdef SIGSTKFLT
00276 sigaction(SIGSTKFLT, &sa, NULL);
00277 # endif
00278 #else
00279 signal(SIGSEGV, SIG_DFL);
00280 signal(SIGFPE, SIG_DFL);
00281 signal(SIGILL, SIG_DFL);
00282 # ifdef SIGBUS
00283 signal(SIGBUS, SIG_DFL);
00284 # endif
00285 # ifdef SIGSTKFLT
00286 signal(SIGSTKFLT, SIG_DFL);
00287 # endif
00288 #endif
00289 }
00290 }
00291 };
00292
00293
00294
00295
00296
00297
00298
00299 test_driver::test_result
00300 test_driver::runtest(const test_desc *test)
00301 {
00302
00303 volatile int runcount = 0;
00304
00305 FDTracker fdtracker;
00306 fdtracker.init();
00307
00308 while (true) {
00309 tout.str(string());
00310 if (SIGSETJMP(jb, 1) == 0) {
00311 SignalRedirector sig;
00312 static bool catch_signals =
00313 (getenv("XAPIAN_TESTSUITE_SIG_DFL") == NULL);
00314 if (catch_signals) sig.activate();
00315 try {
00316 expected_exception = NULL;
00317 #ifdef HAVE_VALGRIND
00318 int vg_errs = 0;
00319 long vg_leaks = 0, vg_dubious = 0, vg_reachable = 0;
00320 if (vg_log_fd != -1) {
00321 VALGRIND_DO_LEAK_CHECK;
00322 vg_errs = VALGRIND_COUNT_ERRORS;
00323 long dummy;
00324 VALGRIND_COUNT_LEAKS(vg_leaks, vg_dubious, vg_reachable, dummy);
00325 (void)dummy;
00326
00327 lseek(vg_log_fd, 0, SEEK_END);
00328 }
00329 #endif
00330 if (!test->run()) {
00331 out << col_red << " FAILED" << col_reset;
00332 write_and_clear_tout();
00333 return FAIL;
00334 }
00335 if (backendmanager)
00336 backendmanager->clean_up();
00337 #ifdef HAVE_VALGRIND
00338 if (vg_log_fd != -1) {
00339
00340
00341
00342
00343 tout.str(string());
00344 #define REPORT_FAIL_VG(M) do { \
00345 if (verbose) { \
00346 while (true) { \
00347 ssize_t c = read(vg_log_fd, buf, sizeof(buf)); \
00348 if (c == 0 || (c < 0 && errno != EINTR)) break; \
00349 if (c > 0) out << string(buf, c); \
00350 } \
00351 } \
00352 out << " " << col_red << M << col_reset; \
00353 } while (0)
00354
00355
00356 off_t curpos = lseek(vg_log_fd, 0, SEEK_CUR);
00357 char buf[1024];
00358 while (true) {
00359 ssize_t c = read(vg_log_fd, buf, sizeof(buf));
00360 if (c == 0 || (c < 0 && errno != EINTR)) {
00361 buf[0] = 0;
00362 break;
00363 }
00364 if (c > 0) {
00365
00366
00367
00368 ssize_t i = 0;
00369 do {
00370 const char * spc;
00371 spc = static_cast<const char *>(
00372 memchr(buf + i, ' ', c - i));
00373 if (!spc) {
00374 i = c;
00375 break;
00376 }
00377 i = spc - buf;
00378 } while (++i < c && buf[i] == '\n');
00379
00380 char *start = buf + i;
00381 c -= i;
00382 if (c > 128) c = 128;
00383
00384 {
00385 const char *p;
00386 p = static_cast<const char*>(
00387 memchr(start, '\n', c));
00388 if (p != NULL) c = p - start;
00389 }
00390
00391 memmove(buf, start, c);
00392 buf[c] = '\0';
00393 break;
00394 }
00395 }
00396 lseek(vg_log_fd, curpos, SEEK_SET);
00397
00398 int vg_errs2 = VALGRIND_COUNT_ERRORS;
00399 vg_errs = vg_errs2 - vg_errs;
00400 VALGRIND_DO_LEAK_CHECK;
00401 long vg_leaks2 = 0, vg_dubious2 = 0, vg_reachable2 = 0;
00402 long dummy;
00403 VALGRIND_COUNT_LEAKS(vg_leaks2, vg_dubious2, vg_reachable2,
00404 dummy);
00405 (void)dummy;
00406 vg_leaks = vg_leaks2 - vg_leaks;
00407 vg_dubious = vg_dubious2 - vg_dubious;
00408 vg_reachable = vg_reachable2 - vg_reachable;
00409 if (vg_errs) {
00410 string fail_msg(buf);
00411 if (fail_msg.empty())
00412 fail_msg = "VALGRIND DETECTED A PROBLEM";
00413 REPORT_FAIL_VG(fail_msg);
00414 return FAIL;
00415 }
00416 if (vg_leaks > 0) {
00417 REPORT_FAIL_VG("LEAKED " << vg_leaks << " BYTES");
00418 return FAIL;
00419 }
00420 if (vg_dubious > 0) {
00421
00422
00423
00424
00425 if (runcount == 0) {
00426 out << col_yellow << " PROBABLY LEAKED MEMORY - RETRYING TEST" << col_reset;
00427 ++runcount;
00428
00429
00430 (void)fdtracker.check();
00431 continue;
00432 }
00433 REPORT_FAIL_VG("PROBABLY LEAKED " << vg_dubious << " BYTES");
00434 return FAIL;
00435 }
00436 if (vg_reachable > 0) {
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449 if (runcount == 0) {
00450 out << col_yellow << " POSSIBLE UNRELEASED MEMORY - RETRYING TEST" << col_reset;
00451 ++runcount;
00452
00453
00454 (void)fdtracker.check();
00455 continue;
00456 }
00457 REPORT_FAIL_VG("FAILED TO RELEASE " << vg_reachable << " BYTES");
00458 return FAIL;
00459 }
00460 }
00461 #endif
00462 if (!fdtracker.check()) {
00463 if (runcount == 0) {
00464 out << col_yellow << " POSSIBLE FDLEAK:" << fdtracker.get_message() << col_reset;
00465 ++runcount;
00466 continue;
00467 }
00468 out << col_red << " FDLEAK:" << fdtracker.get_message() << col_reset;
00469 return FAIL;
00470 }
00471 } catch (const TestFail &) {
00472 out << col_red << " FAILED" << col_reset;
00473 write_and_clear_tout();
00474 return FAIL;
00475 } catch (const TestSkip &) {
00476 out << col_yellow << " SKIPPED" << col_reset;
00477 write_and_clear_tout();
00478 return SKIP;
00479 } catch (const Xapian::Error &err) {
00480 string errclass = err.get_type();
00481 if (expected_exception && expected_exception == errclass) {
00482 out << col_yellow << " C++ FAILED TO CATCH " << errclass << col_reset;
00483 return SKIP;
00484 }
00485 out << " " << col_red << err.get_description() << col_reset;
00486 write_and_clear_tout();
00487 return FAIL;
00488 } catch (const string & msg) {
00489 out << col_red << " EXCEPTION std::string " << msg << col_reset;
00490 write_and_clear_tout();
00491 return FAIL;
00492 } catch (const std::exception & e) {
00493 out << " " << col_red;
00494 #ifndef USE_RTTI
00495 out << "std::exception";
00496 #else
00497 const char * name = typeid(e).name();
00498 # ifdef __GNUC__
00499
00500
00501 int status;
00502 char * realname = abi::__cxa_demangle(name, NULL, 0, &status);
00503 if (realname) {
00504 out << realname;
00505 free(realname);
00506 } else {
00507 out << name;
00508 }
00509 # else
00510 out << name;
00511 # endif
00512 #endif
00513 out << ": " << e.what() << col_reset;
00514 write_and_clear_tout();
00515 return FAIL;
00516 } catch (const char * msg) {
00517 out << col_red;
00518 if (msg) {
00519 out << " EXCEPTION char * " << msg;
00520 } else {
00521 out << " EXCEPTION (char*)NULL";
00522 }
00523 out << col_reset;
00524 write_and_clear_tout();
00525 return FAIL;
00526 } catch (...) {
00527 out << col_red << " UNKNOWN EXCEPTION" << col_reset;
00528 write_and_clear_tout();
00529 return FAIL;
00530 }
00531 return PASS;
00532 }
00533
00534
00535 const char *signame = "SIGNAL";
00536 bool show_addr = true;
00537 switch (signum) {
00538 case SIGSEGV: signame = "SIGSEGV"; break;
00539 case SIGFPE: signame = "SIGFPE"; break;
00540 case SIGILL: signame = "SIGILL"; break;
00541 #ifdef SIGBUS
00542 case SIGBUS: signame = "SIGBUS"; break;
00543 #endif
00544 #ifdef SIGSTKFLT
00545 case SIGSTKFLT:
00546 signame = "SIGSTKFLT";
00547 show_addr = false;
00548 break;
00549 #endif
00550 }
00551 out << " " << col_red << signame;
00552 if (show_addr) {
00553 char buf[40];
00554 sprintf(buf, " at %p", sigaddr);
00555 out << buf;
00556 }
00557 out << col_reset;
00558 write_and_clear_tout();
00559 return FAIL;
00560 }
00561 }
00562
00563 test_driver::result
00564 test_driver::run_tests(vector<string>::const_iterator b,
00565 vector<string>::const_iterator e)
00566 {
00567 return do_run_tests(b, e);
00568 }
00569
00570 test_driver::result
00571 test_driver::run_tests()
00572 {
00573 const vector<string> blank;
00574 return do_run_tests(blank.begin(), blank.end());
00575 }
00576
00577 test_driver::result
00578 test_driver::do_run_tests(vector<string>::const_iterator b,
00579 vector<string>::const_iterator e)
00580 {
00581 set<string> m(b, e);
00582 bool check_name = !m.empty();
00583
00584 test_driver::result res;
00585
00586 for (const test_desc *test = tests; test->name; test++) {
00587 bool do_this_test = !check_name;
00588 if (!do_this_test && m.find(test->name) != m.end())
00589 do_this_test = true;
00590 if (!do_this_test) {
00591
00592
00593 string t = test->name;
00594 string::size_type i;
00595 i = t.find_last_not_of("0123456789") + 1;
00596 if (i != string::npos) {
00597 t.resize(i);
00598 if (m.find(t) != m.end()) do_this_test = true;
00599 }
00600 }
00601 if (do_this_test) {
00602 out << "Running test: " << test->name << "...";
00603 out.flush();
00604 test_driver::test_result test_res = runtest(test);
00605 if (backendmanager)
00606 backendmanager->clean_up();
00607 switch (test_res) {
00608 case PASS:
00609 ++res.succeeded;
00610 if (verbose || !use_cr) {
00611 out << col_green << " ok" << col_reset << endl;
00612 } else {
00613 out << "\r \r";
00614 }
00615 break;
00616 case FAIL:
00617 ++res.failed;
00618 out << endl;
00619 if (abort_on_error) {
00620 throw "Test failed - aborting further tests";
00621 }
00622 break;
00623 case SKIP:
00624 ++res.skipped;
00625 out << endl;
00626
00627 break;
00628 }
00629 }
00630 }
00631 return res;
00632 }
00633
00634 void
00635 test_driver::usage()
00636 {
00637 cout << "Usage: " << argv0 << " [-v|--verbose] [-o|--abort-on-error] " << opt_help
00638 << "[TESTNAME]..." << endl;
00639 cout << " " << argv0 << " [-h|--help]" << endl;
00640 exit(1);
00641 }
00642
00643
00644 extern "C" {
00645
00646 static void
00647 report_totals(void)
00648 {
00649 test_driver::report(test_driver::total, "total");
00650 }
00651 }
00652
00653 void
00654 test_driver::report(const test_driver::result &r, const string &desc)
00655 {
00656
00657 if (++runs == 2) atexit(report_totals);
00658
00659 if (r.succeeded != 0 || r.failed != 0) {
00660 cout << argv0 << " " << desc << ": ";
00661
00662 if (r.failed == 0)
00663 cout << "All ";
00664
00665 cout << col_green << r.succeeded << col_reset << " tests passed";
00666
00667 if (r.failed != 0)
00668 cout << ", " << col_red << r.failed << col_reset << " failed";
00669
00670 if (r.skipped) {
00671 cout << ", " << col_yellow << r.skipped << col_reset
00672 << " skipped." << endl;
00673 } else {
00674 cout << "." << endl;
00675 }
00676 }
00677 }
00678
00679 void
00680 test_driver::add_command_line_option(const string &l, char s, string * arg)
00681 {
00682 short_opts.insert(make_pair<int, string *>(int(s), arg));
00683 opt_help += "[-";
00684 opt_help += s;
00685 opt_help += ' ';
00686 opt_help += l;
00687 opt_help += "] ";
00688 }
00689
00690 void
00691 test_driver::parse_command_line(int argc, char **argv)
00692 {
00693 argv0 = argv[0];
00694
00695 #ifndef __WIN32__
00696 bool colourise = true;
00697 const char *p = getenv("XAPIAN_TESTSUITE_OUTPUT");
00698 if (p == NULL || !*p || strcmp(p, "auto") == 0) {
00699 colourise = isatty(1);
00700 } else if (strcmp(p, "plain") == 0) {
00701 colourise = false;
00702 }
00703 if (colourise) {
00704 col_red = "\x1b[1m\x1b[31m";
00705 col_green = "\x1b[1m\x1b[32m";
00706 col_yellow = "\x1b[1m\x1b[33m";
00707 col_reset = "\x1b[0m";
00708 use_cr = true;
00709 }
00710 #endif
00711
00712 const struct option long_opts[] = {
00713 {"verbose", no_argument, 0, 'v'},
00714 {"abort-on-error", no_argument, 0, 'o'},
00715 {"help", no_argument, 0, 'h'},
00716 {NULL, 0, 0, 0}
00717 };
00718
00719 string short_opts_string = "voh";
00720 map<int, string *>::const_iterator i;
00721 for (i = short_opts.begin(); i != short_opts.end(); ++i) {
00722 short_opts_string += char(i->first);
00723 short_opts_string += ':';
00724 }
00725 const char * opts = short_opts_string.c_str();
00726
00727 int c;
00728 while ((c = gnu_getopt_long(argc, argv, opts, long_opts, 0)) != -1) {
00729 switch (c) {
00730 case 'v':
00731 verbose = true;
00732 break;
00733 case 'o':
00734 abort_on_error = true;
00735 break;
00736 default: {
00737 i = short_opts.find(c);
00738 if (i != short_opts.end()) {
00739 i->second->assign(optarg);
00740 break;
00741 }
00742
00743 usage();
00744 return;
00745 }
00746 }
00747 }
00748
00749 while (argv[optind]) {
00750 test_names.push_back(string(argv[optind]));
00751 optind++;
00752 }
00753
00754 #ifdef HAVE_VALGRIND
00755 if (RUNNING_ON_VALGRIND) {
00756 if (getenv("XAPIAN_TESTSUITE_VALGRIND") != NULL) {
00757
00758 char fname[64];
00759 sprintf(fname, ".valgrind.log.%lu", (unsigned long)getpid());
00760 vg_log_fd = open(fname, O_RDONLY|O_NONBLOCK);
00761 if (vg_log_fd == -1 && errno == ENOENT) {
00762
00763 sprintf(fname, ".valgrind.log.pid%lu", (unsigned long)getpid());
00764 vg_log_fd = open(fname, O_RDONLY|O_NONBLOCK);
00765 }
00766 if (vg_log_fd != -1) unlink(fname);
00767 }
00768 }
00769 #endif
00770 }
00771
00772 int
00773 test_driver::run(const test_desc *tests)
00774 {
00775 test_driver driver(tests);
00776
00777 test_driver::result myresult;
00778 myresult = driver.run_tests(test_names.begin(), test_names.end());
00779
00780 subtotal += myresult;
00781
00782 return bool(myresult.failed);
00783 }
00784
00785 bool
00786 TEST_EQUAL_DOUBLE_(double a, double b)
00787 {
00788 if (a == b) return true;
00789 return (ceil(log10(max(fabs(a), fabs(b)))) - log10(fabs(a - b)) > DBL_DIG);
00790 }