xapian-core  1.4.25
testsuite.cc
Go to the documentation of this file.
1 
4 /* Copyright 1999,2000,2001 BrightStation PLC
5  * Copyright 2002 Ananova Ltd
6  * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2012,2013,2015,2016,2017 Olly Betts
7  * Copyright 2007 Richard Boulton
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22  * USA
23  */
24 
25 #include <config.h>
26 
27 #include "testsuite.h"
28 
29 #ifndef NO_LIBXAPIAN
30 # include "backendmanager.h"
31 #endif
32 #include "fdtracker.h"
33 #include "testrunner.h"
34 #include "safeunistd.h"
35 
36 #ifdef HAVE_VALGRIND
37 # include <valgrind/memcheck.h>
38 # include <sys/types.h>
39 # include "safefcntl.h"
40 #endif
41 
42 #include <algorithm>
43 #include <ios>
44 #include <iostream>
45 #include <set>
46 
47 #include <cerrno>
48 #include <cfloat> // For DBL_DIG.
49 #include <cmath> // For ceil, fabs, log10.
50 #include <cstdio>
51 #include <cstdlib>
52 #include <cstring>
53 
54 #include "gnu_getopt.h"
55 
56 #include <setjmp.h>
57 #include <signal.h>
58 
59 #include <exception>
60 #ifdef USE_RTTI
61 # include <typeinfo>
62 # ifdef HAVE_CXXABI_H
63 # include <cxxabi.h>
64 # endif
65 #endif
66 
67 #ifndef NO_LIBXAPIAN
68 # include <xapian/error.h>
69 #endif
70 #include "errno_to_string.h"
71 #include "filetests.h"
72 #include "noreturn.h"
73 #include "stringutils.h"
74 
75 using namespace std;
76 
78 int verbose;
79 
80 #ifdef HAVE_VALGRIND
81 static int vg_log_fd = -1;
82 #endif
83 
84 #if HAVE_DECL_SIGSETJMP && HAVE_DECL_SIGLONGJMP
85 # define SIGSETJMP(ENV, SAVESIGS) sigsetjmp(ENV, SAVESIGS)
86 # define SIGLONGJMP(ENV, VAL) siglongjmp(ENV, VAL)
87 # define SIGJMP_BUF sigjmp_buf
88 #else
89 # define SIGSETJMP(ENV, SAVESIGS) setjmp(ENV)
90 # define SIGLONGJMP(ENV, VAL) longjmp(ENV, VAL)
91 # define SIGJMP_BUF jmp_buf
92 #endif
93 
95 // We use this to attempt to diagnose when the code fails to catch an
96 // exception when it should (due to a compiler or runtime fault in
97 // GCC 2.95 it seems)
98 const char * expected_exception = NULL;
99 
100 const char* expected_failure;
101 
103 std::ostringstream tout;
104 
105 int test_driver::runs = 0;
108 string test_driver::argv0;
109 string test_driver::opt_help;
110 map<int, string *> test_driver::short_opts;
111 vector<string> test_driver::test_names;
112 bool test_driver::abort_on_error = false;
115 bool test_driver::use_cr = false;
116 
117 void
119 {
120  const string & s = tout.str();
121  if (!s.empty()) {
122  out << '\n' << s;
123  tout.str(string());
124  }
125 }
126 
127 string
129 {
130  char *p = getenv("srcdir");
131  if (p != NULL) return string(p);
132 
133  // Default srcdir to the pathname of argv[0].
134  string srcdir(argv0);
135  string::size_type i = srcdir.find_last_of(DIR_SEPS);
136  string srcfile;
137  if (i != string::npos) {
138  srcfile.assign(srcdir, i + 1, string::npos);
139  srcdir.erase(i);
140  // libtool may put the real executable in .libs.
141  i = srcdir.find_last_of(DIR_SEPS);
142  if (srcdir.substr(i + 1) == ".libs") {
143  srcdir.erase(i);
144  // And it may have an "lt-" prefix.
145  if (startswith(srcfile, "lt-")) srcfile.erase(0, 3);
146  }
147  } else {
148  // No path of argv[0], so default srcdir to the current directory.
149  // This may not work if libtool is involved as the true executable is
150  // sometimes in ".libs".
151  srcfile = srcdir;
152  srcdir = ".";
153  }
154 
155  // Remove any trailing ".exe" suffix, since some platforms add this.
156  if (endswith(srcfile, ".exe")) srcfile.resize(srcfile.size() - 4);
157 
158  // Sanity check.
159  if (!file_exists(srcdir + '/' + srcfile + ".cc")) {
160  cout << argv0
161  << ": srcdir is not in the environment and I can't guess it!\n"
162  "Run test programs using the runtest script - see HACKING "
163  "for details\n";
164  exit(1);
165  }
166  return srcdir;
167 }
168 
170  : out(cout.rdbuf()), tests(tests_)
171 {
172  tout << boolalpha;
173 }
174 
175 static SIGJMP_BUF jb;
176 static int signum = 0;
177 static void * sigaddr = NULL;
178 
179 // Needs C linkage so we can pass it to sigaction()/signal() without problems.
180 extern "C" {
181 
182 #if defined HAVE_SIGACTION && defined SA_SIGINFO
183 XAPIAN_NORETURN(static void handle_sig(int signum_, siginfo_t *si, void *));
184 static void handle_sig(int signum_, siginfo_t *si, void *)
185 {
186  // Disable all our signal handlers to avoid problems if the signal
187  // handling code causes a signal.
188  struct sigaction sa;
189  sa.sa_handler = SIG_DFL;
190  sigemptyset(&sa.sa_mask);
191  sa.sa_flags = 0;
192  // We set the handlers with SA_RESETHAND, but that will only reset the
193  // handler for the signal which fired.
194  if (signum_ != SIGSEGV) sigaction(SIGSEGV, &sa, NULL);
195  if (signum_ != SIGFPE) sigaction(SIGFPE, &sa, NULL);
196  if (signum_ != SIGILL) sigaction(SIGILL, &sa, NULL);
197 # ifdef SIGBUS
198  if (signum_ != SIGBUS) sigaction(SIGBUS, &sa, NULL);
199 # endif
200 # ifdef SIGSTKFLT
201  if (signum_ != SIGSTKFLT) sigaction(SIGSTKFLT, &sa, NULL);
202 # endif
203  signum = signum_;
204  sigaddr = si->si_addr;
205  SIGLONGJMP(jb, 1);
206 }
207 
208 #else
209 
210 XAPIAN_NORETURN(static void handle_sig(int signum_));
211 static void handle_sig(int signum_)
212 {
213  // Disable all our signal handlers to avoid problems if the signal
214  // handling code causes a signal.
215  signal(SIGSEGV, SIG_DFL);
216  signal(SIGFPE, SIG_DFL);
217  signal(SIGILL, SIG_DFL);
218 #ifdef SIGBUS
219  signal(SIGBUS, SIG_DFL);
220 #endif
221 #ifdef SIGSTKFLT
222  signal(SIGSTKFLT, SIG_DFL);
223 #endif
224  signum = signum_;
225  SIGLONGJMP(jb, 1);
226 }
227 #endif
228 
229 }
230 
232  private:
233  bool active;
234  public:
235  SignalRedirector() : active(false) { }
236  void activate() {
237  active = true;
238  signum = 0;
239  sigaddr = NULL;
240  // SA_SIGINFO is not universal (e.g. not present on Linux < 2.2 or
241  // older Hurd). If we have it, we use it to report the address
242  // associated with the signal (for signals where that makes sense).
243 #if defined HAVE_SIGACTION && defined SA_SIGINFO
244  struct sigaction sa;
245  sa.sa_sigaction = handle_sig;
246  sigemptyset(&sa.sa_mask);
247  sa.sa_flags = SA_RESETHAND|SA_SIGINFO;
248  sigaction(SIGSEGV, &sa, NULL);
249  sigaction(SIGFPE, &sa, NULL);
250  sigaction(SIGILL, &sa, NULL);
251 # ifdef SIGBUS
252  sigaction(SIGBUS, &sa, NULL);
253 # endif
254 # ifdef SIGSTKFLT
255  sigaction(SIGSTKFLT, &sa, NULL);
256 # endif
257 #else
258  signal(SIGSEGV, handle_sig);
259  signal(SIGFPE, handle_sig);
260  signal(SIGILL, handle_sig);
261 # ifdef SIGBUS
262  signal(SIGBUS, handle_sig);
263 # endif
264 # ifdef SIGSTKFLT
265  signal(SIGSTKFLT, handle_sig);
266 # endif
267 #endif
268  }
270  if (active) {
271 #if defined HAVE_SIGACTION && defined SA_SIGINFO
272  struct sigaction sa;
273  sa.sa_handler = SIG_DFL;
274  sigemptyset(&sa.sa_mask);
275  sa.sa_flags = 0;
276  sigaction(SIGSEGV, &sa, NULL);
277  sigaction(SIGFPE, &sa, NULL);
278  sigaction(SIGILL, &sa, NULL);
279 # ifdef SIGBUS
280  sigaction(SIGBUS, &sa, NULL);
281 # endif
282 # ifdef SIGSTKFLT
283  sigaction(SIGSTKFLT, &sa, NULL);
284 # endif
285 #else
286  signal(SIGSEGV, SIG_DFL);
287  signal(SIGFPE, SIG_DFL);
288  signal(SIGILL, SIG_DFL);
289 # ifdef SIGBUS
290  signal(SIGBUS, SIG_DFL);
291 # endif
292 # ifdef SIGSTKFLT
293  signal(SIGSTKFLT, SIG_DFL);
294 # endif
295 #endif
296  }
297  }
298 };
299 
300 // A wrapper around the tests to trap exceptions,
301 // and avoid having to catch them in every test function.
302 // If this test driver is used for anything other than
303 // Xapian tests, then this ought to be provided by
304 // the client, really.
305 // return: test_driver::PASS, test_driver::FAIL, test_driver::SKIP,
306 // test_driver::XFAIL or test_driver:XPASS.
309 {
310  // This is used to make a note of how many times we've run the test
311  volatile int runcount = 0;
312 
313  FDTracker fdtracker;
314  fdtracker.init();
315 
316  while (true) {
317  tout.str(string());
318  if (SIGSETJMP(jb, 1) == 0) {
319  SignalRedirector sig; // use object so signal handlers are reset
320  static bool catch_signals =
321  (getenv("XAPIAN_TESTSUITE_SIG_DFL") == NULL);
322  if (catch_signals) sig.activate();
323  try {
324  expected_exception = NULL;
325  expected_failure = NULL;
326 #ifdef HAVE_VALGRIND
327  int vg_errs = 0;
328  long vg_leaks = 0, vg_dubious = 0, vg_reachable = 0;
329  if (vg_log_fd != -1) {
330  VALGRIND_DO_LEAK_CHECK;
331  vg_errs = VALGRIND_COUNT_ERRORS;
332  long dummy;
333  VALGRIND_COUNT_LEAKS(vg_leaks, vg_dubious, vg_reachable, dummy);
334  (void)dummy;
335  // Skip past any unread log output.
336  lseek(vg_log_fd, 0, SEEK_END);
337  }
338 #endif
339  test->run();
340  if (verbose > 1)
342 #ifndef NO_LIBXAPIAN
343  if (backendmanager)
345 #endif
346 #ifdef HAVE_VALGRIND
347  if (vg_log_fd != -1) {
348  // We must empty tout before asking valgrind to perform its
349  // leak checks, otherwise the buffers holding the output
350  // may be identified as a memory leak (especially if >1K of
351  // output has been buffered it appears...)
352  tout.str(string());
353 #define REPORT_FAIL_VG(M) do { \
354  if (verbose) { \
355  while (true) { \
356  ssize_t c = read(vg_log_fd, buf, sizeof(buf)); \
357  if (c == 0 || (c < 0 && errno != EINTR)) break; \
358  if (c > 0) out << string(buf, c); \
359  } \
360  } \
361  out << " " << col_red << M << col_reset; \
362 } while (0)
363  // Record the current position so we can restore it so
364  // REPORT_FAIL_VG() gets the whole output.
365  off_t curpos = lseek(vg_log_fd, 0, SEEK_CUR);
366  char buf[4096];
367  while (true) {
368  ssize_t c = read(vg_log_fd, buf, sizeof(buf));
369  if (c == 0 || (c < 0 && errno != EINTR)) {
370  buf[0] = 0;
371  break;
372  }
373  if (c > 0) {
374  // Valgrind output has "==<pid>== \n" between
375  // report "records", so skip any lines like that,
376  // and also any warnings and continuation lines.
377  ssize_t i = 0;
378  while (true) {
379  const char * spc;
380  spc = static_cast<const char *>(
381  memchr(buf + i, ' ', c - i));
382  if (!spc) {
383  i = c;
384  break;
385  }
386  i = spc - buf;
387  if (++i >= c) break;
388  if (buf[i] == '\n')
389  continue;
390  if (c - i >= 8 &&
391  (memcmp(buf + i, "Warning:", 8) == 0 ||
392  memcmp(buf + i, " ", 3) == 0)) {
393  // Skip this line.
394  i += 3;
395  const char * nl;
396  nl = static_cast<const char *>(
397  memchr(buf + i, '\n', c - i));
398  if (!nl) {
399  i = c;
400  break;
401  }
402  i = nl - buf;
403  continue;
404  }
405  break;
406  }
407 
408  char *start = buf + i;
409  c -= i;
410  if (c > 128) c = 128;
411 
412  {
413  const char *p;
414  p = static_cast<const char*>(
415  memchr(start, '\n', c));
416  if (p != NULL) c = p - start;
417  }
418 
419  memmove(buf, start, c);
420  buf[c] = '\0';
421  break;
422  }
423  }
424  lseek(vg_log_fd, curpos, SEEK_SET);
425 
426  int vg_errs2 = VALGRIND_COUNT_ERRORS;
427  vg_errs = vg_errs2 - vg_errs;
428  VALGRIND_DO_LEAK_CHECK;
429  long vg_leaks2 = 0, vg_dubious2 = 0, vg_reachable2 = 0;
430  long dummy;
431  VALGRIND_COUNT_LEAKS(vg_leaks2, vg_dubious2, vg_reachable2,
432  dummy);
433  (void)dummy;
434  vg_leaks = vg_leaks2 - vg_leaks;
435  vg_dubious = vg_dubious2 - vg_dubious;
436  vg_reachable = vg_reachable2 - vg_reachable;
437  if (vg_errs) {
438  string fail_msg(buf);
439  if (fail_msg.empty())
440  fail_msg = "VALGRIND DETECTED A PROBLEM";
441  REPORT_FAIL_VG(fail_msg);
442  return FAIL;
443  }
444  if (vg_leaks > 0) {
445  REPORT_FAIL_VG("LEAKED " << vg_leaks << " BYTES");
446  return FAIL;
447  }
448  if (vg_dubious > 0) {
449  // If code deliberately holds onto blocks by a pointer
450  // not to the start (e.g. languages/utilities.c does)
451  // then we need to rerun the test to see if the leak is
452  // real...
453  if (runcount == 0) {
454  out << col_yellow << " PROBABLY LEAKED MEMORY - RETRYING TEST" << col_reset;
455  ++runcount;
456  // Ensure that any cached memory from fd tracking
457  // is allocated before we rerun the test.
458  (void)fdtracker.check();
459  continue;
460  }
461  REPORT_FAIL_VG("PROBABLY LEAKED " << vg_dubious << " BYTES");
462  return FAIL;
463  }
464  if (vg_reachable > 0) {
465  // C++ STL implementations often "horde" released
466  // memory - for GCC 3.4 and newer the runtest script
467  // sets GLIBCXX_FORCE_NEW=1 which will disable this
468  // behaviour so we avoid this issue, but for older
469  // GCC and other compilers this may be an issue.
470  //
471  // See also:
472  // https://valgrind.org/docs/manual/faq.html#faq.reports
473  //
474  // For now, just use runcount to rerun the test and see
475  // if more is leaked - hopefully this shouldn't give
476  // false positives.
477  if (runcount == 0) {
478  out << col_yellow << " POSSIBLE UNRELEASED MEMORY - RETRYING TEST" << col_reset;
479  ++runcount;
480  // Ensure that any cached memory from fd tracking
481  // is allocated before we rerun the test.
482  (void)fdtracker.check();
483  continue;
484  }
485  REPORT_FAIL_VG("FAILED TO RELEASE " << vg_reachable << " BYTES");
486  return FAIL;
487  }
488  }
489 #endif
490  if (!fdtracker.check()) {
491  if (runcount == 0) {
492  out << col_yellow << " POSSIBLE FDLEAK:" << fdtracker.get_message() << col_reset;
493  ++runcount;
494  continue;
495  }
496  out << col_red << " FDLEAK:" << fdtracker.get_message() << col_reset;
497  return FAIL;
498  }
499  } catch (const TestFail &) {
500  out << ' ';
501  if (expected_failure) {
502  out << col_yellow << "XFAIL (" << expected_failure << ")";
503  } else {
504  out << col_red << "FAILED";
505  }
506  out << col_reset;
508  return expected_failure ? XFAIL : FAIL;
509  } catch (const TestSkip &) {
510  out << col_yellow << " SKIPPED" << col_reset;
512  return SKIP;
513 #ifndef NO_LIBXAPIAN
514  } catch (const Xapian::Error &err) {
515  out << ' ';
516  string errclass = err.get_type();
517  if (expected_exception && expected_exception == errclass) {
518  out << col_yellow << "C++ FAILED TO CATCH " << errclass << col_reset;
519  return SKIP;
520  }
521  if (errclass == "NetworkError" &&
522  err.get_error_string() != NULL &&
523  err.get_error_string() == errno_to_string(ECHILD)) {
524  // ECHILD suggests we've run out of processes, and that's
525  // much more likely to be a system issue than a Xapian bug.
526  //
527  // We also see apparently spurious ECHILD on Debian
528  // buildds sometimes: https://bugs.debian.org/681941
529  out << col_yellow << "ECHILD in network code" << col_reset;
530  return SKIP;
531  }
532 
533  if (expected_failure) {
534  out << col_yellow << "XFAIL (" << expected_failure
535  << "): ";
536  } else {
537  out << col_red << "FAIL: ";
538  }
539  out << err.get_description() << col_reset;
541  return expected_failure ? XFAIL : FAIL;
542 #endif
543  } catch (const string & msg) {
544  out << ' ';
545  if (expected_failure) {
546  out << col_yellow << "XFAIL (" << expected_failure
547  << "): ";
548  } else {
549  out << col_red << "FAIL: ";
550  }
551  out << "EXCEPTION std::string " << msg << col_reset;
553  return expected_failure ? XFAIL : FAIL;
554  } catch (const std::exception & e) {
555  out << ' ';
556  if (expected_failure) {
557  out << col_yellow << "XFAIL (" << expected_failure
558  << "): ";
559  } else {
560  out << col_red << "FAIL: ";
561  }
562 #ifndef USE_RTTI
563  out << "std::exception";
564 #else
565  const char * name = typeid(e).name();
566 # ifdef HAVE_CXXABI_H
567  // __cxa_demangle() apparently requires GCC >= 3.1.
568  // Demangle the name which GCC returns for type_info::name().
569  int status;
570  char * realname = abi::__cxa_demangle(name, NULL, 0, &status);
571  if (realname) {
572  out << realname;
573  free(realname);
574  } else {
575  out << name;
576  }
577 # else
578  out << name;
579 # endif
580 #endif
581  out << ": " << e.what() << col_reset;
583  return expected_failure ? XFAIL : FAIL;
584  } catch (const char * msg) {
585  out << ' ';
586  if (expected_failure) {
587  out << col_yellow << "XFAIL (" << expected_failure
588  << "): ";
589  } else {
590  out << col_red << "FAIL: ";
591  }
592  if (msg) {
593  out << "EXCEPTION char* " << msg;
594  } else {
595  out << "EXCEPTION (char*)NULL";
596  }
597  out << col_reset;
599  return expected_failure ? XFAIL : FAIL;
600  } catch (...) {
601  out << ' ';
602  if (expected_failure) {
603  out << col_yellow << "XFAIL (" << expected_failure
604  << "): ";
605  } else {
606  out << col_red << "FAIL: ";
607  }
608  out << "UNKNOWN EXCEPTION" << col_reset;
610  return expected_failure ? XFAIL : FAIL;
611  }
612 
613  if (expected_failure) {
614  // Testcase marked as expected to fail but actually passed.
615  out << ' ' << col_red << "XPASS (" << expected_failure << ")"
616  << col_reset;
618  return XPASS;
619  }
620  return PASS;
621  }
622 
623  // Caught a signal.
624  const char *signame = "SIGNAL";
625 #if defined HAVE_SIGACTION && defined SA_SIGINFO
626  bool show_addr = true;
627 #else
628  bool show_addr = false;
629 #endif
630  switch (signum) {
631  case SIGSEGV: signame = "SIGSEGV"; break;
632  case SIGFPE: signame = "SIGFPE"; break;
633  case SIGILL: signame = "SIGILL"; break;
634 #ifdef SIGBUS
635  case SIGBUS: signame = "SIGBUS"; break;
636 #endif
637 #ifdef SIGSTKFLT
638  case SIGSTKFLT:
639  signame = "SIGSTKFLT";
640  show_addr = false;
641  break;
642 #endif
643  }
644  out << " " << col_red << signame;
645  if (show_addr) {
646  char buf[40];
647  sprintf(buf, " at %p", sigaddr);
648  out << buf;
649  }
650  out << col_reset;
652  return FAIL;
653  }
654 }
655 
657 test_driver::run_tests(vector<string>::const_iterator b,
658  vector<string>::const_iterator e)
659 {
660  return do_run_tests(b, e);
661 }
662 
665 {
666  const vector<string> blank;
667  return do_run_tests(blank.begin(), blank.end());
668 }
669 
671 test_driver::do_run_tests(vector<string>::const_iterator b,
672  vector<string>::const_iterator e)
673 {
674  set<string> m(b, e);
675  bool check_name = !m.empty();
676 
678 
679  for (const test_desc *test = tests; test->name; ++test) {
680  bool do_this_test = !check_name;
681  if (!do_this_test && m.find(test->name) != m.end())
682  do_this_test = true;
683  if (!do_this_test) {
684  // if this test is "foo123" see if "foo" was listed
685  // this way "./testprog foo" can run foo1, foo2, etc.
686  string t = test->name;
687  string::size_type i;
688  i = t.find_last_not_of("0123456789") + 1;
689  if (i != string::npos) {
690  t.resize(i);
691  if (m.find(t) != m.end()) do_this_test = true;
692  }
693  }
694  if (do_this_test) {
695  out << "Running test: " << test->name << "...";
696  out.flush();
697  test_driver::test_result test_res = runtest(test);
698 #ifndef NO_LIBXAPIAN
699  if (backendmanager)
701 #endif
702  switch (test_res) {
703  case PASS:
704  ++res.succeeded;
705  if (verbose || !use_cr) {
706  out << col_green << " ok" << col_reset << '\n';
707  } else {
708  out << "\r \r";
709  }
710  break;
711  case XFAIL:
712  ++res.xfailed;
713  out << '\n';
714  break;
715  case FAIL:
716  ++res.failed;
717  out << '\n';
718  if (abort_on_error) {
719  throw "Test failed - aborting further tests";
720  }
721  break;
722  case XPASS:
723  ++res.xpassed;
724  out << '\n';
725  if (abort_on_error) {
726  throw "Test marked as XFAIL passed - aborting further tests";
727  }
728  break;
729  case SKIP:
730  ++res.skipped;
731  out << '\n';
732  // ignore the result of this test.
733  break;
734  }
735  }
736  }
737  return res;
738 }
739 
740 void
742 {
743  cout << "Usage: " << argv0 << " [-v|--verbose] [-o|--abort-on-error] "
744  << opt_help << "[TESTNAME]...\n"
745  " " << argv0 << " [-h|--help]\n";
746  exit(1);
747 }
748 
749 /* Needs C linkage so we can pass it to atexit() without problems. */
750 extern "C" {
751 // Call upon program exit if there's more than one test run.
752 static void
754 {
755  test_driver::report(test_driver::total, "total");
756 }
757 }
758 
759 void
760 test_driver::report(const test_driver::result &r, const string &desc)
761 {
762  // Report totals at the end if we reported two or more subtotals.
763  if (++runs == 2) atexit(report_totals);
764 
765  if (r.succeeded != 0 || r.failed != 0) {
766  cout << argv0 << " " << desc << ": ";
767 
768  if (r.failed == 0 && r.xpassed == 0)
769  cout << "All ";
770 
771  cout << col_green << r.succeeded << col_reset << " tests passed";
772 
773  if (r.failed != 0)
774  cout << ", " << col_red << r.failed << col_reset << " failed";
775 
776  if (r.xpassed != 0)
777  cout << ", " << col_red << r.xpassed << col_reset
778  << " expected failures passed";
779 
780  if (r.xfailed != 0)
781  cout << ", " << col_yellow << r.xfailed << col_reset
782  << " expected failures";
783 
784  if (r.skipped) {
785  cout << ", " << col_yellow << r.skipped << col_reset
786  << " skipped.\n";
787  } else {
788  cout << ".\n";
789  }
790  }
791 }
792 
793 void
794 test_driver::add_command_line_option(const string &l, char s, string * arg)
795 {
796  short_opts.insert(make_pair(int(s), arg));
797  opt_help += "[-";
798  opt_help += s;
799  opt_help += ' ';
800  opt_help += l;
801  opt_help += "] ";
802 }
803 
804 void
805 test_driver::parse_command_line(int argc, char **argv)
806 {
807  argv0 = argv[0];
808 
809 #ifdef HAVE_VALGRIND
810  if (RUNNING_ON_VALGRIND) {
811  if (getenv("XAPIAN_TESTSUITE_VALGRIND") != NULL) {
812  // Open the valgrind log file, and unlink it.
813  char fname[64];
814  sprintf(fname, ".valgrind.log.%lu",
815  static_cast<unsigned long>(getpid()));
816  vg_log_fd = open(fname, O_RDONLY|O_NONBLOCK|O_CLOEXEC);
817  if (vg_log_fd != -1) unlink(fname);
818  }
819  }
820 #endif
821 
822 #ifndef __WIN32__
823  {
824  bool colourise = true;
825  const char *p = getenv("XAPIAN_TESTSUITE_OUTPUT");
826  if (p == NULL || !*p || strcmp(p, "auto") == 0) {
827  colourise = isatty(1);
828  } else if (strcmp(p, "plain") == 0) {
829  colourise = false;
830  }
831  if (colourise) {
832  col_red = "\x1b[1m\x1b[31m";
833  col_green = "\x1b[1m\x1b[32m";
834  col_yellow = "\x1b[1m\x1b[33m";
835  col_reset = "\x1b[0m";
836  use_cr = true;
837  }
838  }
839 #endif
840 
841  static const struct option long_opts[] = {
842  {"verbose", no_argument, 0, 'v'},
843  {"abort-on-error", no_argument, 0, 'o'},
844  {"help", no_argument, 0, 'h'},
845  {NULL, 0, 0, 0}
846  };
847 
848  string short_opts_string = "voh";
849  map<int, string *>::const_iterator i;
850  for (i = short_opts.begin(); i != short_opts.end(); ++i) {
851  short_opts_string += char(i->first);
852  short_opts_string += ':';
853  }
854  const char * opts = short_opts_string.c_str();
855 
856  int c;
857  while ((c = gnu_getopt_long(argc, argv, opts, long_opts, 0)) != -1) {
858  switch (c) {
859  case 'v':
860  ++verbose;
861  break;
862  case 'o':
863  abort_on_error = true;
864  break;
865  default: {
866  i = short_opts.find(c);
867  if (i != short_opts.end()) {
868  i->second->assign(optarg);
869  break;
870  }
871  // -h or unrecognised option
872  usage();
873  return; // usage() doesn't return ...
874  }
875  }
876  }
877 
878  if (verbose == 0) {
879  const char *p = getenv("VERBOSE");
880  if (p != NULL) {
881  verbose = atoi(p);
882  }
883  }
884 
885  while (argv[optind]) {
886  test_names.push_back(string(argv[optind]));
887  ++optind;
888  }
889 }
890 
891 int
893 {
894  test_driver driver(tests);
895 
896  test_driver::result myresult;
897  myresult = driver.run_tests(test_names.begin(), test_names.end());
898 
899  subtotal += myresult;
900 
901  // Return value is a Unix-style exit code, so 0 for success and 1 for
902  // failure.
903  return myresult.failed > 0 || myresult.xpassed > 0;
904 }
905 
906 bool
907 TEST_EQUAL_DOUBLE_(double a, double b)
908 {
909  if (a == b) return true;
910  return (ceil(log10(max(fabs(a), fabs(b)))) - log10(fabs(a - b)) > DBL_DIG);
911 }
static bool use_cr
Definition: testsuite.h:260
static std::string argv0
Definition: testsuite.h:253
bool endswith(const std::string &s, char sfx)
Definition: stringutils.h:75
static void usage()
Definition: testsuite.cc:741
Define the XAPIAN_NORETURN macro.
Run multiple tests for different backends.
Wrappers to allow GNU getopt to be used cleanly from C++ code.
int optind
Definition: getopt.cc:94
result run_tests()
Run all the tests supplied and return the results.
Definition: testsuite.cc:664
unsigned int xfailed
Number of tests with result XFAIL.
Definition: testsuite.h:130
static std::string col_yellow
Definition: testsuite.h:256
Class which is thrown when a test case fails.
Definition: testsuite.h:46
int gnu_getopt_long(int argc_, char *const *argv_, const char *shortopts_, const struct option *longopts_, int *optind_)
Definition: gnu_getopt.h:97
static void parse_command_line(int argc, char **argv)
Parse the command line arguments.
Definition: testsuite.cc:805
test_result runtest(const test_desc *test)
Runs the test function and returns its result.
Definition: testsuite.cc:308
static void report_totals(void)
Definition: testsuite.cc:753
static int signum
Definition: testsuite.cc:176
unsigned int xpassed
Number of tests with result XFAIL.
Definition: testsuite.h:136
Track leaked file descriptors.
static void report(const test_driver::result &r, const std::string &desc)
Print summary of tests passed, failed, and skipped.
Definition: testsuite.cc:760
static std::map< int, std::string * > short_opts
Definition: testsuite.h:216
static const char * opts
static result subtotal
Definition: testsuite.h:199
Convert errno value to std::string, thread-safe if possible.
#define DIR_SEPS
Definition: config.h:8
a generic test suite engine
WritableDatabase open()
Construct a WritableDatabase object for a new, empty InMemory database.
Definition: dbfactory.h:104
STL namespace.
result do_run_tests(std::vector< std::string >::const_iterator b, std::vector< std::string >::const_iterator e)
The implementation used by run_tests.
Definition: testsuite.cc:671
Utility functions for testing files.
#define O_CLOEXEC
Definition: safefcntl.h:90
const char * get_type() const
The type of this error (e.g. "DocNotFoundError".)
Definition: error.h:117
#define false
Definition: header.h:9
static std::string col_green
Definition: testsuite.h:256
static std::string get_srcdir()
Read srcdir from environment and if not present, make a valiant attempt to guess a value...
Definition: testsuite.cc:128
test_driver(const test_desc *tests_)
The constructor, which sets up the test driver.
Definition: testsuite.cc:169
static void add_command_line_option(const std::string &l, char s, std::string *arg)
Add a test-specific command line option.
Definition: testsuite.cc:794
static SIGJMP_BUF jb
Definition: testsuite.cc:175
#define no_argument
Definition: gnu_getopt.h:79
Hierarchy of classes which Xapian can throw as exceptions.
const char * dummy[]
Definition: version_h.cc:7
std::ostream out
Definition: testsuite.h:244
Base class for backend handling in test harness.
static std::string col_reset
Definition: testsuite.h:256
std::ostringstream tout
The debug printing stream.
Definition: testsuite.cc:103
void errno_to_string(int e, string &s)
unsigned int succeeded
The number of tests which succeeded.
Definition: testsuite.h:118
const char * expected_failure
Set to a string explanation for testcases expected to fail.
Definition: testsuite.cc:100
static result total
Definition: testsuite.h:202
unsigned int failed
The number of tests which failed.
Definition: testsuite.h:121
static string srcdir
Definition: stemtest.cc:43
BackendManager * backendmanager
backendmanager is global so that it can be accessed by individual tests.
Definition: testrunner.cc:42
char * optarg
Definition: getopt.cc:79
#define SIGJMP_BUF
Definition: testsuite.cc:91
bool startswith(const std::string &s, char pfx)
Definition: stringutils.h:51
static const test_desc tests[]
The lists of tests to perform.
bool TEST_EQUAL_DOUBLE_(double a, double b)
Helper function for TEST_EQUAL_DOUBLE macro.
Definition: testsuite.cc:907
static int runs
Definition: testsuite.h:250
static std::string opt_help
Definition: testsuite.h:218
static std::vector< std::string > test_names
Definition: testsuite.h:220
const test_desc * tests
Definition: testsuite.h:247
virtual void clean_up()
Called after each test, to perform any necessary cleanup.
std::string get_description() const
Return a string describing this object.
Definition: error.cc:93
#define SIGSETJMP(ENV, SAVESIGS)
Definition: testsuite.cc:89
static bool abort_on_error
Definition: testsuite.h:241
const std::string & get_message() const
Definition: fdtracker.h:64
char name[9]
Definition: dbcheck.cc:55
void write_and_clear_tout()
Write out anything in tout and clear it.
Definition: testsuite.cc:118
const char * get_error_string() const
Returns any system error string associated with this exception.
Definition: error.cc:50
#define SIGLONGJMP(ENV, VAL)
Definition: testsuite.cc:90
All exceptions thrown by Xapian are subclasses of Xapian::Error.
Definition: error.h:43
void(* run)()
The function to run to perform the test.
Definition: testsuite.h:82
static std::string col_red
Definition: testsuite.h:256
Various handy helpers which std::string really should provide.
void init()
Definition: fdtracker.cc:77
static int run(const test_desc *tests)
Definition: testsuite.cc:892
<unistd.h>, but with compat.
Definition: header.h:151
int verbose
The global verbose flag.
Definition: testsuite.cc:78
A structure used to report the summary of tests passed and failed.
Definition: testsuite.h:116
const char * expected_exception
The exception type we were expecting in TEST_EXCEPTION.
Definition: testsuite.cc:98
Structure holding a description of a test.
Definition: testsuite.h:77
bool check()
Definition: fdtracker.cc:108
The test driver. This class takes care of running the tests.
Definition: testsuite.h:108
const char * name
The name of the test.
Definition: testsuite.h:79
bool file_exists(const char *path)
Test if a file exists.
Definition: filetests.h:39
unsigned int skipped
The number of tests which were skipped.
Definition: testsuite.h:124
include <fcntl.h>, but working around broken platforms.
static void * sigaddr
Definition: testsuite.cc:177
Class which is thrown when a test case is to be skipped.
Definition: testsuite.h:53
static void handle_sig(int signum_)
Definition: testsuite.cc:211