Branch data Line data Source code
1 : : /* testsuite.cc: a test suite engine
2 : : *
3 : : * Copyright 1999,2000,2001 BrightStation PLC
4 : : * Copyright 2002 Ananova Ltd
5 : : * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010 Olly Betts
6 : : *
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.
11 : : *
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.
16 : : *
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
20 : : * USA
21 : : */
22 : :
23 : : #include <config.h>
24 : :
25 : : #include "testsuite.h"
26 : :
27 : : #include "backendmanager.h"
28 : : #include "fdtracker.h"
29 : : #include "testrunner.h"
30 : :
31 : : #ifdef HAVE_VALGRIND
32 : : # include "safeerrno.h"
33 : : # include <valgrind/memcheck.h>
34 : : # include <sys/types.h>
35 : : # include "safefcntl.h"
36 : : # include "safeunistd.h"
37 : : #endif
38 : :
39 : : #include <algorithm>
40 : : #include <iostream>
41 : : #include <set>
42 : :
43 : : #include <cfloat> // For DBL_DIG.
44 : : #include <cmath> // For ceil, fabs, log10.
45 : : #include <cstdio>
46 : : #include <cstdlib>
47 : : #include <cstring>
48 : :
49 : : #include "gnu_getopt.h"
50 : :
51 : : #include <setjmp.h>
52 : : #include <signal.h>
53 : :
54 : : #include <exception>
55 : : #ifdef USE_RTTI
56 : : # include <typeinfo>
57 : : # ifdef __GNUC__
58 : : # include <cxxabi.h> // Added in GCC 3.1 which is now required.
59 : : # endif
60 : : #endif
61 : :
62 : : #include <xapian/error.h>
63 : : #include "noreturn.h"
64 : : #include "stringutils.h"
65 : : #include "utils.h"
66 : :
67 : : using namespace std;
68 : :
69 : : /// The global verbose flag.
70 : : bool verbose;
71 : :
72 : : #ifdef HAVE_VALGRIND
73 : : static int vg_log_fd = -1;
74 : : #endif
75 : :
76 : : #ifdef HAVE_SIGSETJMP
77 : : # define SIGSETJMP(ENV, SAVESIGS) sigsetjmp(ENV, SAVESIGS)
78 : : # define SIGLONGJMP(ENV, VAL) siglongjmp(ENV, VAL)
79 : : # define SIGJMP_BUF sigjmp_buf
80 : : #else
81 : : # define SIGSETJMP(ENV, SAVESIGS) setjmp(ENV)
82 : : # define SIGLONGJMP(ENV, VAL) longjmp(ENV, VAL)
83 : : # define SIGJMP_BUF jmp_buf
84 : : #endif
85 : :
86 : : /// The exception type we were expecting in TEST_EXCEPTION
87 : : // We use this to attempt to diagnose when the code fails to catch an
88 : : // exception when it should (due to a compiler or runtime fault in
89 : : // GCC 2.95 it seems)
90 : : const char * expected_exception = NULL;
91 : :
92 : : /// The debug printing stream
93 : 5 : std::ostringstream tout;
94 : :
95 : : int test_driver::runs = 0;
96 : 5 : test_driver::result test_driver::subtotal;
97 : 5 : test_driver::result test_driver::total;
98 : 5 : string test_driver::argv0;
99 : 5 : string test_driver::opt_help;
100 : 5 : map<int, string *> test_driver::short_opts;
101 : 5 : vector<string> test_driver::test_names;
102 : : bool test_driver::abort_on_error = false;
103 : 5 : string test_driver::col_red, test_driver::col_green;
104 : 5 : string test_driver::col_yellow, test_driver::col_reset;
105 : : bool test_driver::use_cr = false;
106 : :
107 : : void
108 : 45 : test_driver::write_and_clear_tout()
109 : : {
110 : 45 : const string & s = tout.str();
111 [ - + ]: 45 : if (!s.empty()) {
112 : 0 : out << '\n' << s;
113 : 0 : tout.str(string());
114 : 45 : }
115 : 45 : }
116 : :
117 : : string
118 : 33 : test_driver::get_srcdir()
119 : : {
120 : 33 : char *p = getenv("srcdir");
121 [ + - ]: 33 : if (p != NULL) return string(p);
122 : :
123 : : #ifdef __WIN32__
124 : : // The path on argv[0] will always use \ for the directory separator.
125 : : const char ARGV0_SEP = '\\';
126 : : #else
127 : 0 : const char ARGV0_SEP = '/';
128 : : #endif
129 : : // Default srcdir to the pathname of argv[0].
130 : 0 : string srcdir(argv0);
131 : 0 : string::size_type i = srcdir.find_last_of(ARGV0_SEP);
132 : 0 : string srcfile;
133 [ # # ]: 0 : if (i != string::npos) {
134 : 0 : srcfile = srcdir.substr(i + 1);
135 : 0 : srcdir.erase(i);
136 : : // libtool may put the real executable in .libs.
137 : 0 : i = srcdir.find_last_of(ARGV0_SEP);
138 [ # # ]: 0 : if (srcdir.substr(i + 1) == ".libs") {
139 : 0 : srcdir.erase(i);
140 : : // And it may have an "lt-" prefix.
141 [ # # ]: 0 : if (startswith(srcfile, "lt-")) srcfile.erase(0, 3);
142 : : }
143 : : } else {
144 : : // No path of argv[0], so default srcdir to the current directory.
145 : : // This may not work if libtool is involved as the true executable is
146 : : // sometimes in ".libs".
147 : 0 : srcfile = srcdir;
148 : 0 : srcdir = ".";
149 : : }
150 : :
151 : : // Remove any trailing ".exe" suffix, since some platforms add this.
152 [ # # ]: 0 : if (endswith(srcfile, ".exe")) srcfile.resize(srcfile.size() - 4);
153 : :
154 : : // Sanity check.
155 [ # # ]: 0 : if (!file_exists(srcdir + '/' + srcfile + ".cc")) {
156 : : cout << argv0
157 : : << ": srcdir is not in the environment and I can't guess it!\n"
158 : : "Run test programs using the runtest script - see HACKING for details"
159 : 0 : << endl;
160 : 0 : exit(1);
161 : : }
162 : 33 : return srcdir;
163 : : }
164 : :
165 : 217 : test_driver::test_driver(const test_desc *tests_)
166 : 217 : : out(cout.rdbuf()), tests(tests_)
167 : : {
168 : 217 : }
169 : :
170 : : static SIGJMP_BUF jb;
171 : : static int signum = 0;
172 : : static void * sigaddr = NULL;
173 : :
174 : : // Needs C linkage so we can pass it to sigaction()/signal() without problems.
175 : : extern "C" {
176 : :
177 : : #if defined HAVE_SIGACTION && defined SA_SIGINFO
178 : : XAPIAN_NORETURN(static void handle_sig(int signum_, siginfo_t *si, void *));
179 : 0 : static void handle_sig(int signum_, siginfo_t *si, void *)
180 : : {
181 : : // Disable all our signal handlers to avoid problems if the signal
182 : : // handling code causes a signal.
183 : : struct sigaction sa;
184 : 0 : sa.sa_handler = SIG_DFL;
185 : 0 : sigemptyset(&sa.sa_mask);
186 : 0 : sa.sa_flags = 0;
187 : : // We set the handlers with SA_RESETHAND, but that will only reset the
188 : : // handler for the signal which fired.
189 [ # # ]: 0 : if (signum_ != SIGSEGV) sigaction(SIGSEGV, &sa, NULL);
190 [ # # ]: 0 : if (signum_ != SIGFPE) sigaction(SIGFPE, &sa, NULL);
191 [ # # ]: 0 : if (signum_ != SIGILL) sigaction(SIGILL, &sa, NULL);
192 : : # ifdef SIGBUS
193 [ # # ]: 0 : if (signum_ != SIGBUS) sigaction(SIGBUS, &sa, NULL);
194 : : # endif
195 : : # ifdef SIGSTKFLT
196 [ # # ]: 0 : if (signum_ != SIGSTKFLT) sigaction(SIGSTKFLT, &sa, NULL);
197 : : # endif
198 : 0 : signum = signum_;
199 : 0 : sigaddr = si->si_addr;
200 : 0 : SIGLONGJMP(jb, 1);
201 : : }
202 : :
203 : : #else
204 : :
205 : : XAPIAN_NORETURN(static void handle_sig(int signum_));
206 : : static void handle_sig(int signum_)
207 : : {
208 : : // Disable all our signal handlers to avoid problems if the signal
209 : : // handling code causes a signal.
210 : : signal(SIGSEGV, SIG_DFL);
211 : : signal(SIGFPE, SIG_DFL);
212 : : signal(SIGILL, SIG_DFL);
213 : : #ifdef SIGBUS
214 : : signal(SIGBUS, SIG_DFL);
215 : : #endif
216 : : #ifdef SIGSTKFLT
217 : : signal(SIGSTKFLT, SIG_DFL);
218 : : #endif
219 : : signum = signum_;
220 : : SIGLONGJMP(jb, 1);
221 : : }
222 : : #endif
223 : :
224 : : }
225 : :
226 : : class SignalRedirector {
227 : : private:
228 : : bool active;
229 : : public:
230 : 2972 : SignalRedirector() : active(false) { }
231 : 2972 : void activate() {
232 : 2972 : active = true;
233 : 2972 : signum = 0;
234 : 2972 : sigaddr = NULL;
235 : : // SA_SIGINFO not universal (e.g. not present on Linux < 2.2 and Hurd).
236 : : #if defined HAVE_SIGACTION && defined SA_SIGINFO
237 : : struct sigaction sa;
238 : 2972 : sa.sa_sigaction = handle_sig;
239 : 2972 : sigemptyset(&sa.sa_mask);
240 : 2972 : sa.sa_flags = SA_RESETHAND|SA_SIGINFO;
241 : 2972 : sigaction(SIGSEGV, &sa, NULL);
242 : 2972 : sigaction(SIGFPE, &sa, NULL);
243 : 2972 : sigaction(SIGILL, &sa, NULL);
244 : : # ifdef SIGBUS
245 : 2972 : sigaction(SIGBUS, &sa, NULL);
246 : : # endif
247 : : # ifdef SIGSTKFLT
248 : 2972 : sigaction(SIGSTKFLT, &sa, NULL);
249 : : # endif
250 : : #else
251 : : signal(SIGSEGV, handle_sig);
252 : : signal(SIGFPE, handle_sig);
253 : : signal(SIGILL, handle_sig);
254 : : # ifdef SIGBUS
255 : : signal(SIGBUS, handle_sig);
256 : : # endif
257 : : # ifdef SIGSTKFLT
258 : : signal(SIGSTKFLT, handle_sig);
259 : : # endif
260 : : #endif
261 : 2972 : }
262 : 2972 : ~SignalRedirector() {
263 [ + - ]: 2972 : if (active) {
264 : : #if defined HAVE_SIGACTION && defined SA_SIGINFO
265 : : struct sigaction sa;
266 : 2972 : sa.sa_handler = SIG_DFL;
267 : 2972 : sigemptyset(&sa.sa_mask);
268 : 2972 : sa.sa_flags = 0;
269 : 2972 : sigaction(SIGSEGV, &sa, NULL);
270 : 2972 : sigaction(SIGFPE, &sa, NULL);
271 : 2972 : sigaction(SIGILL, &sa, NULL);
272 : : # ifdef SIGBUS
273 : 2972 : sigaction(SIGBUS, &sa, NULL);
274 : : # endif
275 : : # ifdef SIGSTKFLT
276 : 2972 : sigaction(SIGSTKFLT, &sa, NULL);
277 : : # endif
278 : : #else
279 : : signal(SIGSEGV, SIG_DFL);
280 : : signal(SIGFPE, SIG_DFL);
281 : : signal(SIGILL, SIG_DFL);
282 : : # ifdef SIGBUS
283 : : signal(SIGBUS, SIG_DFL);
284 : : # endif
285 : : # ifdef SIGSTKFLT
286 : : signal(SIGSTKFLT, SIG_DFL);
287 : : # endif
288 : : #endif
289 : : }
290 : 2972 : }
291 : : };
292 : :
293 : : // A wrapper around the tests to trap exceptions,
294 : : // and avoid having to catch them in every test function.
295 : : // If this test driver is used for anything other than
296 : : // Xapian tests, then this ought to be provided by
297 : : // the client, really.
298 : : // return: test_driver::PASS, test_driver::FAIL, or test_driver::SKIP
299 : : test_driver::test_result
300 : 2969 : test_driver::runtest(const test_desc *test)
301 : : {
302 : : // This is used to make a note of how many times we've run the test
303 : 2969 : volatile int runcount = 0;
304 : :
305 : 2969 : FDTracker fdtracker;
306 : 2969 : fdtracker.init();
307 : :
308 : 3 : while (true) {
309 : 2972 : tout.str(string());
310 [ + - ]: 2972 : if (SIGSETJMP(jb, 1) == 0) {
311 : 2972 : SignalRedirector sig; // use object so signal handlers are reset
312 : : static bool catch_signals =
313 [ + + + - ]: 2972 : (getenv("XAPIAN_TESTSUITE_SIG_DFL") == NULL);
314 [ + - ]: 2972 : if (catch_signals) sig.activate();
315 : : try {
316 : 2972 : expected_exception = NULL;
317 : : #ifdef HAVE_VALGRIND
318 : : int vg_errs = 0;
319 : : long vg_leaks = 0, vg_dubious = 0, vg_reachable = 0;
320 : : if (vg_log_fd != -1) {
321 : : VALGRIND_DO_LEAK_CHECK;
322 : : vg_errs = VALGRIND_COUNT_ERRORS;
323 : : long dummy;
324 : : VALGRIND_COUNT_LEAKS(vg_leaks, vg_dubious, vg_reachable, dummy);
325 : : // Skip past any unread log output.
326 : : lseek(vg_log_fd, 0, SEEK_END);
327 : : }
328 : : #endif
329 [ - + ]: 2972 : if (!test->run()) {
330 : 0 : out << col_red << " FAILED" << col_reset;
331 : 0 : write_and_clear_tout();
332 : 0 : return FAIL;
333 : : }
334 [ + + ]: 2927 : if (backendmanager)
335 : 2813 : backendmanager->clean_up();
336 : : #ifdef HAVE_VALGRIND
337 : : if (vg_log_fd != -1) {
338 : : // We must empty tout before asking valgrind to perform its
339 : : // leak checks, otherwise the buffers holding the output
340 : : // may be identified as a memory leak (especially if >1K of
341 : : // output has been buffered it appears...)
342 : : tout.str(string());
343 : : #define REPORT_FAIL_VG(M) do { \
344 : : if (verbose) { \
345 : : while (true) { \
346 : : ssize_t c = read(vg_log_fd, buf, sizeof(buf)); \
347 : : if (c == 0 || (c < 0 && errno != EINTR)) break; \
348 : : if (c > 0) out << string(buf, c); \
349 : : } \
350 : : } \
351 : : out << " " << col_red << M << col_reset; \
352 : : } while (0)
353 : : // Record the current position so we can restore it so
354 : : // REPORT_FAIL_VG() gets the whole output.
355 : : off_t curpos = lseek(vg_log_fd, 0, SEEK_CUR);
356 : : char buf[1024];
357 : : while (true) {
358 : : ssize_t c = read(vg_log_fd, buf, sizeof(buf));
359 : : if (c == 0 || (c < 0 && errno != EINTR)) {
360 : : buf[0] = 0;
361 : : break;
362 : : }
363 : : if (c > 0) {
364 : : // Valgrind output has "==<pid>== \n" between
365 : : // report "records", so skip to the next occurrence
366 : : // of ' ' not followed by '\n'.
367 : : ssize_t i = 0;
368 : : do {
369 : : const char * spc;
370 : : spc = static_cast<const char *>(
371 : : memchr(buf + i, ' ', c - i));
372 : : if (!spc) {
373 : : i = c;
374 : : break;
375 : : }
376 : : i = spc - buf;
377 : : } while (++i < c && buf[i] == '\n');
378 : :
379 : : char *start = buf + i;
380 : : c -= i;
381 : : if (c > 128) c = 128;
382 : :
383 : : {
384 : : const char *p;
385 : : p = static_cast<const char*>(
386 : : memchr(start, '\n', c));
387 : : if (p != NULL) c = p - start;
388 : : }
389 : :
390 : : memmove(buf, start, c);
391 : : buf[c] = '\0';
392 : : break;
393 : : }
394 : : }
395 : : lseek(vg_log_fd, curpos, SEEK_SET);
396 : :
397 : : int vg_errs2 = VALGRIND_COUNT_ERRORS;
398 : : vg_errs = vg_errs2 - vg_errs;
399 : : VALGRIND_DO_LEAK_CHECK;
400 : : long vg_leaks2 = 0, vg_dubious2 = 0, vg_reachable2 = 0;
401 : : long dummy;
402 : : VALGRIND_COUNT_LEAKS(vg_leaks2, vg_dubious2, vg_reachable2,
403 : : dummy);
404 : : vg_leaks = vg_leaks2 - vg_leaks;
405 : : vg_dubious = vg_dubious2 - vg_dubious;
406 : : vg_reachable = vg_reachable2 - vg_reachable;
407 : : if (vg_errs) {
408 : : string fail_msg(buf);
409 : : if (fail_msg.empty())
410 : : fail_msg = "VALGRIND DETECTED A PROBLEM";
411 : : REPORT_FAIL_VG(fail_msg);
412 : : return FAIL;
413 : : }
414 : : if (vg_leaks > 0) {
415 : : REPORT_FAIL_VG("LEAKED " << vg_leaks << " BYTES");
416 : : return FAIL;
417 : : }
418 : : if (vg_dubious > 0) {
419 : : // If code deliberately holds onto blocks by a pointer
420 : : // not to the start (e.g. languages/utilities.c does)
421 : : // then we need to rerun the test to see if the leak is
422 : : // real...
423 : : if (runcount == 0) {
424 : : out << col_yellow << " PROBABLY LEAKED MEMORY - RETRYING TEST" << col_reset;
425 : : ++runcount;
426 : : // Ensure that any cached memory from fd tracking
427 : : // is allocated before we rerun the test.
428 : : (void)fdtracker.check();
429 : : continue;
430 : : }
431 : : REPORT_FAIL_VG("PROBABLY LEAKED " << vg_dubious << " BYTES");
432 : : return FAIL;
433 : : }
434 : : if (vg_reachable > 0) {
435 : : // C++ STL implementations often "horde" released
436 : : // memory - for GCC 3.4 and newer the runtest script
437 : : // sets GLIBCXX_FORCE_NEW=1 which will disable this
438 : : // behaviour so we avoid this issue, but for older
439 : : // GCC and other compilers this may be an issue.
440 : : //
441 : : // See also:
442 : : // http://valgrind.org/docs/FAQ/#faq.reports
443 : : //
444 : : // For now, just use runcount to rerun the test and see
445 : : // if more is leaked - hopefully this shouldn't give
446 : : // false positives.
447 : : if (runcount == 0) {
448 : : out << col_yellow << " POSSIBLE UNRELEASED MEMORY - RETRYING TEST" << col_reset;
449 : : ++runcount;
450 : : // Ensure that any cached memory from fd tracking
451 : : // is allocated before we rerun the test.
452 : : (void)fdtracker.check();
453 : : continue;
454 : : }
455 : : REPORT_FAIL_VG("FAILED TO RELEASE " << vg_reachable << " BYTES");
456 : : return FAIL;
457 : : }
458 : : }
459 : : #endif
460 [ + + ]: 2927 : if (!fdtracker.check()) {
461 [ + - ]: 3 : if (runcount == 0) {
462 : 3 : out << col_yellow << " POSSIBLE FDLEAK:" << fdtracker.get_message() << col_reset;
463 : 3 : ++runcount;
464 : 3 : continue;
465 : : }
466 : 0 : out << col_red << " FDLEAK:" << fdtracker.get_message() << col_reset;
467 : 0 : return FAIL;
468 : : }
469 : 0 : } catch (const TestFail &) {
470 : 0 : out << col_red << " FAILED" << col_reset;
471 : 0 : write_and_clear_tout();
472 : 0 : return FAIL;
473 : 90 : } catch (const TestSkip &) {
474 : 45 : out << col_yellow << " SKIPPED" << col_reset;
475 : 45 : write_and_clear_tout();
476 : 45 : return SKIP;
477 : 0 : } catch (const Xapian::Error &err) {
478 : 0 : string errclass = err.get_type();
479 [ # # # # ]: 0 : if (expected_exception && expected_exception == errclass) {
[ # # ]
480 : 0 : out << col_yellow << " C++ FAILED TO CATCH " << errclass << col_reset;
481 : 0 : return SKIP;
482 : : }
483 : 0 : out << " " << col_red << err.get_description() << col_reset;
484 : 0 : write_and_clear_tout();
485 : 0 : return FAIL;
486 : 0 : } catch (const string & msg) {
487 : 0 : out << col_red << " EXCEPTION std::string " << msg << col_reset;
488 : 0 : write_and_clear_tout();
489 : 0 : return FAIL;
490 : 0 : } catch (const std::exception & e) {
491 : 0 : out << " " << col_red;
492 : : #ifndef USE_RTTI
493 : : out << "std::exception";
494 : : #else
495 : 0 : const char * name = typeid(e).name();
496 : : # ifdef __GNUC__
497 : : // __cxa_demangle() apparently requires GCC >= 3.1.
498 : : // Demangle the name which GCC returns for type_info::name().
499 : : int status;
500 : 0 : char * realname = abi::__cxa_demangle(name, NULL, 0, &status);
501 [ # # ]: 0 : if (realname) {
502 : 0 : out << realname;
503 : 0 : free(realname);
504 : : } else {
505 : 0 : out << name;
506 : : }
507 : : # else
508 : : out << name;
509 : : # endif
510 : : #endif
511 : 0 : out << ": " << e.what() << col_reset;
512 : 0 : write_and_clear_tout();
513 : 0 : return FAIL;
514 : 0 : } catch (const char * msg) {
515 : 0 : out << col_red;
516 [ # # ]: 0 : if (msg) {
517 : 0 : out << " EXCEPTION char * " << msg;
518 : : } else {
519 : 0 : out << " EXCEPTION (char*)NULL";
520 : : }
521 : 0 : out << col_reset;
522 : 0 : write_and_clear_tout();
523 : 0 : return FAIL;
524 : 0 : } catch (...) {
525 : 0 : out << col_red << " UNKNOWN EXCEPTION" << col_reset;
526 : 0 : write_and_clear_tout();
527 : 0 : return FAIL;
528 : : }
529 [ + + ]: 2972 : return PASS;
530 : : }
531 : :
532 : : // Caught a signal.
533 : 0 : const char *signame = "SIGNAL";
534 : 0 : bool show_addr = true;
535 [ # # # # # : 0 : switch (signum) {
# ]
536 : 0 : case SIGSEGV: signame = "SIGSEGV"; break;
537 : 0 : case SIGFPE: signame = "SIGFPE"; break;
538 : 0 : case SIGILL: signame = "SIGILL"; break;
539 : : #ifdef SIGBUS
540 : 0 : case SIGBUS: signame = "SIGBUS"; break;
541 : : #endif
542 : : #ifdef SIGSTKFLT
543 : : case SIGSTKFLT:
544 : 0 : signame = "SIGSTKFLT";
545 : 0 : show_addr = false;
546 : : break;
547 : : #endif
548 : : }
549 : 0 : out << " " << col_red << signame;
550 [ # # ]: 0 : if (show_addr) {
551 : : char buf[40];
552 : 0 : sprintf(buf, " at %p", sigaddr);
553 : 0 : out << buf;
554 : : }
555 : 0 : out << col_reset;
556 : 0 : write_and_clear_tout();
557 : 0 : return FAIL;
558 : 2969 : }
559 : : }
560 : :
561 : : test_driver::result
562 : 217 : test_driver::run_tests(vector<string>::const_iterator b,
563 : : vector<string>::const_iterator e)
564 : : {
565 : 217 : return do_run_tests(b, e);
566 : : }
567 : :
568 : : test_driver::result
569 : 0 : test_driver::run_tests()
570 : : {
571 : 0 : const vector<string> blank;
572 : 0 : return do_run_tests(blank.begin(), blank.end());
573 : : }
574 : :
575 : : test_driver::result
576 : 217 : test_driver::do_run_tests(vector<string>::const_iterator b,
577 : : vector<string>::const_iterator e)
578 : : {
579 : 217 : set<string> m(b, e);
580 : 217 : bool check_name = !m.empty();
581 : :
582 : 217 : test_driver::result res;
583 : :
584 [ + + ]: 3186 : for (const test_desc *test = tests; test->name; test++) {
585 : 2969 : bool do_this_test = !check_name;
586 [ - + ][ # # ]: 2969 : if (!do_this_test && m.find(test->name) != m.end())
[ - + ][ # # ]
[ # # ][ - + ]
[ - + ]
587 : 0 : do_this_test = true;
588 [ - + ]: 2969 : if (!do_this_test) {
589 : : // if this test is "foo123" see if "foo" was listed
590 : : // this way "./testprog foo" can run foo1, foo2, etc.
591 : 0 : string t = test->name;
592 : : string::size_type i;
593 : 0 : i = t.find_last_not_of("0123456789") + 1;
594 [ # # ]: 0 : if (i != string::npos) {
595 : 0 : t.resize(i);
596 [ # # ]: 0 : if (m.find(t) != m.end()) do_this_test = true;
597 : 0 : }
598 : : }
599 [ + - ]: 2969 : if (do_this_test) {
600 : 2969 : out << "Running test: " << test->name << "...";
601 : 2969 : out.flush();
602 : 2969 : test_driver::test_result test_res = runtest(test);
603 [ + + ]: 2969 : if (backendmanager)
604 : 2857 : backendmanager->clean_up();
605 [ + - + - ]: 2969 : switch (test_res) {
606 : : case PASS:
607 : 2924 : ++res.succeeded;
608 [ + - ][ - + ]: 2924 : if (verbose || !use_cr) {
609 : 0 : out << col_green << " ok" << col_reset << endl;
610 : : } else {
611 : 2924 : out << "\r \r";
612 : : }
613 : 2924 : break;
614 : : case FAIL:
615 : 0 : ++res.failed;
616 : 0 : out << endl;
617 [ # # ]: 0 : if (abort_on_error) {
618 : 0 : throw "Test failed - aborting further tests";
619 : : }
620 : 0 : break;
621 : : case SKIP:
622 : 45 : ++res.skipped;
623 : 45 : out << endl;
624 : : // ignore the result of this test.
625 : : break;
626 : : }
627 : : }
628 : : }
629 : 217 : return res;
630 : : }
631 : :
632 : : void
633 : 0 : test_driver::usage()
634 : : {
635 : : cout << "Usage: " << argv0 << " [-v|--verbose] [-o|--abort-on-error] " << opt_help
636 : 0 : << "[TESTNAME]..." << endl;
637 : 0 : cout << " " << argv0 << " [-h|--help]" << endl;
638 : 0 : exit(1);
639 : : }
640 : :
641 : : /* Needs C linkage so we can pass it to atexit() without problems. */
642 : : extern "C" {
643 : : // Call upon program exit if there's more than one test run.
644 : : static void
645 : 1 : report_totals(void)
646 : : {
647 : 1 : test_driver::report(test_driver::total, "total");
648 : 1 : }
649 : : }
650 : :
651 : : void
652 : 15 : test_driver::report(const test_driver::result &r, const string &desc)
653 : : {
654 : : // Report totals at the end if we reported two or more subtotals.
655 [ + + ]: 15 : if (++runs == 2) atexit(report_totals);
656 : :
657 [ - + ][ # # ]: 15 : if (r.succeeded != 0 || r.failed != 0) {
658 : 15 : cout << argv0 << " " << desc << ": ";
659 : :
660 [ + - ]: 15 : if (r.failed == 0)
661 : 15 : cout << "All ";
662 : :
663 : 15 : cout << col_green << r.succeeded << col_reset << " tests passed";
664 : :
665 [ - + ]: 15 : if (r.failed != 0)
666 : 0 : cout << ", " << col_red << r.failed << col_reset << " failed";
667 : :
668 [ + + ]: 15 : if (r.skipped) {
669 : : cout << ", " << col_yellow << r.skipped << col_reset
670 : 14 : << " skipped." << endl;
671 : : } else {
672 : 1 : cout << "." << endl;
673 : : }
674 : : }
675 : 15 : }
676 : :
677 : : void
678 : 3 : test_driver::add_command_line_option(const string &l, char s, string * arg)
679 : : {
680 : 3 : short_opts.insert(make_pair<int, string *>(int(s), arg));
681 : 3 : opt_help += "[-";
682 : 3 : opt_help += s;
683 : 3 : opt_help += ' ';
684 : 3 : opt_help += l;
685 : 3 : opt_help += "] ";
686 : 3 : }
687 : :
688 : : void
689 : 5 : test_driver::parse_command_line(int argc, char **argv)
690 : : {
691 : 5 : argv0 = argv[0];
692 : :
693 : : #ifndef __WIN32__
694 : 5 : bool colourise = true;
695 : 5 : const char *p = getenv("XAPIAN_TESTSUITE_OUTPUT");
696 [ - + # # ]: 10 : if (p == NULL || !*p || strcmp(p, "auto") == 0) {
[ # # ]
697 : 5 : colourise = isatty(1);
698 [ # # ]: 0 : } else if (strcmp(p, "plain") == 0) {
699 : 0 : colourise = false;
700 : : }
701 [ + - ]: 5 : if (colourise) {
702 : 5 : col_red = "\x1b[1m\x1b[31m";
703 : 5 : col_green = "\x1b[1m\x1b[32m";
704 : 5 : col_yellow = "\x1b[1m\x1b[33m";
705 : 5 : col_reset = "\x1b[0m";
706 : 5 : use_cr = true;
707 : : }
708 : : #endif
709 : :
710 : : const struct option long_opts[] = {
711 : : {"verbose", no_argument, 0, 'v'},
712 : : {"abort-on-error", no_argument, 0, 'o'},
713 : : {"help", no_argument, 0, 'h'},
714 : : {NULL, 0, 0, 0}
715 : 5 : };
716 : :
717 : 5 : string short_opts_string = "voh";
718 : 5 : map<int, string *>::const_iterator i;
719 [ + + ]: 8 : for (i = short_opts.begin(); i != short_opts.end(); ++i) {
720 : 3 : short_opts_string += char(i->first);
721 : 3 : short_opts_string += ':';
722 : : }
723 : 5 : const char * opts = short_opts_string.c_str();
724 : :
725 : : int c;
726 [ - + ]: 5 : while ((c = gnu_getopt_long(argc, argv, opts, long_opts, 0)) != -1) {
727 [ # # # ]: 0 : switch (c) {
728 : : case 'v':
729 : 0 : verbose = true;
730 : 0 : break;
731 : : case 'o':
732 : 0 : abort_on_error = true;
733 : 0 : break;
734 : : default: {
735 : 0 : i = short_opts.find(c);
736 [ # # ]: 0 : if (i != short_opts.end()) {
737 : 0 : i->second->assign(optarg);
738 : 0 : break;
739 : : }
740 : : // -h or unrecognised option
741 : 0 : usage();
742 : : return; // usage() doesn't return ...
743 : : }
744 : : }
745 : : }
746 : :
747 [ - + ]: 5 : while (argv[optind]) {
748 : 0 : test_names.push_back(string(argv[optind]));
749 : 0 : optind++;
750 : 5 : }
751 : :
752 : : #ifdef HAVE_VALGRIND
753 : : if (RUNNING_ON_VALGRIND) {
754 : : if (getenv("XAPIAN_TESTSUITE_VALGRIND") != NULL) {
755 : : // Open the valgrind log file, and unlink it.
756 : : char fname[64];
757 : : sprintf(fname, ".valgrind.log.%lu", (unsigned long)getpid());
758 : : vg_log_fd = open(fname, O_RDONLY|O_NONBLOCK);
759 : : if (vg_log_fd == -1 && errno == ENOENT) {
760 : : // Older valgrind versions named the log output differently.
761 : : sprintf(fname, ".valgrind.log.pid%lu", (unsigned long)getpid());
762 : : vg_log_fd = open(fname, O_RDONLY|O_NONBLOCK);
763 : : }
764 : : if (vg_log_fd != -1) unlink(fname);
765 : : }
766 : : }
767 : : #endif
768 : : }
769 : :
770 : : int
771 : 217 : test_driver::run(const test_desc *tests)
772 : : {
773 : 217 : test_driver driver(tests);
774 : :
775 : 217 : test_driver::result myresult;
776 : 217 : myresult = driver.run_tests(test_names.begin(), test_names.end());
777 : :
778 : 217 : subtotal += myresult;
779 : :
780 : 217 : return bool(myresult.failed); // if 0, then everything passed
781 : : }
782 : :
783 : : bool
784 : 11441132 : TEST_EQUAL_DOUBLE_(double a, double b)
785 : : {
786 [ + + ]: 11441132 : if (a == b) return true;
787 : 11441132 : return (ceil(log10(max(fabs(a), fabs(b)))) - log10(fabs(a - b)) > DBL_DIG);
788 [ + - ][ + - ]: 15 : }
|