#ifndef _TEST_H_ #define _TEST_H_ #include #include #include /************* * REGIONS * *************/ /* extra levels of abstraction are necessary * to get __COUNTER__ to evaluate before * being concatenated */ #define _TEST_PASTE(x, y) \ x ## y /* the whole thing is inside one * big function. yay! */ #define BEGIN_TEST \ typedef void (*_test_cleanup_t)(void); \ _test_cleanup_t _test_cleanup_current=NULL; \ int _test_count_current=1; \ int main(int argc, char *argv[]) \ { /* yaaaay! */ #define END_TEST \ printf("\e[1m :: \e[m\e[32msuccess!\e[m\n"); \ return 0; \ } /* print a fancy description and * reset the cleanup function * pointer to NULL so, if it isn't * defined again, no cleanup will * be called */ #define TEST(description) \ _test_cleanup_current=NULL; \ printf("\e[1m%3i :: \e[m\e[33m%s\e[m\n", _test_count_current++, description); /* pass in statements to be called on test * exit. goes after varible declarations and * before test body. */ #define CLEANUP(statements) \ _TEST_CLEANUP_UNIQ(statements, __COUNTER__) /* insert a unique cleanup function */ #define _TEST_CLEANUP_UNIQ(statements, unique_count) \ void _TEST_PASTE(_test_cleanup_, unique_count)(void) \ { \ statements \ } \ _test_cleanup_current=_TEST_PASTE(_test_cleanup_, unique_count); /* pretty printing for the current state within * a test */ #define STATE(...) \ do { \ printf("\e[1m :: \e[m\e[34m"); \ printf(__VA_ARGS__); \ printf("...\e[m\n"); \ } while(0) /* one of these goes at the end of every test * with CLEANUP (unless you want memory leaks). * if you want, stick it in those without CLEANUP * too. */ #define RETURN() _TEST_RETURN(false) /* checks if there is a cleanup function to * call and then either continues or terminates * the test */ #define _TEST_RETURN(fail) \ do { \ if(_test_cleanup_current != NULL) \ (*_test_cleanup_current)(); \ if(fail) \ exit(1); \ } while(0) /*********** * TESTS * ***********/ #define EXPECT_NULL(summary, arg) \ do { \ if((arg) != NULL) \ _TEST_FAIL_VAL(summary, "%s", "%p", "NULL", (void*)(arg)); \ } while(0); #define EXPECT_NON_NULL(summary, arg) \ do { \ if((arg) == NULL) \ _TEST_FAIL_VAL(summary, "%s", "%p", "non NULL", (void*)(arg)); \ } while(0); #define EXPECT_ZERO(summary, arg) \ do { \ if(arg) \ _TEST_FAIL_VAL(summary, "%i", "%i", 0, arg); \ } while(0); #define EXPECT_ONE(summary, arg) \ do { \ if((arg) != 1) \ _TEST_FAIL_VAL(summary, "%i", "%i", 1, arg); \ } while(0); #define EXPECT_GREATER_THAN_ZERO(summary, arg) \ do { \ if((arg) <= 0) \ _TEST_FAIL_VAL(summary, "%s", "%i", ">0", arg); \ } while(0); #define EXPECT_INT(summary, arg1, arg2) \ do { \ if((arg1) != (arg2)) \ _TEST_FAIL_VAL(summary, "%i", "%i", arg1, arg2); \ } while(0); #define EXPECT_EQUAL_INT(summary, arg1, arg2) \ do { \ if((arg1) != (arg2)) \ _TEST_FAIL_EQUAL(summary, "%i", "%i", arg1, arg2); \ } while(0); #define EXPECT_UNEQUAL_INT(summary, arg1, arg2) \ do { \ if((arg1) == (arg2)) \ _TEST_FAIL_EQUAL(summary, "%i", "%i", arg1, arg2); \ } while(0); #define EXPECT_STR(summary, arg1, arg2) \ do { \ if( strcmp(arg1, arg2) ) \ _TEST_FAIL_VAL(summary, "‘%s’", "‘%s’", arg1, arg2); \ } while(0); #define EXPECT_EQUAL_STR(summary, arg1, arg2) \ do { \ if( strcmp(arg1, arg2) ) \ _TEST_FAIL_EQUAL(summary, "‘%s’", "‘%s’", arg1, arg2); \ } while(0); #define EXPECT_UNEQUAL_STR(summary, arg1, arg2) \ do { \ if( !strcmp(arg1, arg2) ) \ _TEST_FAIL_EQUAL(summary, "‘%s’", "‘%s’", arg1, arg2); \ } while(0); #define _TEST_FAIL_VAL(summary, format1, format2, expected, actual) \ do { \ printf("\e[1m :: \e[m\e[31mFAIL: " summary "\e[m\n"); \ printf("\e[1m :: \e[m\e[1;32m expected:\e[m " format1 "\n", expected);\ printf("\e[1m :: \e[m\e[1;31m actual:\e[m " format2 "\n", actual); \ _TEST_RETURN(true); \ } while(0) #define _TEST_FAIL_EQUAL(summary, format1, format2, arg1, arg2) \ do { \ printf("\e[1m :: \e[m\e[31mFAIL: " summary "\e[m\n"); \ printf("\e[1m :: \e[m arg1 == \e[1;31m" format1 "\e[m\n", arg1); \ printf("\e[1m :: \e[m arg2 == \e[1;31m" format2 "\e[m\n", arg2); \ _TEST_RETURN(true); \ } while(0) #endif