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