mumble

A Lisp written in C, following the *Build Your Own Lisp* book
Log | Files | Refs | README

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 }