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