1 /* testsuite.cc: a test suite engine
3 * Copyright 1999,2000,2001 BrightStation PLC
4 * Copyright 2002 Ananova Ltd
5 * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2012,2013,2015,2016,2017 Olly Betts
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
25 #include "testsuite.h"
28 # include "backendmanager.h"
30 #include "fdtracker.h"
31 #include "testrunner.h"
32 #include "safeunistd.h"
35 # include <valgrind/memcheck.h>
36 # include <sys/types.h>
37 # include "safefcntl.h"
45 #include <cfloat> // For DBL_DIG.
46 #include <cmath> // For ceil, fabs, log10.
51 #include "gnu_getopt.h"
65 # include <xapian/error.h>
67 #include "filetests.h"
68 #include "stringutils.h"
72 /// The global verbose flag.
76 static int vg_log_fd
= -1;
80 # define SIGSETJMP(ENV, SAVESIGS) sigsetjmp(ENV, SAVESIGS)
81 # define SIGLONGJMP(ENV, VAL) siglongjmp(ENV, VAL)
82 # define SIGJMP_BUF sigjmp_buf
84 # define SIGSETJMP(ENV, SAVESIGS) setjmp(ENV)
85 # define SIGLONGJMP(ENV, VAL) longjmp(ENV, VAL)
86 # define SIGJMP_BUF jmp_buf
89 /// The exception type we were expecting in TEST_EXCEPTION
90 // We use this to attempt to diagnose when the code fails to catch an
91 // exception when it should (due to a compiler or runtime fault in
93 const char * expected_exception
= NULL
;
95 /// The debug printing stream
96 std::ostringstream tout
;
98 int test_driver::runs
= 0;
99 test_driver::result
test_driver::subtotal
;
100 test_driver::result
test_driver::total
;
101 string
test_driver::argv0
;
102 string
test_driver::opt_help
;
103 map
<int, string
*> test_driver::short_opts
;
104 vector
<string
> test_driver::test_names
;
105 bool test_driver::abort_on_error
= false;
106 string
test_driver::col_red
, test_driver::col_green
;
107 string
test_driver::col_yellow
, test_driver::col_reset
;
108 bool test_driver::use_cr
= false;
111 test_driver::write_and_clear_tout()
113 const string
& s
= tout
.str();
121 test_driver::get_srcdir()
123 char *p
= getenv("srcdir");
124 if (p
!= NULL
) return string(p
);
127 // The path on argv[0] will always use \ for the directory separator.
128 const char ARGV0_SEP
= '\\';
130 const char ARGV0_SEP
= '/';
132 // Default srcdir to the pathname of argv[0].
133 string
srcdir(argv0
);
134 string::size_type i
= srcdir
.find_last_of(ARGV0_SEP
);
136 if (i
!= string::npos
) {
137 srcfile
.assign(srcdir
, i
+ 1, string::npos
);
139 // libtool may put the real executable in .libs.
140 i
= srcdir
.find_last_of(ARGV0_SEP
);
141 if (srcdir
.substr(i
+ 1) == ".libs") {
143 // And it may have an "lt-" prefix.
144 if (startswith(srcfile
, "lt-")) srcfile
.erase(0, 3);
147 // No path of argv[0], so default srcdir to the current directory.
148 // This may not work if libtool is involved as the true executable is
149 // sometimes in ".libs".
154 // Remove any trailing ".exe" suffix, since some platforms add this.
155 if (endswith(srcfile
, ".exe")) srcfile
.resize(srcfile
.size() - 4);
158 if (!file_exists(srcdir
+ '/' + srcfile
+ ".cc")) {
160 << ": srcdir is not in the environment and I can't guess it!\n"
161 "Run test programs using the runtest script - see HACKING for details"
168 test_driver::test_driver(const test_desc
*tests_
)
169 : out(cout
.rdbuf()), tests(tests_
)
173 static SIGJMP_BUF jb
;
174 static int signum
= 0;
175 static void * sigaddr
= NULL
;
177 // Needs C linkage so we can pass it to sigaction()/signal() without problems.
180 #if defined HAVE_SIGACTION && defined SA_SIGINFO
182 static void handle_sig(int signum_
, siginfo_t
*si
, void *)
184 // Disable all our signal handlers to avoid problems if the signal
185 // handling code causes a signal.
187 sa
.sa_handler
= SIG_DFL
;
188 sigemptyset(&sa
.sa_mask
);
190 // We set the handlers with SA_RESETHAND, but that will only reset the
191 // handler for the signal which fired.
192 if (signum_
!= SIGSEGV
) sigaction(SIGSEGV
, &sa
, NULL
);
193 if (signum_
!= SIGFPE
) sigaction(SIGFPE
, &sa
, NULL
);
194 if (signum_
!= SIGILL
) sigaction(SIGILL
, &sa
, NULL
);
196 if (signum_
!= SIGBUS
) sigaction(SIGBUS
, &sa
, NULL
);
199 if (signum_
!= SIGSTKFLT
) sigaction(SIGSTKFLT
, &sa
, NULL
);
202 sigaddr
= si
->si_addr
;
209 static void handle_sig(int signum_
)
211 // Disable all our signal handlers to avoid problems if the signal
212 // handling code causes a signal.
213 signal(SIGSEGV
, SIG_DFL
);
214 signal(SIGFPE
, SIG_DFL
);
215 signal(SIGILL
, SIG_DFL
);
217 signal(SIGBUS
, SIG_DFL
);
220 signal(SIGSTKFLT
, SIG_DFL
);
229 class SignalRedirector
{
233 SignalRedirector() : active(false) { }
238 // SA_SIGINFO not universal (e.g. not present on Linux < 2.2 and Hurd).
239 #if defined HAVE_SIGACTION && defined SA_SIGINFO
241 sa
.sa_sigaction
= handle_sig
;
242 sigemptyset(&sa
.sa_mask
);
243 sa
.sa_flags
= SA_RESETHAND
|SA_SIGINFO
;
244 sigaction(SIGSEGV
, &sa
, NULL
);
245 sigaction(SIGFPE
, &sa
, NULL
);
246 sigaction(SIGILL
, &sa
, NULL
);
248 sigaction(SIGBUS
, &sa
, NULL
);
251 sigaction(SIGSTKFLT
, &sa
, NULL
);
254 signal(SIGSEGV
, handle_sig
);
255 signal(SIGFPE
, handle_sig
);
256 signal(SIGILL
, handle_sig
);
258 signal(SIGBUS
, handle_sig
);
261 signal(SIGSTKFLT
, handle_sig
);
265 ~SignalRedirector() {
267 #if defined HAVE_SIGACTION && defined SA_SIGINFO
269 sa
.sa_handler
= SIG_DFL
;
270 sigemptyset(&sa
.sa_mask
);
272 sigaction(SIGSEGV
, &sa
, NULL
);
273 sigaction(SIGFPE
, &sa
, NULL
);
274 sigaction(SIGILL
, &sa
, NULL
);
276 sigaction(SIGBUS
, &sa
, NULL
);
279 sigaction(SIGSTKFLT
, &sa
, NULL
);
282 signal(SIGSEGV
, SIG_DFL
);
283 signal(SIGFPE
, SIG_DFL
);
284 signal(SIGILL
, SIG_DFL
);
286 signal(SIGBUS
, SIG_DFL
);
289 signal(SIGSTKFLT
, SIG_DFL
);
296 // A wrapper around the tests to trap exceptions,
297 // and avoid having to catch them in every test function.
298 // If this test driver is used for anything other than
299 // Xapian tests, then this ought to be provided by
300 // the client, really.
301 // return: test_driver::PASS, test_driver::FAIL, or test_driver::SKIP
302 test_driver::test_result
303 test_driver::runtest(const test_desc
*test
)
305 // This is used to make a note of how many times we've run the test
306 volatile int runcount
= 0;
313 if (SIGSETJMP(jb
, 1) == 0) {
314 SignalRedirector sig
; // use object so signal handlers are reset
315 static bool catch_signals
=
316 (getenv("XAPIAN_TESTSUITE_SIG_DFL") == NULL
);
317 if (catch_signals
) sig
.activate();
319 expected_exception
= NULL
;
322 long vg_leaks
= 0, vg_dubious
= 0, vg_reachable
= 0;
323 if (vg_log_fd
!= -1) {
324 VALGRIND_DO_LEAK_CHECK
;
325 vg_errs
= VALGRIND_COUNT_ERRORS
;
327 VALGRIND_COUNT_LEAKS(vg_leaks
, vg_dubious
, vg_reachable
, dummy
);
329 // Skip past any unread log output.
330 lseek(vg_log_fd
, 0, SEEK_END
);
334 out
<< col_red
<< " FAILED" << col_reset
;
335 write_and_clear_tout();
339 write_and_clear_tout();
342 backendmanager
->clean_up();
345 if (vg_log_fd
!= -1) {
346 // We must empty tout before asking valgrind to perform its
347 // leak checks, otherwise the buffers holding the output
348 // may be identified as a memory leak (especially if >1K of
349 // output has been buffered it appears...)
351 #define REPORT_FAIL_VG(M) do { \
354 ssize_t c = read(vg_log_fd, buf, sizeof(buf)); \
355 if (c == 0 || (c < 0 && errno != EINTR)) break; \
356 if (c > 0) out << string(buf, c); \
359 out << " " << col_red << M << col_reset; \
361 // Record the current position so we can restore it so
362 // REPORT_FAIL_VG() gets the whole output.
363 off_t curpos
= lseek(vg_log_fd
, 0, SEEK_CUR
);
366 ssize_t c
= read(vg_log_fd
, buf
, sizeof(buf
));
367 if (c
== 0 || (c
< 0 && errno
!= EINTR
)) {
372 // Valgrind output has "==<pid>== \n" between
373 // report "records", so skip any lines like that,
374 // and also any warnings and continuation lines.
378 spc
= static_cast<const char *>(
379 memchr(buf
+ i
, ' ', c
- i
));
389 (memcmp(buf
+ i
, "Warning:", 8) == 0 ||
390 memcmp(buf
+ i
, " ", 3) == 0)) {
394 nl
= static_cast<const char *>(
395 memchr(buf
+ i
, '\n', c
- i
));
406 char *start
= buf
+ i
;
408 if (c
> 128) c
= 128;
412 p
= static_cast<const char*>(
413 memchr(start
, '\n', c
));
414 if (p
!= NULL
) c
= p
- start
;
417 memmove(buf
, start
, c
);
422 lseek(vg_log_fd
, curpos
, SEEK_SET
);
424 int vg_errs2
= VALGRIND_COUNT_ERRORS
;
425 vg_errs
= vg_errs2
- vg_errs
;
426 VALGRIND_DO_LEAK_CHECK
;
427 long vg_leaks2
= 0, vg_dubious2
= 0, vg_reachable2
= 0;
429 VALGRIND_COUNT_LEAKS(vg_leaks2
, vg_dubious2
, vg_reachable2
,
432 vg_leaks
= vg_leaks2
- vg_leaks
;
433 vg_dubious
= vg_dubious2
- vg_dubious
;
434 vg_reachable
= vg_reachable2
- vg_reachable
;
436 string
fail_msg(buf
);
437 if (fail_msg
.empty())
438 fail_msg
= "VALGRIND DETECTED A PROBLEM";
439 REPORT_FAIL_VG(fail_msg
);
443 REPORT_FAIL_VG("LEAKED " << vg_leaks
<< " BYTES");
446 if (vg_dubious
> 0) {
447 // If code deliberately holds onto blocks by a pointer
448 // not to the start (e.g. languages/utilities.c does)
449 // then we need to rerun the test to see if the leak is
452 out
<< col_yellow
<< " PROBABLY LEAKED MEMORY - RETRYING TEST" << col_reset
;
454 // Ensure that any cached memory from fd tracking
455 // is allocated before we rerun the test.
456 (void)fdtracker
.check();
459 REPORT_FAIL_VG("PROBABLY LEAKED " << vg_dubious
<< " BYTES");
462 if (vg_reachable
> 0) {
463 // C++ STL implementations often "horde" released
464 // memory - for GCC 3.4 and newer the runtest script
465 // sets GLIBCXX_FORCE_NEW=1 which will disable this
466 // behaviour so we avoid this issue, but for older
467 // GCC and other compilers this may be an issue.
470 // http://valgrind.org/docs/FAQ/#faq.reports
472 // For now, just use runcount to rerun the test and see
473 // if more is leaked - hopefully this shouldn't give
476 out
<< col_yellow
<< " POSSIBLE UNRELEASED MEMORY - RETRYING TEST" << col_reset
;
478 // Ensure that any cached memory from fd tracking
479 // is allocated before we rerun the test.
480 (void)fdtracker
.check();
483 REPORT_FAIL_VG("FAILED TO RELEASE " << vg_reachable
<< " BYTES");
488 if (!fdtracker
.check()) {
490 out
<< col_yellow
<< " POSSIBLE FDLEAK:" << fdtracker
.get_message() << col_reset
;
494 out
<< col_red
<< " FDLEAK:" << fdtracker
.get_message() << col_reset
;
497 } catch (const TestFail
&) {
498 out
<< col_red
<< " FAILED" << col_reset
;
499 write_and_clear_tout();
501 } catch (const TestSkip
&) {
502 out
<< col_yellow
<< " SKIPPED" << col_reset
;
503 write_and_clear_tout();
506 } catch (const Xapian::Error
&err
) {
507 string errclass
= err
.get_type();
508 if (expected_exception
&& expected_exception
== errclass
) {
509 out
<< col_yellow
<< " C++ FAILED TO CATCH " << errclass
<< col_reset
;
512 if (errclass
== "NetworkError" &&
513 strcmp(err
.get_error_string(), strerror(ECHILD
)) == 0) {
514 // ECHILD suggests we've run out of processes, and that's
515 // much more likely to be a system issue than a Xapian bug.
517 // We also see apparently spurious ECHILD on Debian
518 // buildds sometimes: https://bugs.debian.org/681941
519 out
<< col_yellow
<< " ECHILD in network code" << col_reset
;
522 out
<< " " << col_red
<< err
.get_description() << col_reset
;
523 write_and_clear_tout();
526 } catch (const string
& msg
) {
527 out
<< col_red
<< " EXCEPTION std::string " << msg
<< col_reset
;
528 write_and_clear_tout();
530 } catch (const std::exception
& e
) {
531 out
<< " " << col_red
;
533 out
<< "std::exception";
535 const char * name
= typeid(e
).name();
536 # ifdef HAVE_CXXABI_H__
537 // __cxa_demangle() apparently requires GCC >= 3.1.
538 // Demangle the name which GCC returns for type_info::name().
540 char * realname
= abi::__cxa_demangle(name
, NULL
, 0, &status
);
551 out
<< ": " << e
.what() << col_reset
;
552 write_and_clear_tout();
554 } catch (const char * msg
) {
557 out
<< " EXCEPTION char * " << msg
;
559 out
<< " EXCEPTION (char*)NULL";
562 write_and_clear_tout();
565 out
<< col_red
<< " UNKNOWN EXCEPTION" << col_reset
;
566 write_and_clear_tout();
573 const char *signame
= "SIGNAL";
574 bool show_addr
= true;
576 case SIGSEGV
: signame
= "SIGSEGV"; break;
577 case SIGFPE
: signame
= "SIGFPE"; break;
578 case SIGILL
: signame
= "SIGILL"; break;
580 case SIGBUS
: signame
= "SIGBUS"; break;
584 signame
= "SIGSTKFLT";
589 out
<< " " << col_red
<< signame
;
592 sprintf(buf
, " at %p", sigaddr
);
596 write_and_clear_tout();
602 test_driver::run_tests(vector
<string
>::const_iterator b
,
603 vector
<string
>::const_iterator e
)
605 return do_run_tests(b
, e
);
609 test_driver::run_tests()
611 const vector
<string
> blank
;
612 return do_run_tests(blank
.begin(), blank
.end());
616 test_driver::do_run_tests(vector
<string
>::const_iterator b
,
617 vector
<string
>::const_iterator e
)
620 bool check_name
= !m
.empty();
622 test_driver::result res
;
624 for (const test_desc
*test
= tests
; test
->name
; ++test
) {
625 bool do_this_test
= !check_name
;
626 if (!do_this_test
&& m
.find(test
->name
) != m
.end())
629 // if this test is "foo123" see if "foo" was listed
630 // this way "./testprog foo" can run foo1, foo2, etc.
631 string t
= test
->name
;
633 i
= t
.find_last_not_of("0123456789") + 1;
634 if (i
!= string::npos
) {
636 if (m
.find(t
) != m
.end()) do_this_test
= true;
640 out
<< "Running test: " << test
->name
<< "...";
642 test_driver::test_result test_res
= runtest(test
);
645 backendmanager
->clean_up();
650 if (verbose
|| !use_cr
) {
651 out
<< col_green
<< " ok" << col_reset
<< endl
;
659 if (abort_on_error
) {
660 throw "Test failed - aborting further tests";
666 // ignore the result of this test.
677 cout
<< "Usage: " << argv0
<< " [-v|--verbose] [-o|--abort-on-error] " << opt_help
678 << "[TESTNAME]..." << endl
;
679 cout
<< " " << argv0
<< " [-h|--help]" << endl
;
683 /* Needs C linkage so we can pass it to atexit() without problems. */
685 // Call upon program exit if there's more than one test run.
689 test_driver::report(test_driver::total
, "total");
694 test_driver::report(const test_driver::result
&r
, const string
&desc
)
696 // Report totals at the end if we reported two or more subtotals.
697 if (++runs
== 2) atexit(report_totals
);
699 if (r
.succeeded
!= 0 || r
.failed
!= 0) {
700 cout
<< argv0
<< " " << desc
<< ": ";
705 cout
<< col_green
<< r
.succeeded
<< col_reset
<< " tests passed";
708 cout
<< ", " << col_red
<< r
.failed
<< col_reset
<< " failed";
711 cout
<< ", " << col_yellow
<< r
.skipped
<< col_reset
712 << " skipped." << endl
;
720 test_driver::add_command_line_option(const string
&l
, char s
, string
* arg
)
722 short_opts
.insert(make_pair(int(s
), arg
));
731 test_driver::parse_command_line(int argc
, char **argv
)
737 bool colourise
= true;
738 const char *p
= getenv("XAPIAN_TESTSUITE_OUTPUT");
739 if (p
== NULL
|| !*p
|| strcmp(p
, "auto") == 0) {
740 colourise
= isatty(1);
741 } else if (strcmp(p
, "plain") == 0) {
745 col_red
= "\x1b[1m\x1b[31m";
746 col_green
= "\x1b[1m\x1b[32m";
747 col_yellow
= "\x1b[1m\x1b[33m";
748 col_reset
= "\x1b[0m";
754 const struct option long_opts
[] = {
755 {"verbose", no_argument
, 0, 'v'},
756 {"abort-on-error", no_argument
, 0, 'o'},
757 {"help", no_argument
, 0, 'h'},
761 string short_opts_string
= "voh";
762 map
<int, string
*>::const_iterator i
;
763 for (i
= short_opts
.begin(); i
!= short_opts
.end(); ++i
) {
764 short_opts_string
+= char(i
->first
);
765 short_opts_string
+= ':';
767 const char * opts
= short_opts_string
.c_str();
770 while ((c
= gnu_getopt_long(argc
, argv
, opts
, long_opts
, 0)) != -1) {
776 abort_on_error
= true;
779 i
= short_opts
.find(c
);
780 if (i
!= short_opts
.end()) {
781 i
->second
->assign(optarg
);
784 // -h or unrecognised option
786 return; // usage() doesn't return ...
792 const char *p
= getenv("VERBOSE");
798 while (argv
[optind
]) {
799 test_names
.push_back(string(argv
[optind
]));
804 if (RUNNING_ON_VALGRIND
) {
805 if (getenv("XAPIAN_TESTSUITE_VALGRIND") != NULL
) {
806 // Open the valgrind log file, and unlink it.
808 sprintf(fname
, ".valgrind.log.%lu",
809 static_cast<unsigned long>(getpid()));
810 vg_log_fd
= open(fname
, O_RDONLY
|O_NONBLOCK
|O_CLOEXEC
);
811 if (vg_log_fd
!= -1) unlink(fname
);
818 test_driver::run(const test_desc
*tests
)
820 test_driver
driver(tests
);
822 test_driver::result myresult
;
823 myresult
= driver
.run_tests(test_names
.begin(), test_names
.end());
825 subtotal
+= myresult
;
827 return bool(myresult
.failed
); // if 0, then everything passed
831 TEST_EQUAL_DOUBLE_(double a
, double b
)
833 if (a
== b
) return true;
834 return (ceil(log10(max(fabs(a
), fabs(b
)))) - log10(fabs(a
- b
)) > DBL_DIG
);