tests/harness/testsuite.cc

Go to the documentation of this file.
00001 /* testsuite.cc: a test suite engine
00002  *
00003  * Copyright 1999,2000,2001 BrightStation PLC
00004  * Copyright 2002 Ananova Ltd
00005  * Copyright 2002,2003,2004,2005,2006,2007,2008 Olly Betts
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License as
00009  * published by the Free Software Foundation; either version 2 of the
00010  * License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
00020  * USA
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> // For DBL_DIG.
00049 #include <math.h> // For ceil, fabs, log10.
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 //  We use this to attempt to diagnose when the code fails to catch an
00083 //  exception when it should (due to a compiler or runtime fault in
00084 //  GCC 2.95 it seems)
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     // The path on argv[0] will always use \ for the directory separator.
00110     const char ARGV0_SEP = '\\';
00111 #else
00112     const char ARGV0_SEP = '/';
00113 #endif
00114     // Default srcdir to the pathname of argv[0].
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         // libtool may put the real executable in .libs.
00122         i = srcdir.find_last_of(ARGV0_SEP);
00123         if (srcdir.substr(i + 1) == ".libs") {
00124             srcdir.erase(i);
00125             // And it may have an "lt-" prefix.
00126             if (startswith(srcfile, "lt-")) srcfile.erase(0, 3);
00127         }
00128     } else {
00129         // No path of argv[0], so default srcdir to the current directory.
00130         // This may not work if libtool is involved as the true executable is
00131         // sometimes in ".libs".
00132         srcfile = srcdir;
00133         srcdir = ".";
00134     }
00135 
00136     // Remove any trailing ".exe" suffix, since some platforms add this.
00137     if (endswith(srcfile, ".exe")) srcfile.resize(srcfile.size() - 4);
00138 
00139     // Sanity check.
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 /* Needs C linkage so we can pass it to signal() without problems. */
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 //  A wrapper around the tests to trap exceptions,
00212 //  and avoid having to catch them in every test function.
00213 //  If this test driver is used for anything other than
00214 //  Xapian tests, then this ought to be provided by
00215 //  the client, really.
00216 //  return: test_driver::PASS, test_driver::FAIL, or test_driver::SKIP
00217 test_driver::test_result
00218 test_driver::runtest(const test_desc *test)
00219 {
00220 #ifdef HAVE_VALGRIND
00221     // This is used to make a note of how many times we've run the test
00222     volatile int runcount = 0;
00223 #endif
00224 
00225     while (true) {
00226         tout.str("");
00227         SignalRedirector sig; // use object so signal handlers are reset
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                     // Skip past any unread log output.
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                     // We must empty tout before asking valgrind to perform its
00259                     // leak checks, otherwise the buffers holding the output
00260                     // may be identified as a memory leak (especially if >1K of
00261                     // output has been buffered it appears...)
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                     // Record the current position so we can restore it so
00274                     // REPORT_FAIL_VG() gets the whole output.
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                             // Valgrind output has "==<pid>== \n" between
00285                             // report "records", so skip to the next occurrence
00286                             // of ' ' not followed by '\n'.
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                         // If code deliberately holds onto blocks by a pointer
00340                         // not to the start (e.g. languages/utilities.c does)
00341                         // then we need to rerun the test to see if the leak is
00342                         // real...
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                         // C++ STL implementations often "horde" released
00353                         // memory - for GCC 3.4 and newer the runtest script
00354                         // sets GLIBCXX_FORCE_NEW=1 which will disable this
00355                         // behaviour so we avoid this issue, but for older
00356                         // GCC and other compilers this may be an issue.
00357                         //
00358                         // See also:
00359                         // http://valgrind.org/docs/FAQ/#faq.reports
00360                         //
00361                         // For now, just use runcount to rerun the test and see
00362                         // if more is leaked - hopefully this shouldn't give
00363                         // false positives.
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                 // __cxa_demangle() apparently requires GCC >= 3.1.
00437                 // Demangle the name which GCC returns for type_info::name().
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             // caught signal
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             // if this test is "foo123" see if "foo" was listed
00519             // this way "./testprog foo" can run foo1, foo2, etc.
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                     // ignore the result of this test.
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 /* Needs C linkage so we can pass it to atexit() without problems. */
00569 extern "C" {
00570 // Call upon program exit if there's more than one test run.
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     // Report totals at the end if we reported two or more subtotals.
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                 // -h or unrecognised option
00668                 usage();
00669                 return; // usage() doesn't 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             // Open the valgrind log file, and unlink it.
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                 // Older valgrind versions named the log output differently.
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     // We need to display something before we start, or the allocation
00698     // made when the first debug message is displayed is (wrongly) picked
00699     // up on as a memory leak.
00700     DEBUGLINE(UNKNOWN, "Starting testsuite run.");
00701     om_debug.initialise();
00702 #endif /* XAPIAN_DEBUG_VERBOSE */
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); // if 0, then everything passed
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 }

Documentation for Xapian (version 1.0.10).
Generated on 24 Dec 2008 by Doxygen 1.5.2.