tests: Automatically time TAP tests
[lttng-tools.git] / tests / utils / tap / tap.c
index 5d64d844cbd3975ac7735e0fa75f837cf858ade1..1ef417fed34a70b4897918815c2c783db4840fd5 100644 (file)
@@ -1,36 +1,22 @@
-/*-
- * Copyright (c) 2004 Nik Clayton
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
  *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * Copyright (C) 2004 Nik Clayton
+ * Copyright (C) 2017 Jérémie Galarneau
  */
 
-#define _GNU_SOURCE
+#include "../utils.h"
+#include "common/compat/time.hpp"
+#include "tap.h"
+
+#include <assert.h>
 #include <ctype.h>
+#include <limits.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-
-#include "tap.h"
+#include <string.h>
+#include <time.h>
 
 static int no_plan = 0;
 static int skip_all = 0;
@@ -39,26 +25,40 @@ static unsigned int test_count = 0; /* Number of tests that have been run */
 static unsigned int e_tests = 0; /* Expected number of tests to run */
 static unsigned int failures = 0; /* Number of tests that failed */
 static char *todo_msg = NULL;
-static char *todo_msg_fixed = "libtap malloc issue";
+static const char *todo_msg_fixed = "libtap malloc issue";
 static int todo = 0;
 static int test_died = 0;
+static int time_tests = 1;
+struct timespec last_time;
 
 /* Encapsulate the pthread code in a conditional.  In the absence of
    libpthread the code does nothing */
 #ifdef HAVE_LIBPTHREAD
 #include <pthread.h>
 static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
-# define LOCK pthread_mutex_lock(&M);
-# define UNLOCK pthread_mutex_unlock(&M);
+#define LOCK   pthread_mutex_lock(&M);
+#define UNLOCK pthread_mutex_unlock(&M);
 #else
-# define LOCK
-# define UNLOCK
+#define LOCK
+#define UNLOCK
 #endif
 
 static void _expected_tests(unsigned int);
 static void _tap_init(void);
 static void _cleanup(void);
 
+#ifdef __MINGW32__
+static inline void flockfile(FILE *filehandle)
+{
+       return;
+}
+
+static inline void funlockfile(FILE *filehandle)
+{
+       return;
+}
+#endif
+
 /*
  * Generate a test result.
  *
@@ -66,9 +66,8 @@ static void _cleanup(void);
  * test_name -- the name of the test, may be NULL
  * test_comment -- a comment to print afterwards, may be NULL
  */
-unsigned int
-_gen_result(int ok, const char *func, char *file, unsigned int line,
-           char *test_name, ...)
+unsigned int _gen_result(
+       int ok, const char *func, const char *file, unsigned int line, const char *test_name, ...)
 {
        va_list ap;
        char *local_test_name = NULL;
@@ -81,7 +80,7 @@ _gen_result(int ok, const char *func, char *file, unsigned int line,
 
        /* Start by taking the test name and performing any printf()
           expansions on it */
-       if(test_name != NULL) {
+       if (test_name != NULL) {
                va_start(ap, test_name);
                if (vasprintf(&local_test_name, test_name, ap) == -1) {
                        local_test_name = NULL;
@@ -91,43 +90,44 @@ _gen_result(int ok, const char *func, char *file, unsigned int line,
                /* Make sure the test name contains more than digits
                   and spaces.  Emit an error message and exit if it
                   does */
-               if(local_test_name) {
+               if (local_test_name) {
                        name_is_digits = 1;
-                       for(c = local_test_name; *c != '\0'; c++) {
-                               if(!isdigit(*c) && !isspace(*c)) {
+                       for (c = local_test_name; *c != '\0'; c++) {
+                               if (!isdigit((unsigned char) *c) && !isspace((unsigned char) *c)) {
                                        name_is_digits = 0;
                                        break;
                                }
                        }
 
-                       if(name_is_digits) {
-                               diag("    You named your test '%s'.  You shouldn't use numbers for your test names.", local_test_name);
+                       if (name_is_digits) {
+                               diag("    You named your test '%s'.  You shouldn't use numbers for your test names.",
+                                    local_test_name);
                                diag("    Very confusing.");
                        }
                }
        }
 
-       if(!ok) {
+       if (!ok) {
                printf("not ");
                failures++;
        }
 
        printf("ok %d", test_count);
 
-       if(test_name != NULL) {
+       if (test_name != NULL) {
                printf(" - ");
 
                /* Print the test name, escaping any '#' characters it
                   might contain */
-               if(local_test_name != NULL) {
+               if (local_test_name != NULL) {
                        flockfile(stdout);
-                       for(c = local_test_name; *c != '\0'; c++) {
-                               if(*c == '#')
+                       for (c = local_test_name; *c != '\0'; c++) {
+                               if (*c == '#')
                                        fputc('\\', stdout);
-                               fputc((int)*c, stdout);
+                               fputc((int) *c, stdout);
                        }
                        funlockfile(stdout);
-               } else {        /* vasprintf() failed, use a fixed message */
+               } else { /* vasprintf() failed, use a fixed message */
                        printf("%s", todo_msg_fixed);
                }
        }
@@ -139,20 +139,24 @@ _gen_result(int ok, const char *func, char *file, unsigned int line,
 
           This is not counted as a failure, so decrement the counter if
           the test failed. */
-       if(todo) {
+       if (todo) {
                printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
-               if(!ok)
+               if (!ok)
                        failures--;
        }
 
        printf("\n");
+       _output_test_time();
 
-       if(!ok) {
-               if(getenv("HARNESS_ACTIVE") != NULL)
+       if (!ok) {
+               if (getenv("HARNESS_ACTIVE") != NULL)
                        fputs("\n", stderr);
 
                diag("    Failed %stest (%s:%s() at line %d)",
-                    todo ? "(TODO) " : "", file, func, line);
+                    todo ? "(TODO) " : "",
+                    file,
+                    func,
+                    line);
        }
        free(local_test_name);
 
@@ -167,34 +171,41 @@ _gen_result(int ok, const char *func, char *file, unsigned int line,
  * Initialise the TAP library.  Will only do so once, however many times it's
  * called.
  */
-void
-_tap_init(void)
+void _tap_init(void)
 {
        static int run_once = 0;
 
-       if(!run_once) {
+       if (!run_once) {
                atexit(_cleanup);
 
                /* stdout needs to be unbuffered so that the output appears
                   in the same place relative to stderr output as it does
                   with Test::Harness */
                setbuf(stdout, 0);
+
+               char *autotime_env = getenv("TAP_AUTOTIME");
+               if (autotime_env != NULL) {
+                       time_tests = atoi(autotime_env);
+                       if (time_tests != 0) {
+                               time_tests = 1;
+                       }
+               }
+
                run_once = 1;
        }
+       lttng_clock_gettime(CLOCK_MONOTONIC, &last_time);
 }
 
 /*
  * Note that there's no plan.
  */
-int
-plan_no_plan(void)
+int plan_no_plan(void)
 {
-
        LOCK;
 
        _tap_init();
 
-       if(have_plan != 0) {
+       if (have_plan != 0) {
                fprintf(stderr, "You tried to plan twice!\n");
                test_died = 1;
                UNLOCK;
@@ -212,10 +223,8 @@ plan_no_plan(void)
 /*
  * Note that the plan is to skip all tests
  */
-int
-plan_skip_all(char *reason)
+int plan_skip_all(const char *reason)
 {
-
        LOCK;
 
        _tap_init();
@@ -224,7 +233,7 @@ plan_skip_all(char *reason)
 
        printf("1..0");
 
-       if(reason != NULL)
+       if (reason != NULL)
                printf(" # Skip %s", reason);
 
        printf("\n");
@@ -237,22 +246,20 @@ plan_skip_all(char *reason)
 /*
  * Note the number of tests that will be run.
  */
-int
-plan_tests(unsigned int tests)
+int plan_tests(unsigned int tests)
 {
-
        LOCK;
 
        _tap_init();
 
-       if(have_plan != 0) {
+       if (have_plan != 0) {
                fprintf(stderr, "You tried to plan twice!\n");
                test_died = 1;
                UNLOCK;
                exit(255);
        }
 
-       if(tests == 0) {
+       if (tests == 0) {
                fprintf(stderr, "You said to run 0 tests!  You've got to run something.\n");
                test_died = 1;
                UNLOCK;
@@ -268,8 +275,7 @@ plan_tests(unsigned int tests)
        return e_tests;
 }
 
-unsigned int
-diag(char *fmt, ...)
+unsigned int diag(const char *fmt, ...)
 {
        va_list ap;
 
@@ -284,33 +290,52 @@ diag(char *fmt, ...)
        return 0;
 }
 
-void
-_expected_tests(unsigned int tests)
+void diag_multiline(const char *val)
 {
+       size_t len, i, line_start_idx = 0;
+
+       assert(val);
+       len = strlen(val);
+
+       for (i = 0; i < len; i++) {
+               int line_length;
+
+               if (val[i] != '\n') {
+                       continue;
+               }
+
+               assert((i - line_start_idx + 1) <= INT_MAX);
+               line_length = i - line_start_idx + 1;
+               fprintf(stderr, "# %.*s", line_length, &val[line_start_idx]);
+               line_start_idx = i + 1;
+       }
+}
 
+void _expected_tests(unsigned int tests)
+{
        printf("1..%d\n", tests);
        e_tests = tests;
 }
 
-int
-skip(unsigned int n, char *fmt, ...)
+int skip(unsigned int n, const char *fmt, ...)
 {
        va_list ap;
-       char *skip_msg;
+       char *skip_msg = NULL;
 
        LOCK;
 
        va_start(ap, fmt);
-       if (asprintf(&skip_msg, fmt, ap) == -1) {
+       if (vasprintf(&skip_msg, fmt, ap) == -1) {
                skip_msg = NULL;
        }
        va_end(ap);
 
-       while(n-- > 0) {
+       while (n-- > 0) {
                test_count++;
-               printf("ok %d # skip %s\n", test_count,
-                      skip_msg != NULL ?
-                      skip_msg : "libtap():malloc() failed");
+               printf("ok %d # skip %s\n",
+                      test_count,
+                      skip_msg != NULL ? skip_msg : "libtap():malloc() failed");
+               _output_test_time();
        }
 
        free(skip_msg);
@@ -320,8 +345,21 @@ skip(unsigned int n, char *fmt, ...)
        return 1;
 }
 
-void
-todo_start(char *fmt, ...)
+void _output_test_time(void)
+{
+       struct timespec new_time;
+       int64_t time_ns;
+       if (time_tests) {
+               lttng_clock_gettime(CLOCK_MONOTONIC, &new_time);
+               time_ns = elapsed_time_ns(&last_time, &new_time);
+               printf("  ---\n    duration_ms: %ld.%ld\n  ...\n",
+                      time_ns / 1000000,
+                      time_ns % 1000000);
+       }
+       lttng_clock_gettime(CLOCK_MONOTONIC, &last_time);
+}
+
+void todo_start(const char *fmt, ...)
 {
        va_list ap;
 
@@ -338,10 +376,8 @@ todo_start(char *fmt, ...)
        UNLOCK;
 }
 
-void
-todo_end(void)
+void todo_end(void)
 {
-
        LOCK;
 
        todo = 0;
@@ -350,22 +386,21 @@ todo_end(void)
        UNLOCK;
 }
 
-int
-exit_status(void)
+int exit_status(void)
 {
        int r;
 
        LOCK;
 
        /* If there's no plan, just return the number of failures */
-       if(no_plan || !have_plan) {
+       if (no_plan || !have_plan) {
                UNLOCK;
                return failures;
        }
 
        /* Ran too many tests?  Return the number of tests that were run
           that shouldn't have been */
-       if(e_tests < test_count) {
+       if (e_tests < test_count) {
                r = test_count - e_tests;
                UNLOCK;
                return r;
@@ -383,51 +418,54 @@ exit_status(void)
  * Cleanup at the end of the run, produce any final output that might be
  * required.
  */
-void
-_cleanup(void)
+void _cleanup(void)
 {
-
        LOCK;
 
        /* If plan_no_plan() wasn't called, and we don't have a plan,
           and we're not skipping everything, then something happened
           before we could produce any output */
-       if(!no_plan && !have_plan && !skip_all) {
+       if (!no_plan && !have_plan && !skip_all) {
                diag("Looks like your test died before it could output anything.");
                UNLOCK;
                return;
        }
 
-       if(test_died) {
+       if (test_died) {
                diag("Looks like your test died just after %d.", test_count);
                UNLOCK;
                return;
        }
 
-
        /* No plan provided, but now we know how many tests were run, and can
           print the header at the end */
-       if(!skip_all && (no_plan || !have_plan)) {
+       if (!skip_all && (no_plan || !have_plan)) {
                printf("1..%d\n", test_count);
        }
 
-       if((have_plan && !no_plan) && e_tests < test_count) {
+       if ((have_plan && !no_plan) && e_tests < test_count) {
                diag("Looks like you planned %d %s but ran %d extra.",
-                    e_tests, e_tests == 1 ? "test" : "tests", test_count - e_tests);
+                    e_tests,
+                    e_tests == 1 ? "test" : "tests",
+                    test_count - e_tests);
                UNLOCK;
                return;
        }
 
-       if((have_plan || !no_plan) && e_tests > test_count) {
+       if ((have_plan || !no_plan) && e_tests > test_count) {
                diag("Looks like you planned %d %s but only ran %d.",
-                    e_tests, e_tests == 1 ? "test" : "tests", test_count);
+                    e_tests,
+                    e_tests == 1 ? "test" : "tests",
+                    test_count);
                UNLOCK;
                return;
        }
 
-       if(failures)
+       if (failures)
                diag("Looks like you failed %d %s of %d.",
-                    failures, failures == 1 ? "test" : "tests", test_count);
+                    failures,
+                    failures == 1 ? "test" : "tests",
+                    test_count);
 
        UNLOCK;
 }
This page took 0.031113 seconds and 4 git commands to generate.