ptest.c (7417B)
1 #include "ptest.h" 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <signal.h> 6 #include <time.h> 7 #include <unistd.h> 8 9 /* Globals */ 10 11 enum { 12 MAX_NAME = 512 13 }; 14 15 enum { 16 MAX_ERROR = 2048 17 }; 18 19 enum { 20 MAX_TESTS = 2048 21 }; 22 23 static int test_passing = 0; 24 static int suite_passing = 0; 25 26 /* Colors */ 27 28 enum { 29 BLACK = 0, 30 BLUE = 1, 31 GREEN = 2, 32 AQUA = 3, 33 RED = 4, 34 PURPLE = 5, 35 YELLOW = 6, 36 WHITE = 7, 37 GRAY = 8, 38 39 LIGHT_BLUE = 9, 40 LIGHT_GREEN = 10, 41 LIGHT_AQUA = 11, 42 LIGHT_RED = 12, 43 LIGHT_PURPLE = 13, 44 LIGHT_YELLOW = 14, 45 LIGHT_WHITE = 15, 46 47 DEFAULT = 16 48 }; 49 50 #ifdef _WIN32 51 52 #include <windows.h> 53 54 static WORD defaults; 55 static int defaults_loaded = 0; 56 57 static void pt_color(int color) { 58 59 HANDLE cnsl = GetStdHandle(STD_OUTPUT_HANDLE); 60 61 if (!defaults_loaded) { 62 CONSOLE_SCREEN_BUFFER_INFO info; 63 GetConsoleScreenBufferInfo(cnsl, &info); 64 defaults = info.wAttributes; 65 defaults_loaded = 1; 66 } 67 68 SetConsoleTextAttribute(cnsl, color == DEFAULT ? defaults : color); 69 } 70 71 #else 72 73 static const char* colors[] = { 74 "\x1B[0m", 75 "\x1B[34m", 76 "\x1B[32m", 77 "\x1B[36m", 78 "\x1B[31m", 79 "\x1B[35m", 80 "\x1B[33m", 81 "\x1B[37m", 82 "", 83 "\x1B[34m", 84 "\x1B[32m", 85 "\x1B[36m", 86 "\x1B[31m", 87 "\x1B[35m", 88 "\x1B[33m", 89 "\x1B[37m", 90 "\x1B[39m", 91 }; 92 93 static void pt_color(int color) { 94 printf("%s", colors[color]); 95 } 96 97 #endif 98 99 /* Asserts */ 100 101 static int num_asserts = 0; 102 static int num_assert_passes = 0; 103 static int num_assert_fails = 0; 104 105 static char assert_err[MAX_ERROR]; 106 static char assert_err_buff[MAX_ERROR]; 107 static int assert_err_num = 0; 108 109 void pt_assert_run(int result, const char* expr, const char* file, int line) { 110 111 num_asserts++; 112 test_passing = test_passing && result; 113 114 if (result) { 115 num_assert_passes++; 116 } else { 117 sprintf(assert_err_buff, 118 " %i. Assert [ %s ] (%s:%i)\n", 119 assert_err_num+1, expr, file, line ); 120 strcat(assert_err, assert_err_buff); 121 assert_err_num++; 122 num_assert_fails++; 123 } 124 125 } 126 127 static void ptest_signal(int sig) { 128 129 test_passing = 0; 130 131 switch( sig ) { 132 case SIGFPE: sprintf(assert_err_buff, 133 " %i. Division by Zero\n", assert_err_num+1); 134 break; 135 case SIGILL: sprintf(assert_err_buff, 136 " %i. Illegal Instruction\n", assert_err_num+1); 137 break; 138 case SIGSEGV: sprintf(assert_err_buff, 139 " %i. Segmentation Fault\n", assert_err_num+1); 140 break; 141 default: break; 142 } 143 144 assert_err_num++; 145 strcat(assert_err, assert_err_buff); 146 147 pt_color(RED); 148 printf("Failed! \n\n%s\n", assert_err); 149 pt_color(DEFAULT); 150 151 puts(" | Stopping Execution."); 152 fflush(stdout); 153 exit(0); 154 155 } 156 157 /* Tests */ 158 159 static void pt_title_case(char* output, const char* input) { 160 161 int space = 1; 162 unsigned int i; 163 164 strcpy(output, input); 165 166 for(i = 0; i < strlen(output); i++) { 167 168 if (output[i] == '_' || output[i] == ' ') { 169 space = 1; 170 output[i] = ' '; 171 continue; 172 } 173 174 if (space && output[i] >= 'a' && output[i] <= 'z') { 175 space = 0; 176 output[i] = output[i] - 32; 177 continue; 178 } 179 180 space = 0; 181 182 } 183 184 } 185 186 typedef struct { 187 void (*func)(void); 188 char name[MAX_NAME]; 189 char suite[MAX_NAME]; 190 } test_t; 191 192 static test_t tests[MAX_TESTS]; 193 194 static int num_tests = 0; 195 static int num_tests_passes = 0; 196 static int num_tests_fails = 0; 197 198 void pt_add_test(void (*func)(void), const char* name, const char* suite) { 199 200 test_t test; 201 202 if (num_tests == MAX_TESTS) { 203 printf("ERROR: Exceeded maximum test count of %i!\n", 204 MAX_TESTS); abort(); 205 } 206 207 if (strlen(name) >= MAX_NAME) { 208 printf("ERROR: Test name '%s' too long (Maximum is %i characters)\n", 209 name, MAX_NAME); abort(); 210 } 211 212 if (strlen(suite) >= MAX_NAME) { 213 printf("ERROR: Test suite '%s' too long (Maximum is %i characters)\n", 214 suite, MAX_NAME); abort(); 215 } 216 217 test.func = func; 218 pt_title_case(test.name, name); 219 pt_title_case(test.suite, suite); 220 221 tests[num_tests] = test; 222 num_tests++; 223 } 224 225 /* Suites */ 226 227 static int num_suites = 0; 228 static int num_suites_passes = 0; 229 static int num_suites_fails = 0; 230 231 void pt_add_suite(void (*func)(void)) { 232 num_suites++; 233 func(); 234 } 235 236 /* Running */ 237 238 static clock_t start, end; 239 static char current_suite[MAX_NAME]; 240 241 int pt_run(void) { 242 243 int i; 244 double total; 245 test_t test; 246 247 puts(""); 248 puts(" +-------------------------------------------+"); 249 puts(" | ptest MicroTesting Magic for C |"); 250 puts(" | |"); 251 puts(" | http://github.com/orangeduck/ptest |"); 252 puts(" | |"); 253 puts(" | Daniel Holden (contact@theorangeduck.com) |"); 254 puts(" +-------------------------------------------+"); 255 256 signal(SIGFPE, ptest_signal); 257 signal(SIGILL, ptest_signal); 258 signal(SIGSEGV, ptest_signal); 259 260 start = clock(); 261 strcpy(current_suite, ""); 262 263 for(i = 0; i < num_tests; i++) { 264 265 test = tests[i]; 266 267 /* Check for transition to a new suite */ 268 if (strcmp(test.suite, current_suite)) { 269 270 /* Don't increment any counter for first entrance */ 271 if (strcmp(current_suite, "")) { 272 if (suite_passing) { 273 num_suites_passes++; 274 } else { 275 num_suites_fails++; 276 } 277 } 278 279 suite_passing = 1; 280 strcpy(current_suite, test.suite); 281 printf("\n\n ===== %s =====\n\n", current_suite); 282 } 283 284 /* Run Test */ 285 286 test_passing = 1; 287 strcpy(assert_err, ""); 288 strcpy(assert_err_buff, ""); 289 assert_err_num = 0; 290 printf(" | %s ... ", test.name); 291 fflush(stdout); 292 293 test.func(); 294 295 suite_passing = suite_passing && test_passing; 296 297 if (test_passing) { 298 num_tests_passes++; 299 pt_color(GREEN); 300 puts("Passed!"); 301 pt_color(DEFAULT); 302 } else { 303 num_tests_fails++; 304 pt_color(RED); 305 printf("Failed! \n\n%s\n", assert_err); 306 pt_color(DEFAULT); 307 } 308 309 } 310 311 if (suite_passing) { 312 num_suites_passes++; 313 } else { 314 num_suites_fails++; 315 } 316 317 end = clock(); 318 319 puts(""); 320 puts(" +---------------------------------------------------+"); 321 puts(" | Summary |"); 322 puts(" +---------++------------+-------------+-------------+"); 323 324 printf(" | Suites ||"); 325 pt_color(YELLOW); printf(" Total %4d ", num_suites); 326 pt_color(DEFAULT); putchar('|'); 327 pt_color(GREEN); printf(" Passed %4d ", num_suites_passes); 328 pt_color(DEFAULT); putchar('|'); 329 pt_color(RED); printf(" Failed %4d ", num_suites_fails); 330 pt_color(DEFAULT); puts("|"); 331 332 printf(" | Tests ||"); 333 pt_color(YELLOW); printf(" Total %4d ", num_tests); 334 pt_color(DEFAULT); putchar('|'); 335 pt_color(GREEN); printf(" Passed %4d ", num_tests_passes); 336 pt_color(DEFAULT); putchar('|'); 337 pt_color(RED); printf(" Failed %4d ", num_tests_fails); 338 pt_color(DEFAULT); puts("|"); 339 340 printf(" | Asserts ||"); 341 pt_color(YELLOW); printf(" Total %4d ", num_asserts); 342 pt_color(DEFAULT); putchar('|'); 343 pt_color(GREEN); printf(" Passed %4d ", num_assert_passes); 344 pt_color(DEFAULT); putchar('|'); 345 pt_color(RED); printf(" Failed %4d ", num_assert_fails); 346 pt_color(DEFAULT); puts("|"); 347 348 puts(" +---------++------------+-------------+-------------+"); 349 puts(""); 350 351 total = (double)(end - start) / CLOCKS_PER_SEC; 352 353 printf(" Total Running Time: %0.3fs\n\n", total); 354 355 if (num_suites_fails > 0) { return 1; } else { return 0; } 356 }