X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=common-src%2Ftestutils.c;h=3d642afe82d18b09fe1f7eabe467c4a739d199bd;hb=4f0b86f7a23848c16cfe82fae81e639917fcff27;hp=a614b7c2b83c3aee9241e0d6c2d60c4ee806a39f;hpb=2627875b7d18858bc1f9f7652811e4d8c15a23eb;p=debian%2Famanda diff --git a/common-src/testutils.c b/common-src/testutils.c index a614b7c..3d642af 100644 --- a/common-src/testutils.c +++ b/common-src/testutils.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2008 Zmanda Inc. All Rights Reserved. + * Copyright (c) 2008-2012 Zmanda, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -14,65 +14,137 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * Contact information: Zmanda Inc., 465 S Mathlida Ave, Suite 300 + * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300 * Sunnyvale, CA 94086, USA, or: http://www.zmanda.com */ #include "amanda.h" #include "testutils.h" -int tu_debugging_enabled = FALSE; +gboolean tu_debugging_enabled = FALSE; + +static gboolean run_all = TRUE; +static gboolean ignore_timeouts = FALSE; +static gboolean skip_fork = FALSE; +static gboolean only_one = FALSE; +static gboolean loop_forever = FALSE; +static guint64 occurrences = 1; static void alarm_hdlr(int sig G_GNUC_UNUSED) { - fprintf(stderr, "-- TEST TIMED OUT --\n"); + g_fprintf(stderr, "-- TEST TIMED OUT --\n"); exit(1); } -/* Call testfn in a forked process, such that any failures will trigger a - * test failure, but allow the other tests to proceed. +/* + * Run a single test, accouting for the timeout (if timeouts are not ignored) + * and output runtime information (in milliseconds) at the end of the run. + * Output avg/min/max only if the number of runs is strictly greater than one. + */ + +static gboolean run_one_test(TestUtilsTest *test) +{ + guint64 count = 0; + gboolean ret = TRUE; + const char *test_name = test->name; + GTimer *timer; + + gdouble total = 0.0, thisrun, mintime = G_MAXDOUBLE, maxtime = G_MINDOUBLE; + + signal(SIGALRM, alarm_hdlr); + + timer = g_timer_new(); + + while (count++ < occurrences) { + if (!ignore_timeouts) + alarm(test->timeout); + + g_timer_start(timer); + ret = test->fn(); + g_timer_stop(timer); + + thisrun = g_timer_elapsed(timer, NULL); + total += thisrun; + if (mintime > thisrun) + mintime = thisrun; + if (maxtime < thisrun) + maxtime = thisrun; + + if (!ret) + break; + } + + g_timer_destroy(timer); + + if (loop_forever) + goto out; + + if (ret) { + g_fprintf(stderr, " PASS %s (total: %.06f", test_name, total); + if (occurrences > 1) { + total /= (gdouble) occurrences; + g_fprintf(stderr, ", avg/min/max: %.06f/%.06f/%.06f", + total, mintime, maxtime); + } + g_fprintf(stderr, ")\n"); + } else + g_fprintf(stderr, " FAIL %s (run %ju of %ju, after %.06f secs)\n", + test_name, (uintmax_t)count, (uintmax_t)occurrences, total); + +out: + return ret; +} + +/* + * Call testfn in a forked process, such that any failures will trigger a + * test failure, but allow the other tests to proceed. The only exception is if + * -n is supplied at the command line, but in this case only one test is allowed + * to run. */ -static int -callinfork(TestUtilsTest *test, int ignore_timeouts) + +static gboolean +callinfork(TestUtilsTest *test) { pid_t pid; - int success; amwait_t status; + gboolean result; - switch (pid = fork()) { - case 0: /* child */ - /* kill the test after a bit */ - signal(SIGALRM, alarm_hdlr); - if (!ignore_timeouts) alarm(test->timeout); - - success = test->fn(); - exit(success? 0:1); - - case -1: - perror("fork"); - exit(1); - - default: /* parent */ - waitpid(pid, &status, 0); - if (status == 0) { - fprintf(stderr, " PASS %s\n", test->name); - } else { - fprintf(stderr, " FAIL %s\n", test->name); - } - return status == 0; + if (skip_fork) + result = run_one_test(test); + else { + switch (pid = fork()) { + case 0: /* child */ + exit(run_one_test(test) ? 0 : 1); + + case -1: + perror("fork"); + exit(1); + + default: /* parent */ + waitpid(pid, &status, 0); + result = status == 0; + break; + } } + + return result; } static void usage( TestUtilsTest *tests) { - printf("USAGE: [-d] [-h] [testname [testname [..]]]\n" + printf("USAGE: [options] [testname [testname [..]]]\n" "\n" + "Options can be one of:\n" + "\n" "\t-h: this message\n" "\t-d: print debugging messages\n" "\t-t: ignore timeouts\n" + "\t-n: do not fork\n" + "\t-c : run each test times instead of only once\n" + "\t-l: loop the same test repeatedly (use with -n for leak checks)\n" "\n" "If no test names are specified, all tests are run. Available tests:\n" "\n"); @@ -98,9 +170,7 @@ testutils_run_tests( TestUtilsTest *tests) { TestUtilsTest *t; - int run_all = 1; - int success; - int ignore_timeouts = 0; + gboolean success; /* first_parse the command line */ while (argc > 1) { @@ -108,6 +178,29 @@ testutils_run_tests( tu_debugging_enabled = TRUE; } else if (strcmp(argv[1], "-t") == 0) { ignore_timeouts = TRUE; + } else if (strcmp(argv[1], "-n") == 0) { + skip_fork = TRUE; + only_one = TRUE; + } else if (strcmp(argv[1], "-l") == 0) { + loop_forever = TRUE; + only_one = TRUE; + } else if (strcmp(argv[1], "-c") == 0) { + char *p; + argv++, argc--; + occurrences = g_ascii_strtoull(argv[1], &p, 10); + if (errno == ERANGE) { + g_fprintf(stderr, "%s is out of range\n", argv[1]); + exit(1); + } + if (*p) { + g_fprintf(stderr, "The -c option expects a positive integer " + "as an argument, but \"%s\" isn't\n", argv[1]); + exit(1); + } + if (occurrences == 0) { + g_fprintf(stderr, "Sorry, I will not run tests 0 times\n"); + exit(1); + } } else if (strcmp(argv[1], "-h") == 0) { usage(tests); return 1; @@ -123,16 +216,44 @@ testutils_run_tests( } if (!found) { - fprintf(stderr, "Test '%s' not found\n", argv[1]); + g_fprintf(stderr, "Test '%s' not found\n", argv[1]); return 1; } - run_all = 0; + run_all = FALSE; } argc--; argv++; } + /* + * Check whether the -c option has been given. In this case, -l must not be + * specified at the same time. + */ + if (occurrences > 1 && loop_forever) { + g_fprintf(stderr, "-c and -l are incompatible\n"); + exit(1); + } + + if (run_all) { + for (t = tests; t->fn; t++) + t->selected = 1; + } + + /* check only_one */ + if (only_one) { + int num_tests = 0; + for (t = tests; t->fn; t++) { + if (t->selected) + num_tests++; + } + + if (num_tests > 1) { + g_fprintf(stderr, "Only run one test with '-n'\n"); + return 1; + } + } + /* Make sure g_critical and g_error will exit */ g_log_set_always_fatal(G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); @@ -142,12 +263,14 @@ testutils_run_tests( } /* Now actually run the tests */ - success = 1; + success = TRUE; for (t = tests; t->fn; t++) { - if (run_all || t->selected) { - success = callinfork(t, ignore_timeouts) && success; - } + if (t->selected) { + do { + success = callinfork(t) && success; + } while (loop_forever); + } } - return success? 0:1; + return success ? 0 : 1; }