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