Back to index

nagios-plugins  1.4.16
tap.c
Go to the documentation of this file.
00001 /*-
00002  * Copyright (c) 2004 Nik Clayton
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  *
00014  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
00015  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00016  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00017  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
00018  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00019  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00020  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00021  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00022  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00023  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00024  * SUCH DAMAGE.
00025  */
00026 
00027 #include <ctype.h>
00028 #include <stdarg.h>
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 
00032 #include "tap.h"
00033 
00034 static int no_plan = 0;
00035 static int skip_all = 0;
00036 static int have_plan = 0;
00037 static unsigned int test_count = 0; /* Number of tests that have been run */
00038 static unsigned int e_tests = 0; /* Expected number of tests to run */
00039 static unsigned int failures = 0; /* Number of tests that failed */
00040 static char *todo_msg = NULL;
00041 static char *todo_msg_fixed = "libtap malloc issue";
00042 static int todo = 0;
00043 static int test_died = 0;
00044 
00045 /* Encapsulate the pthread code in a conditional.  In the absence of
00046    libpthread the code does nothing */
00047 #ifdef HAVE_LIBPTHREAD
00048 #include <pthread.h>
00049 static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
00050 # define LOCK pthread_mutex_lock(&M);
00051 # define UNLOCK pthread_mutex_unlock(&M);
00052 #else
00053 # define LOCK
00054 # define UNLOCK
00055 #endif
00056 
00057 static void _expected_tests(unsigned int);
00058 static void _tap_init(void);
00059 static void _cleanup(void);
00060 
00061 /*
00062  * Generate a test result.
00063  *
00064  * ok -- boolean, indicates whether or not the test passed.
00065  * test_name -- the name of the test, may be NULL
00066  * test_comment -- a comment to print afterwards, may be NULL
00067  */
00068 unsigned int
00069 _gen_result(int ok, const char *func, char *file, unsigned int line, 
00070            char *test_name, ...)
00071 {
00072        va_list ap;
00073        char *local_test_name = NULL;
00074        char *c;
00075        int name_is_digits;
00076 
00077        LOCK;
00078 
00079        test_count++;
00080 
00081        /* Start by taking the test name and performing any printf()
00082           expansions on it */
00083        if(test_name != NULL) {
00084               va_start(ap, test_name);
00085               vasprintf(&local_test_name, test_name, ap);
00086               va_end(ap);
00087 
00088               /* Make sure the test name contains more than digits
00089                  and spaces.  Emit an error message and exit if it
00090                  does */
00091               if(local_test_name) {
00092                      name_is_digits = 1;
00093                      for(c = local_test_name; *c != '\0'; c++) {
00094                             if(!isdigit(*c) && !isspace(*c)) {
00095                                    name_is_digits = 0;
00096                                    break;
00097                             }
00098                      }
00099 
00100                      if(name_is_digits) {
00101                             diag("    You named your test '%s'.  You shouldn't use numbers for your test names.", local_test_name);
00102                             diag("    Very confusing.");
00103                      }
00104               }
00105        }
00106 
00107        if(!ok) {
00108               printf("not ");
00109               failures++;
00110        }
00111 
00112        printf("ok %d", test_count);
00113 
00114        if(test_name != NULL) {
00115               printf(" - ");
00116 
00117               /* Print the test name, escaping any '#' characters it
00118                  might contain */
00119               if(local_test_name != NULL) {
00120                      flockfile(stdout);
00121                      for(c = local_test_name; *c != '\0'; c++) {
00122                             if(*c == '#')
00123                                    fputc('\\', stdout);
00124                             fputc((int)*c, stdout);
00125                      }
00126                      funlockfile(stdout);
00127               } else {      /* vasprintf() failed, use a fixed message */
00128                      printf("%s", todo_msg_fixed);
00129               }
00130        }
00131 
00132        /* If we're in a todo_start() block then flag the test as being
00133           TODO.  todo_msg should contain the message to print at this
00134           point.  If it's NULL then asprintf() failed, and we should
00135           use the fixed message.
00136 
00137           This is not counted as a failure, so decrement the counter if
00138           the test failed. */
00139        if(todo) {
00140               printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
00141               if(!ok)
00142                      failures--;
00143        }
00144 
00145        printf("\n");
00146 
00147        if(!ok)
00148               diag("    Failed %stest (%s:%s() at line %d)", 
00149                    todo ? "(TODO) " : "", file, func, line);
00150 
00151        free(local_test_name);
00152 
00153        UNLOCK;
00154 
00155        /* We only care (when testing) that ok is positive, but here we
00156           specifically only want to return 1 or 0 */
00157        return ok ? 1 : 0;
00158 }
00159 
00160 /*
00161  * Initialise the TAP library.  Will only do so once, however many times it's
00162  * called.
00163  */
00164 void
00165 _tap_init(void)
00166 {
00167        static int run_once = 0;
00168 
00169        LOCK;
00170 
00171        if(!run_once) {
00172               atexit(_cleanup);
00173 
00174               /* stdout needs to be unbuffered so that the output appears
00175                  in the same place relative to stderr output as it does 
00176                  with Test::Harness */
00177               setbuf(stdout, 0);
00178               run_once = 1;
00179        }
00180 
00181        UNLOCK;
00182 }
00183 
00184 /*
00185  * Note that there's no plan.
00186  */
00187 int
00188 plan_no_plan(void)
00189 {
00190 
00191        LOCK;
00192 
00193        _tap_init();
00194 
00195        if(have_plan != 0) {
00196               fprintf(stderr, "You tried to plan twice!\n");
00197               test_died = 1;
00198               UNLOCK;
00199               exit(255);
00200        }
00201 
00202        have_plan = 1;
00203        no_plan = 1;
00204 
00205        UNLOCK;
00206 
00207        return 0;
00208 }
00209 
00210 /*
00211  * Note that the plan is to skip all tests
00212  */
00213 int
00214 plan_skip_all(char *reason)
00215 {
00216 
00217        LOCK;
00218 
00219        _tap_init();
00220 
00221        skip_all = 1;
00222 
00223        printf("1..0");
00224 
00225        if(reason != NULL)
00226               printf(" # Skip %s", reason);
00227 
00228        printf("\n");
00229 
00230        UNLOCK;
00231 
00232        exit(0);
00233 }
00234 
00235 /*
00236  * Note the number of tests that will be run.
00237  */
00238 int
00239 plan_tests(unsigned int tests)
00240 {
00241 
00242        LOCK;
00243 
00244        _tap_init();
00245 
00246        if(have_plan != 0) {
00247               fprintf(stderr, "You tried to plan twice!\n");
00248               test_died = 1;
00249               UNLOCK;
00250               exit(255);
00251        }
00252 
00253        if(tests == 0) {
00254               fprintf(stderr, "You said to run 0 tests!  You've got to run something.\n");
00255               test_died = 1;
00256               UNLOCK;
00257               exit(255);
00258        }
00259 
00260        have_plan = 1;
00261 
00262        _expected_tests(tests);
00263 
00264        UNLOCK;
00265 
00266        return 0;
00267 }
00268 
00269 unsigned int
00270 diag(char *fmt, ...)
00271 {
00272        va_list ap;
00273 
00274        LOCK;
00275 
00276        fputs("# ", stderr);
00277 
00278        va_start(ap, fmt);
00279        vfprintf(stderr, fmt, ap);
00280        va_end(ap);
00281 
00282        fputs("\n", stderr);
00283 
00284        UNLOCK;
00285 
00286        return 0;
00287 }
00288 
00289 void
00290 _expected_tests(unsigned int tests)
00291 {
00292 
00293        LOCK;
00294 
00295        printf("1..%d\n", tests);
00296        e_tests = tests;
00297 
00298        UNLOCK;
00299 }
00300 
00301 int
00302 skip(unsigned int n, char *fmt, ...)
00303 {
00304        va_list ap;
00305        char *skip_msg;
00306 
00307        LOCK;
00308 
00309        va_start(ap, fmt);
00310        asprintf(&skip_msg, fmt, ap);
00311        va_end(ap);
00312 
00313        while(n-- > 0) {
00314               test_count++;
00315               printf("ok %d # skip %s\n", test_count, 
00316                      skip_msg != NULL ? 
00317                      skip_msg : "libtap():malloc() failed");
00318        }
00319 
00320        free(skip_msg);
00321 
00322        UNLOCK;
00323 
00324        return 1;
00325 }
00326 
00327 void
00328 todo_start(char *fmt, ...)
00329 {
00330        va_list ap;
00331 
00332        LOCK;
00333 
00334        va_start(ap, fmt);
00335        vasprintf(&todo_msg, fmt, ap);
00336        va_end(ap);
00337 
00338        todo = 1;
00339 
00340        UNLOCK;
00341 }
00342 
00343 void
00344 todo_end(void)
00345 {
00346 
00347        LOCK;
00348 
00349        todo = 0;
00350        free(todo_msg);
00351 
00352        UNLOCK;
00353 }
00354 
00355 int
00356 exit_status(void)
00357 {
00358        int r;
00359 
00360        LOCK;
00361 
00362        /* If there's no plan, just return the number of failures */
00363        if(no_plan || !have_plan) {
00364               UNLOCK;
00365               return failures;
00366        }
00367 
00368        /* Ran too many tests?  Return the number of tests that were run
00369           that shouldn't have been */
00370        if(e_tests < test_count) {
00371               r = test_count - e_tests;
00372               UNLOCK;
00373               return r;
00374        }
00375 
00376        /* Return the number of tests that failed + the number of tests 
00377           that weren't run */
00378        r = failures + e_tests - test_count;
00379        UNLOCK;
00380 
00381        return r;
00382 }
00383 
00384 /*
00385  * Cleanup at the end of the run, produce any final output that might be
00386  * required.
00387  */
00388 void
00389 _cleanup(void)
00390 {
00391 
00392        LOCK;
00393 
00394        /* If plan_no_plan() wasn't called, and we don't have a plan,
00395           and we're not skipping everything, then something happened
00396           before we could produce any output */
00397        if(!no_plan && !have_plan && !skip_all) {
00398               diag("Looks like your test died before it could output anything.");
00399               UNLOCK;
00400               return;
00401        }
00402 
00403        if(test_died) {
00404               diag("Looks like your test died just after %d.", test_count);
00405               UNLOCK;
00406               return;
00407        }
00408 
00409 
00410        /* No plan provided, but now we know how many tests were run, and can
00411           print the header at the end */
00412        if(!skip_all && (no_plan || !have_plan)) {
00413               printf("1..%d\n", test_count);
00414        }
00415 
00416        if((have_plan && !no_plan) && e_tests < test_count) {
00417               diag("Looks like you planned %d tests but ran %d extra.",
00418                    e_tests, test_count - e_tests);
00419               UNLOCK;
00420               return;
00421        }
00422 
00423        if((have_plan || !no_plan) && e_tests > test_count) {
00424               diag("Looks like you planned %d tests but only ran %d.",
00425                    e_tests, test_count);
00426               UNLOCK;
00427               return;
00428        }
00429 
00430        if(failures)
00431               diag("Looks like you failed %d tests of %d.", 
00432                    failures, test_count);
00433 
00434        UNLOCK;
00435 }