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