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