test.h (5359B)
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 /// API 59 /// --- 60 /// 61 62 /// 63 /// ### Macros 64 /// 65 66 67 /// <details> 68 /// <summary>ASSERT(msg, expression)</summary> 69 /// 70 /// Perform an assertion 71 ///<C 72 #define ASSERT(msg, expression) if (!tap_assert(__FILE__, __LINE__, (msg), (#expression), (expression) ? 1 : 0)) return 73 ///> 74 /// </details> 75 76 77 /// <details> 78 /// <summary>ASSERT_EQUALS(expected, actual)</summary> 79 /// 80 /// Perform an equal assertion 81 ///<C 82 /* Convenient assertion methods */ 83 /* TODO: Generate readable error messages for assert_equals or assert_str_equals */ 84 #define ASSERT_EQUALS(expected, actual) ASSERT((#actual), (expected) == (actual)) 85 ///> 86 /// </details> 87 88 /// <details> 89 /// <summary>ASSERT_STRING_EQUALS(expected, actual)</summary> 90 /// 91 /// Perform an equal string assertion 92 ///<C 93 #define ASSERT_STRING_EQUALS(expected, actual) ASSERT((#actual), strcmp((expected),(actual)) == 0) 94 ///> 95 /// </details> 96 97 /// <details> 98 /// <summary>RUN(fn)</summary> 99 /// 100 /// Run a test suite/function containing assertions 101 ///<C 102 #define RUN(test_function) tap_execute((#test_function), (test_function)) 103 ///> 104 /// </details> 105 106 /// <details> 107 /// <summary>TEST_REPORT()</summary> 108 /// 109 /// Report on the tests that have been run 110 ///<C 111 #define TEST_REPORT() tap_report() 112 ///> 113 /// </details> 114 115 /// 116 /// Extras 117 /// ------ 118 /// 119 /// ### Disable color 120 /// 121 /// If you want to disable color during the assertions, because you want to 122 /// interpret the output for example (it is "tap" format after all), you can 123 /// define `NO_COLOR` during compilation to disable color output. 124 /// 125 /// ```sh 126 /// cc -D NO_COLOR source.c -o test 127 /// ``` 128 /// 129 /// ### Silent assertions 130 /// 131 /// You can also fully disable output for assertions by defining the 132 /// `ASSERT_SILENT` macro. This will fully disable the printf performed after 133 /// the assertion is performed. 134 /// 135 /// ```sh 136 /// cc -D ASSERT_SILENT source.c -o test 137 /// ``` 138 /// 139 /// ### Silent reporting 140 /// 141 /// If you do not want the report to be displayed at the end, you can define the 142 /// `REPORT_SILENT` macro. This will disable the printf during reporting and 143 /// only keep the return code. 144 /// 145 /// ```sh 146 /// cc -D REPORT_SILENT source.c -o test 147 /// ``` 148 /// 149 150 #ifdef NO_COLOR 151 #define TAP_COLOR_CODE "" 152 #define TAP_COLOR_RED "" 153 #define TAP_COLOR_GREEN "" 154 #define TAP_COLOR_RESET "" 155 #else 156 #define TAP_COLOR_CODE "\x1B" 157 #define TAP_COLOR_RED "[1;31m" 158 #define TAP_COLOR_GREEN "[1;32m" 159 #define TAP_COLOR_RESET "[0m" 160 #endif 161 162 int tap_asserts = 0; 163 int tap_passes = 0; 164 int tap_fails = 0; 165 const char *tap_current_name = NULL; 166 167 void tap_execute(const char* name, void (*test_function)()) { 168 tap_current_name = name; 169 printf("# %s\n", name); 170 test_function(); 171 } 172 173 int tap_assert(const char* file, int line, const char* msg, const char* expression, int pass) { 174 tap_asserts++; 175 176 if (pass) { 177 tap_passes++; 178 #ifndef ASSERT_SILENT 179 printf("%s%sok%s%s %d - %s\n", 180 TAP_COLOR_CODE, TAP_COLOR_GREEN, 181 TAP_COLOR_CODE, TAP_COLOR_RESET, 182 tap_asserts, 183 msg 184 ); 185 #endif 186 } else { 187 tap_fails++; 188 #ifndef ASSERT_SILENT 189 printf( 190 "%s%snot ok%s%s %d - %s\n" 191 " On %s:%d, in test %s()\n" 192 " %s\n" 193 , 194 TAP_COLOR_CODE, TAP_COLOR_RED, 195 TAP_COLOR_CODE, TAP_COLOR_RESET, 196 tap_asserts, msg, 197 file, line, tap_current_name, 198 expression 199 ); 200 } 201 #endif 202 return pass; 203 } 204 205 int tap_report(void) { 206 #ifndef REPORT_SILENT 207 printf( 208 "1..%d\n" 209 "# tests %d\n" 210 "# pass %d\n" 211 "# fail %d\n", 212 tap_asserts, 213 tap_asserts, 214 tap_passes, 215 tap_fails 216 ); 217 #endif 218 return tap_fails ? 2 : 0; 219 } 220 221 #endif // __TINYTEST_INCLUDED_H__ 222 223 /// 224 /// Credits 225 /// ------- 226 /// 227 /// This library was heavily based on the [tinytest][tinytest] library by 228 /// [Joe Walnes][joewalnes]. A license reference to his library could not be 229 /// found, which is why this reference is in this file. Should I be contacted 230 /// about licensing issues, I'll investigate further. 231 /// 232 /// [joewalnes]: https://github.com/joewalnes 233 /// [tinytest]: https://github.com/joewalnes/tinytest