315 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			315 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "ptest.h"
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <signal.h>
 | |
| #include <time.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| /* Globals */
 | |
| 
 | |
| enum {
 | |
|   MAX_NAME = 512
 | |
| };
 | |
| 
 | |
| enum {
 | |
|   MAX_ERROR = 2048
 | |
| };
 | |
| 
 | |
| enum {
 | |
|   MAX_TESTS = 2048
 | |
| };
 | |
| 
 | |
| static int test_passing = 0;
 | |
| static int suite_passing = 0;
 | |
| 
 | |
| /* Colors */
 | |
| 
 | |
| enum {
 | |
|   BLACK   = 0x0,
 | |
|   BLUE    = 0x1,
 | |
|   GREEN   = 0x2,
 | |
|   AQUA    = 0x3,
 | |
|   RED     = 0x4,
 | |
|   PURPLE  = 0x5,
 | |
|   YELLOW  = 0x6,
 | |
|   WHITE   = 0x7,
 | |
|   GRAY    = 0x8,
 | |
|   LIGHT_BLUE    = 0x9,
 | |
|   LIGHT_GREEN   = 0xA,
 | |
|   LIGHT_AQUA    = 0xB,
 | |
|   LIGHT_RED     = 0xC,
 | |
|   LIGHT_PURPLE  = 0xD,
 | |
|   LIGHT_YELLOW  = 0xE,
 | |
|   LIGHT_WHITE   = 0xF
 | |
| };
 | |
| 
 | |
| #ifdef _WIN32
 | |
| 
 | |
|   #include <windows.h>
 | |
| 
 | |
|   static void pt_color(int color) {
 | |
|     HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE);
 | |
|     SetConsoleTextAttribute(hCon, color);
 | |
|   }
 | |
| 
 | |
| #else
 | |
| 
 | |
| static const char* colors[] = {
 | |
|   "\x1B[0m",
 | |
|   "\x1B[34m",
 | |
|   "\x1B[32m",
 | |
|   "\x1B[36m",
 | |
|   "\x1B[31m",
 | |
|   "\x1B[35m",
 | |
|   "\x1B[33m",
 | |
|   "\x1B[37m",
 | |
|   "",
 | |
|   "\x1B[34m",
 | |
|   "\x1B[32m",
 | |
|   "\x1B[36m",
 | |
|   "\x1B[31m",
 | |
|   "\x1B[35m",
 | |
|   "\x1B[33m",
 | |
|   "\x1B[37m"
 | |
| };
 | |
| 
 | |
|   static void pt_color(int color) {
 | |
|     
 | |
|     printf("%s", colors[color]);
 | |
|   
 | |
|   }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| /* Asserts */
 | |
| 
 | |
| static int num_asserts = 0;
 | |
| static int num_assert_passes = 0;
 | |
| static int num_assert_fails  = 0;
 | |
| 
 | |
| static char assert_err[MAX_ERROR];
 | |
| static char assert_err_buff[MAX_ERROR];
 | |
| static int assert_err_num = 0;
 | |
| 
 | |
| void pt_assert_run(int result, const char* expr, const char* func, const char* file, int line) {
 | |
|   
 | |
|   num_asserts++;
 | |
|   test_passing = test_passing && result;
 | |
|   
 | |
|   if (result) {
 | |
|     num_assert_passes++;
 | |
|   } else {
 | |
|     sprintf(assert_err_buff, "        %i. Assert [ %s ] (%s:%i)\n", assert_err_num+1, expr, file, line );
 | |
|     strcat(assert_err, assert_err_buff);
 | |
|     assert_err_num++;
 | |
|     num_assert_fails++;
 | |
|   }
 | |
|   
 | |
| }
 | |
| 
 | |
| static void ptest_signal(int sig) {
 | |
|   
 | |
|   test_passing = 0;
 | |
|   
 | |
|   switch( sig ) {
 | |
|     case SIGFPE:  sprintf(assert_err_buff, "        %i. Division by Zero\n", assert_err_num+1); break;
 | |
|     case SIGILL:  sprintf(assert_err_buff, "        %i. Illegal Instruction\n", assert_err_num+1); break;
 | |
|     case SIGSEGV: sprintf(assert_err_buff, "        %i. Segmentation Fault\n", assert_err_num+1); break;
 | |
|   }
 | |
|   
 | |
|   assert_err_num++;
 | |
|   strcat(assert_err, assert_err_buff);
 | |
|   
 | |
|   pt_color(WHITE); pt_color(RED); printf("Failed! \n\n%s\n", assert_err); pt_color(WHITE);
 | |
|   
 | |
|   printf("    | Stopping Execution.\n");
 | |
|   fflush(stdout);
 | |
|   exit(0);
 | |
|   
 | |
| }
 | |
| 
 | |
| /* Tests */
 | |
| 
 | |
| static void pt_title_case(char* output, const char* input) {
 | |
|   
 | |
|   int space = 1;
 | |
|   unsigned int i;
 | |
|   
 | |
|   strcpy(output, input);
 | |
|   
 | |
|   for(i = 0; i < strlen(output); i++) {
 | |
|     
 | |
|     if (output[i] == '_' || output[i] == ' ') {
 | |
|       space = 1;
 | |
|       output[i] = ' ';
 | |
|       continue;
 | |
|     } 
 | |
|     
 | |
|     if (space && output[i] >= 'a' && output[i] <= 'z') {
 | |
|       space = 0;
 | |
|       output[i] = output[i] - 32;
 | |
|       continue;
 | |
|     }
 | |
|     
 | |
|     space = 0;
 | |
|     
 | |
|   }
 | |
|   
 | |
| }
 | |
| 
 | |
| typedef struct {
 | |
|   void (*func)(void);
 | |
|   char name[MAX_NAME];
 | |
|   char suite[MAX_NAME];
 | |
| } test_t;
 | |
| 
 | |
| static test_t tests[MAX_TESTS];
 | |
| 
 | |
| static int num_tests = 0;
 | |
| static int num_tests_passes = 0;
 | |
| static int num_tests_fails  = 0;
 | |
| 
 | |
| void pt_add_test(void (*func)(void), const char* name, const char* suite) {
 | |
|   
 | |
|   test_t test;
 | |
| 
 | |
|   if (num_tests == MAX_TESTS) {
 | |
|     printf("ERROR: Exceeded maximum test count of %i!\n", MAX_TESTS); abort();
 | |
|   }
 | |
|   
 | |
|   if (strlen(name) >= MAX_NAME) {
 | |
|     printf("ERROR: Test name '%s' too long (Maximum is %i characters)\n", name, MAX_NAME); abort();
 | |
|   }
 | |
|   
 | |
|   if (strlen(suite) >= MAX_NAME) {
 | |
|     printf("ERROR: Test suite '%s' too long (Maximum is %i characters)\n", suite, MAX_NAME); abort();
 | |
|   }
 | |
|   
 | |
|   test.func = func;
 | |
|   pt_title_case(test.name, name);
 | |
|   pt_title_case(test.suite, suite);
 | |
|   
 | |
|   tests[num_tests] = test;
 | |
|   num_tests++;
 | |
|   
 | |
| }
 | |
| 
 | |
| /* Suites */
 | |
| 
 | |
| static int num_suites = 0;
 | |
| static int num_suites_passes = 0;
 | |
| static int num_suites_fails  = 0;
 | |
| 
 | |
| void pt_add_suite(void (*func)(void)) {
 | |
|   num_suites++;
 | |
|   func();
 | |
| }
 | |
| 
 | |
| /* Running */
 | |
| 
 | |
| static clock_t start, end;
 | |
| static char current_suite[MAX_NAME];
 | |
| 
 | |
| int pt_run(void) {
 | |
|   
 | |
|   unsigned int i;
 | |
|   double total;
 | |
| 
 | |
|   printf("    \n");
 | |
|   printf("    +-------------------------------------------+\n");
 | |
|   printf("    | ptest          MicroTesting Magic for C   |\n");
 | |
|   printf("    |                                           |\n");
 | |
|   printf("    | http://github.com/orangeduck/ptest        |\n");
 | |
|   printf("    |                                           |\n");
 | |
|   printf("    | Daniel Holden (contact@theorangeduck.com) |\n");
 | |
|   printf("    +-------------------------------------------+\n");
 | |
|   
 | |
|   signal(SIGFPE,  ptest_signal);
 | |
|   signal(SIGILL,  ptest_signal);
 | |
|   signal(SIGSEGV, ptest_signal);
 | |
|   
 | |
|   start = clock();
 | |
|   strcpy(current_suite, "");
 | |
|   
 | |
|   for(i = 0; i < num_tests; i++) {
 | |
|     
 | |
|     test_t test = tests[i];
 | |
|     
 | |
|     /* Check for transition to a new suite */
 | |
|     if (strcmp(test.suite, current_suite)) {
 | |
|       
 | |
|       /* Don't increment any counter for first entrance */
 | |
|       if (strcmp(current_suite, "")) {
 | |
|         if (suite_passing) {
 | |
|           num_suites_passes++;
 | |
|         } else {
 | |
|           num_suites_fails++;
 | |
|         }
 | |
|       }
 | |
|     
 | |
|       suite_passing = 1;
 | |
|       strcpy(current_suite, test.suite);
 | |
|       printf("\n\n  ===== %s =====\n\n", current_suite);
 | |
|     }
 | |
|     
 | |
|     /* Run Test */
 | |
|     
 | |
|     test_passing = 1;
 | |
|     strcpy(assert_err, "");
 | |
|     strcpy(assert_err_buff, "");
 | |
|     assert_err_num = 0;
 | |
|     printf("    | %s ... ", test.name);
 | |
|     
 | |
|     test.func();
 | |
|     
 | |
|     suite_passing = suite_passing && test_passing;
 | |
|     
 | |
|     if (test_passing) {
 | |
|       num_tests_passes++;
 | |
|       pt_color(GREEN);  printf("Passed! \n"); pt_color(WHITE);
 | |
|     } else {
 | |
|       num_tests_fails++;
 | |
|       pt_color(RED);    printf("Failed! \n\n%s\n", assert_err); pt_color(WHITE);
 | |
|     }
 | |
|     
 | |
|   }
 | |
|   
 | |
|   if (suite_passing) {
 | |
|     num_suites_passes++;
 | |
|   } else {
 | |
|     num_suites_fails++;
 | |
|   }
 | |
|   
 | |
|   end = clock();
 | |
|   
 | |
|   printf("  \n");
 | |
|   printf("  +---------------------------------------------------+\n");
 | |
|   printf("  |                      Summary                      |\n");
 | |
|   printf("  +---------++------------+-------------+-------------+\n");
 | |
|   
 | |
|   printf("  | Suites  ||");
 | |
|   pt_color(YELLOW); printf(" Total %4d ",  num_suites);        pt_color(WHITE); printf("|");
 | |
|   pt_color(GREEN);  printf(" Passed %4d ", num_suites_passes); pt_color(WHITE); printf("|");
 | |
|   pt_color(RED);    printf(" Failed %4d ", num_suites_fails);  pt_color(WHITE); printf("|\n");
 | |
|   
 | |
|   printf("  | Tests   ||");
 | |
|   pt_color(YELLOW); printf(" Total %4d ",  num_tests);         pt_color(WHITE); printf("|");
 | |
|   pt_color(GREEN);  printf(" Passed %4d ", num_tests_passes);  pt_color(WHITE); printf("|");
 | |
|   pt_color(RED);    printf(" Failed %4d ", num_tests_fails);   pt_color(WHITE); printf("|\n");
 | |
|   
 | |
|   printf("  | Asserts ||");
 | |
|   pt_color(YELLOW); printf(" Total %4d ",  num_asserts);       pt_color(WHITE); printf("|");
 | |
|   pt_color(GREEN);  printf(" Passed %4d ", num_assert_passes); pt_color(WHITE); printf("|");
 | |
|   pt_color(RED);    printf(" Failed %4d ", num_assert_fails);  pt_color(WHITE); printf("|\n");
 | |
|   
 | |
|   printf("  +---------++------------+-------------+-------------+\n");
 | |
|   printf("  \n");
 | |
|   
 | |
|   total = (double)(end - start) / CLOCKS_PER_SEC;
 | |
|   
 | |
|   printf("      Total Running Time: %0.3fs\n\n", total);
 | |
|   
 | |
|   if (num_suites_fails > 0) { return 1; } else { return 0; }
 | |
| }
 | 
