assert.h

Single-file assertion library for C
git clone git://git.finwo.net/lib/assert.h
Log | Files | Refs | README

test.h (5616B)


      1 /// assert.h
      2 /// ========
      3 ///
      4 /// Single-file unit-testing library for C
      5 ///
      6 /// Features
      7 /// --------
      8 ///
      9 /// - Single header file, no other library dependencies
     10 /// - Simple ANSI C. The library should work with virtually every C(++) compiler on
     11 ///   virtually any playform
     12 /// - Reporting of assertion failures, including the expression and location of the
     13 ///   failure
     14 /// - Stops test on first failed assertion
     15 /// - ANSI color output for maximum visibility
     16 /// - Easily embeddable in applications for runtime tests or separate testing
     17 ///   applications
     18 ///
     19 /// Todo
     20 /// ----
     21 ///
     22 /// - Disable assertions on definition, to allow production build without source modifications
     23 ///
     24 /// Example Usage
     25 /// -------------
     26 ///
     27 /// ```C
     28 /// #include "finwo/assert.h"
     29 /// #include "mylib.h"
     30 ///
     31 /// void test_sheep() {
     32 ///   ASSERT("Sheep are cool", are_sheep_cool());
     33 ///   ASSERT_EQUALS(4, sheep.legs);
     34 /// }
     35 ///
     36 /// void test_cheese() {
     37 ///   ASSERT("Cheese is tangy", cheese.tanginess > 0);
     38 ///   ASSERT_STRING_EQUALS("Wensleydale", cheese.name);
     39 /// }
     40 ///
     41 /// int main() {
     42 ///   RUN(test_sheep);
     43 ///   RUN(test_cheese);
     44 ///   return TEST_REPORT();
     45 /// }
     46 /// ```
     47 ///
     48 /// To run the tests, compile the tests as a binary and run it.
     49 
     50 #ifndef __TINYTEST_INCLUDED_H__
     51 #define __TINYTEST_INCLUDED_H__
     52 
     53 #include <stdio.h>
     54 #include <stdlib.h>
     55 #include <string.h>
     56 
     57 ///
     58 /// Helper function for string comparison to avoid NULL warnings
     59 ///
     60 static inline int _string_equals(const char *a, const char *b) {
     61     if (a == NULL && b == NULL) return 1;
     62     if (a == NULL || b == NULL) return 0;
     63     return strcmp(a, b) == 0;
     64 }
     65 
     66 ///
     67 /// API
     68 /// ---
     69 ///
     70 
     71 ///
     72 /// ### Macros
     73 ///
     74 
     75 
     76 /// <details>
     77 ///   <summary>ASSERT(msg, expression)</summary>
     78 ///
     79 ///   Perform an assertion
     80 ///<C
     81 #define ASSERT(msg, expression) if (!tap_assert(__FILE__, __LINE__, (msg), (#expression), (expression) ? 1 : 0)) return
     82 ///>
     83 /// </details>
     84 
     85 
     86 /// <details>
     87 ///   <summary>ASSERT_EQUALS(expected, actual)</summary>
     88 ///
     89 ///   Perform an equal assertion
     90 ///<C
     91 /* Convenient assertion methods */
     92 /* TODO: Generate readable error messages for assert_equals or assert_str_equals */
     93 #define ASSERT_EQUALS(expected, actual) ASSERT((#actual), (expected) == (actual))
     94 ///>
     95 /// </details>
     96 
     97 /// <details>
     98 ///   <summary>ASSERT_STRING_EQUALS(expected, actual)</summary>
     99 ///
    100 ///   Perform an equal string assertion
    101 ///<
    102 #define ASSERT_STRING_EQUALS(expected, actual) ASSERT((#actual), _string_equals((expected),(actual)))
    103 ///>
    104 /// </details>
    105 
    106 /// <details>
    107 ///   <summary>RUN(fn)</summary>
    108 ///
    109 ///   Run a test suite/function containing assertions
    110 ///<C
    111 #define RUN(test_function) tap_execute((#test_function), (test_function))
    112 ///>
    113 /// </details>
    114 
    115 /// <details>
    116 ///   <summary>TEST_REPORT()</summary>
    117 ///
    118 ///   Report on the tests that have been run
    119 ///<C
    120 #define TEST_REPORT() tap_report()
    121 ///>
    122 /// </details>
    123 
    124 ///
    125 /// Extras
    126 /// ------
    127 ///
    128 /// ### Disable color
    129 ///
    130 /// If you want to disable color during the assertions, because you want to
    131 /// interpret the output for example (it is "tap" format after all), you can
    132 /// define `NO_COLOR` during compilation to disable color output.
    133 ///
    134 /// ```sh
    135 /// cc -D NO_COLOR source.c -o test
    136 /// ```
    137 ///
    138 /// ### Silent assertions
    139 ///
    140 /// You can also fully disable output for assertions by defining the
    141 /// `ASSERT_SILENT` macro. This will fully disable the printf performed after
    142 /// the assertion is performed.
    143 ///
    144 /// ```sh
    145 /// cc -D ASSERT_SILENT source.c -o test
    146 /// ```
    147 ///
    148 /// ### Silent reporting
    149 ///
    150 /// If you do not want the report to be displayed at the end, you can define the
    151 /// `REPORT_SILENT` macro. This will disable the printf during reporting and
    152 /// only keep the return code.
    153 ///
    154 /// ```sh
    155 /// cc -D REPORT_SILENT source.c -o test
    156 /// ```
    157 ///
    158 
    159 #ifdef NO_COLOR
    160 #define TAP_COLOR_CODE  ""
    161 #define TAP_COLOR_RED   ""
    162 #define TAP_COLOR_GREEN ""
    163 #define TAP_COLOR_RESET ""
    164 #else
    165 #define TAP_COLOR_CODE  "\x1B"
    166 #define TAP_COLOR_RED   "[1;31m"
    167 #define TAP_COLOR_GREEN "[1;32m"
    168 #define TAP_COLOR_RESET "[0m"
    169 #endif
    170 
    171 int tap_asserts = 0;
    172 int tap_passes = 0;
    173 int tap_fails = 0;
    174 const char *tap_current_name = NULL;
    175 
    176 void tap_execute(const char* name, void (*test_function)()) {
    177   tap_current_name = name;
    178   printf("# %s\n", name);
    179   test_function();
    180 }
    181 
    182 int tap_assert(const char* file, int line, const char* msg, const char* expression, int pass) {
    183   tap_asserts++;
    184 
    185   if (pass) {
    186     tap_passes++;
    187 #ifndef ASSERT_SILENT
    188     printf("%s%sok%s%s %d - %s\n",
    189       TAP_COLOR_CODE, TAP_COLOR_GREEN,
    190       TAP_COLOR_CODE, TAP_COLOR_RESET,
    191       tap_asserts,
    192       msg
    193     );
    194 #endif
    195   } else {
    196     tap_fails++;
    197 #ifndef ASSERT_SILENT
    198     printf(
    199       "%s%snot ok%s%s %d - %s\n"
    200       "  On %s:%d, in test %s()\n"
    201       "    %s\n"
    202       ,
    203       TAP_COLOR_CODE, TAP_COLOR_RED,
    204       TAP_COLOR_CODE, TAP_COLOR_RESET,
    205       tap_asserts, msg,
    206       file, line, tap_current_name,
    207       expression
    208     );
    209   }
    210 #endif
    211   return pass;
    212 }
    213 
    214 int tap_report(void) {
    215 #ifndef REPORT_SILENT
    216   printf(
    217     "1..%d\n"
    218     "# tests %d\n"
    219     "# pass  %d\n"
    220     "# fail  %d\n",
    221     tap_asserts,
    222     tap_asserts,
    223     tap_passes,
    224     tap_fails
    225   );
    226 #endif
    227   return tap_fails ? 2 : 0;
    228 }
    229 
    230 #endif // __TINYTEST_INCLUDED_H__
    231 
    232 ///
    233 /// Credits
    234 /// -------
    235 ///
    236 /// This library was heavily based on the [tinytest][tinytest] library by
    237 /// [Joe Walnes][joewalnes]. A license reference to his library could not be
    238 /// found, which is why this reference is in this file. Should I be contacted
    239 /// about licensing issues, I'll investigate further.
    240 ///
    241 /// [joewalnes]: https://github.com/joewalnes
    242 /// [tinytest]: https://github.com/joewalnes/tinytest