| /* | |
| ==================================================================== | |
| Copyright (c) 2008 Ian Blumel. All rights reserved. | |
| FCT (Fast C Test) Unit Testing Framework | |
| Copyright (c) 2008, Ian Blumel (ian.blumel@gmail.com) | |
| All rights reserved. | |
| This license is based on the BSD License. | |
| Redistribution and use in source and binary forms, with or without | |
| modification, are permitted provided that the following conditions are | |
| met: | |
| * Redistributions of source code must retain the above copyright | |
| notice, this list of conditions and the following disclaimer. | |
| * Redistributions in binary form must reproduce the above copyright | |
| notice, this list of conditions and the following disclaimer in | |
| the documentation and/or other materials provided with the | |
| distribution. | |
| * Neither the name of, Ian Blumel, nor the names of its | |
| contributors may be used to endorse or promote products derived | |
| from this software without specific prior written permission. | |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS | |
| IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |
| TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | |
| PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER | |
| OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
| LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
| NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| ==================================================================== | |
| File: fct.h | |
| */ | |
| #if !defined(FCT_INCLUDED__IMB) | |
| #define FCT_INCLUDED__IMB | |
| #define FCT_VERSION_STR "1.0.2" | |
| #define FCT_VERSION_MAJOR 1 | |
| #define FCT_VERSION_MINOR 0 | |
| #define FCT_VERSION_MICRO 2 | |
| /* Define this to remove unneeded WIN32 warnings. We will undefine this at | |
| the end of the file so as not to interfere with your build. */ | |
| #if defined(WIN32) && !defined(_CRT_SECURE_NO_WARNINGS) | |
| # define _CRT_SECURE_NO_WARNINGS | |
| #endif | |
| #include <string.h> | |
| #include <assert.h> | |
| #include <stdarg.h> | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| #include <time.h> | |
| #include <float.h> | |
| #include <math.h> | |
| #define FCT_MAX_NAME 256 | |
| #define FCT_MAX_LOG_LINE 256 | |
| #define nbool_t int | |
| #define FCT_TRUE 1 | |
| #define FCT_FALSE 0 | |
| /* Forward declarations. The following forward declarations are required | |
| because there is a inter-relationship between certain objects that | |
| just can not be untwined. */ | |
| typedef struct _fct_logger_i fct_logger_i; | |
| typedef struct _fct_standard_logger_t fct_standard_logger_t; | |
| typedef struct _fct_minimal_logger_t fct_minimal_logger_t; | |
| typedef struct _fctchk_t fctchk_t; | |
| typedef struct _fct_test_t fct_test_t; | |
| typedef struct _fct_ts_t fct_ts_t; | |
| typedef struct _fctkern_t fctkern_t; | |
| /* Forward declare some functions used throughout. */ | |
| static fct_standard_logger_t * | |
| fct_standard_logger__new(void); | |
| static void | |
| fct_logger__del(fct_logger_i *logger); | |
| static void | |
| fct_logger__on_cndtn(fct_logger_i *self, fctchk_t const *chk); | |
| static void | |
| fct_logger__on_test_start(fct_logger_i *logger, fct_test_t const *test); | |
| static void | |
| fct_logger__on_test_end(fct_logger_i *logger, fct_test_t const *test); | |
| static void | |
| fct_logger__on_test_suite_start(fct_logger_i *logger, fct_ts_t const *ts); | |
| static void | |
| fct_logger__on_test_suite_end(fct_logger_i *logger, fct_ts_t const *ts); | |
| static void | |
| fct_logger__on_fct_start(fct_logger_i *logger, fctkern_t const *kern); | |
| static void | |
| fct_logger__on_fct_end(fct_logger_i *logger, fctkern_t const *kern); | |
| /* Explicitly indicate a no-op */ | |
| #define fct_pass() | |
| #define fct_unused(x) ((void) (x)); | |
| /* This is just a little trick to let me put comments inside of macros. I | |
| really only want to bother with this when we are "unwinding" the macros | |
| for debugging purposes. */ | |
| #if defined(FCT_CONF_UNWIND) | |
| # define _fct_cmt(string) {char*_=string;} | |
| #else | |
| # define _fct_cmt(string) | |
| #endif | |
| /* | |
| -------------------------------------------------------- | |
| UTILITIES | |
| -------------------------------------------------------- | |
| */ | |
| /* Utility for truncated, safe string copies. */ | |
| static void | |
| fct_safe_str_cpy(char *dst, char const *src, size_t num) | |
| { | |
| assert( dst != NULL ); | |
| assert( src != NULL ); | |
| assert( num > 0 ); | |
| strncpy(dst, src, num); | |
| dst[num-1] = '\0'; | |
| } | |
| /* Isolate the snprintf implemenation. */ | |
| int | |
| fct_snprintf(char *buffer, size_t buffer_len, char *format, ...) | |
| { | |
| int count =0; | |
| va_list args; | |
| va_start(args, format); | |
| count =vsnprintf(buffer, buffer_len, format, args); | |
| va_end(args); | |
| return count; | |
| } | |
| /* A very, very simple "filter". This just compares the supplied prefix | |
| against the test_str, to see if they both have the same starting | |
| characters. If they do we return true, otherwise we return false. If the | |
| prefix is a blank string or NULL, then it will return FCT_TRUE.*/ | |
| static nbool_t | |
| fct_filter_pass(char const *prefix, char const *test_str) | |
| { | |
| nbool_t is_match = FCT_FALSE; | |
| char const *prefix_p; | |
| char const *test_str_p; | |
| /* If you got nothing to test against, why test? */ | |
| assert( test_str != NULL ); | |
| /* When the prefix is NULL or blank, we always return FCT_TRUE. */ | |
| if ( prefix == NULL || prefix[0] == '\0' ) | |
| { | |
| return FCT_TRUE; | |
| } | |
| /* Iterate through both character arrays at the same time. We are | |
| going to play a game and see if we can beat the house. */ | |
| for ( prefix_p = prefix, test_str_p = test_str; | |
| *prefix_p != '\0' && *test_str_p != '\0'; | |
| ++prefix_p, ++test_str_p ) | |
| { | |
| is_match = *prefix_p == *test_str_p; | |
| if ( !is_match ) | |
| { | |
| break; /* Quit the first time we don't match. */ | |
| } | |
| } | |
| /* If the iterator for the test_str is pointing at the null char, and | |
| the iterator for the prefix string is not, then the prefix string is | |
| larger than the actual test string, and therefore we failed to pass the | |
| filter. */ | |
| if ( *test_str_p == '\0' && *prefix_p != '\0' ) | |
| { | |
| return FCT_FALSE; | |
| } | |
| /* is_match will be set to the either FCT_TRUE if we kicked of the loop | |
| early because our filter ran out of characters or FCT_FALSE if we | |
| encountered a mismatch before our filter ran out of characters. */ | |
| return is_match; | |
| } | |
| /* Returns true if two reals are equal. */ | |
| nbool_t | |
| fct_real_eq(double v1, double v2) | |
| { | |
| return (nbool_t)(fabs(v1 - v2) < DBL_EPSILON); | |
| } | |
| /* | |
| -------------------------------------------------------- | |
| TIMER | |
| -------------------------------------------------------- | |
| */ | |
| typedef struct _fct_timer_t fct_timer_t; | |
| struct _fct_timer_t { | |
| clock_t start; | |
| clock_t stop; | |
| double duration; | |
| }; | |
| static void | |
| fct_timer__init(fct_timer_t *timer) { | |
| assert(timer != NULL); | |
| memset(timer, 0, sizeof(fct_timer_t)); | |
| } | |
| static void | |
| fct_timer__start(fct_timer_t *timer) { | |
| assert(timer != NULL); | |
| timer->start = clock(); | |
| } | |
| static void | |
| fct_timer__stop(fct_timer_t *timer) { | |
| assert(timer != NULL); | |
| timer->stop = clock(); | |
| timer->duration = (double) (timer->stop - timer->start) / CLOCKS_PER_SEC; | |
| } | |
| /* Returns the time in seconds. */ | |
| static double | |
| fct_timer__duration(fct_timer_t *timer) { | |
| assert( timer != NULL ); | |
| return timer->duration; | |
| } | |
| /* | |
| -------------------------------------------------------- | |
| GENERIC LIST | |
| -------------------------------------------------------- | |
| */ | |
| /* For now we will just keep it at a linear growth rate. */ | |
| #define FCT_LIST_GROWTH_FACTOR 2 | |
| /* Starting size for the list, to keep it simple we will start | |
| at a reasonable size. */ | |
| #define FCT_LIST_START_SIZE 2 | |
| /* Helper macros for quickly iterating through a list. You should be able | |
| to do something like, | |
| NLIST_FOREACH_BGN(fct_logger_i*, logger, my_list) | |
| { | |
| fct_logger__on_blah(logger); | |
| } | |
| NLIST_FOREACH_END(); | |
| */ | |
| #define NLIST_FOREACH_BGN(Type, Var, List)\ | |
| {\ | |
| if ( List != NULL ) {\ | |
| size_t item_i##Var;\ | |
| size_t num_items##Var = nlist__size(List);\ | |
| for( item_i##Var =0; item_i##Var != num_items##Var; ++item_i##Var )\ | |
| {\ | |
| Type Var = (Type) nlist__at((List), item_i##Var); | |
| #define NLIST_FOREACH_END() }}} | |
| /* Used to manage a list of loggers. This works mostly like | |
| the STL vector, where the array grows as more items are | |
| appended. */ | |
| typedef struct _nlist_t nlist_t; | |
| struct _nlist_t | |
| { | |
| /* Item's are stored as pointers to void. */ | |
| void **itm_list; | |
| /* Indicates the number of element's in the array. */ | |
| size_t avail_itm_num; | |
| /* Indicates the number of actually elements in the array. */ | |
| size_t used_itm_num; | |
| }; | |
| static nlist_t * | |
| nlist_new(void) | |
| { | |
| nlist_t *list = (nlist_t*)calloc(1, sizeof(nlist_t)); | |
| assert( list != NULL && "memory check"); | |
| list->itm_list = (void**)malloc(sizeof(void*)*FCT_LIST_START_SIZE); | |
| assert( list->itm_list != NULL && "memory check"); | |
| list->avail_itm_num =FCT_LIST_START_SIZE; | |
| list->used_itm_num =0; | |
| return list; | |
| } | |
| typedef void (*on_del_t)(void*); | |
| /* Cleans up list, and applies `on_del` to each item in the list. | |
| If on_del is NULL, it will not be applied. If `list` is NULL this | |
| function does nothing. */ | |
| static void | |
| nlist__del(nlist_t *list, on_del_t on_del) | |
| { | |
| size_t itm_i =0; | |
| if ( list == NULL ) { return; } | |
| /* Walk through the list applying the destroy function, if it was | |
| defined. */ | |
| if ( on_del != NULL ) | |
| { | |
| for ( itm_i =0; itm_i != list->used_itm_num; ++itm_i ) | |
| { | |
| on_del(list->itm_list[itm_i]); | |
| } | |
| } | |
| free(list->itm_list); | |
| free(list); | |
| } | |
| /* Returns the number of elements within the list. */ | |
| static size_t | |
| nlist__size(nlist_t const *list) | |
| { | |
| assert( list != NULL ); | |
| return list->used_itm_num; | |
| } | |
| /* Returns the item at idx, asserts otherwise. */ | |
| static void* | |
| nlist__at(nlist_t const *list, size_t idx) | |
| { | |
| assert( list != NULL ); | |
| assert( idx < list->used_itm_num ); | |
| return list->itm_list[idx]; | |
| } | |
| static void | |
| nlist__append(nlist_t *list, void *itm) | |
| { | |
| assert( list != NULL ); | |
| assert( list->itm_list != NULL ); | |
| assert( list->avail_itm_num != 0 ); | |
| /* If we ran out of room, then the last increment should be equal to the | |
| available space, in this case we need to grow a little more. */ | |
| if ( list->used_itm_num == list->avail_itm_num ) | |
| { | |
| list->avail_itm_num = list->avail_itm_num*FCT_LIST_GROWTH_FACTOR; | |
| list->itm_list = (void**)realloc( | |
| list->itm_list, sizeof(void*)*list->avail_itm_num | |
| ); | |
| assert( list->itm_list != NULL && "memory check"); | |
| } | |
| list->itm_list[list->used_itm_num] = itm; | |
| ++(list->used_itm_num); | |
| } | |
| /* | |
| ----------------------------------------------------------- | |
| A SINGLE CHECK | |
| ----------------------------------------------------------- | |
| This defines a single check. It indicates what the check was, | |
| and where it occurred. A "Test" object will have-a bunch | |
| of "checks". | |
| */ | |
| struct _fctchk_t { | |
| /* This string that represents the condition. */ | |
| char cndtn[FCT_MAX_LOG_LINE]; | |
| /* These indicate where the condition occurred. */ | |
| char file[FCT_MAX_LOG_LINE]; | |
| int lineno; | |
| nbool_t is_pass; | |
| }; | |
| #define fctchk__is_pass(_CHK_) ((_CHK_)->is_pass) | |
| #define fctchk__file(_CHK_) ((_CHK_)->file) | |
| #define fctchk__lineno(_CHK_) ((_CHK_)->lineno) | |
| #define fctchk__cndtn(_CHK_) ((_CHK_)->cndtn) | |
| static fctchk_t* | |
| fctchk_new(char const *cndtn, char const *file, int lineno, nbool_t is_pass) | |
| { | |
| fctchk_t *chk = NULL; | |
| assert( cndtn != NULL ); | |
| assert( file != NULL ); | |
| assert( lineno > 0 ); | |
| chk = (fctchk_t*)calloc(1, sizeof(fctchk_t)); | |
| assert( chk != NULL && "out of memory"); | |
| if ( chk == NULL ) { return NULL; } | |
| fct_safe_str_cpy(chk->cndtn, cndtn, FCT_MAX_LOG_LINE); | |
| fct_safe_str_cpy(chk->file, file, FCT_MAX_LOG_LINE); | |
| chk->lineno = lineno; | |
| chk->is_pass =is_pass; | |
| return chk; | |
| } | |
| /* Cleans up a "check" object. If the `chk` is NULL, this function does | |
| nothing. */ | |
| static void | |
| fctchk__del(fctchk_t *chk) | |
| { | |
| if ( chk == NULL ) { return; } | |
| free( chk ); | |
| } | |
| /* | |
| ----------------------------------------------------------- | |
| A TEST | |
| ----------------------------------------------------------- | |
| A suite will have-a list of tests. Where each test will have-a | |
| list of failed and passed checks. | |
| */ | |
| struct _fct_test_t { | |
| /* List of failed and passed "checks" (fctchk_t). Two seperate | |
| lists make it faster to determine how many checks passed and how | |
| many checks failed. */ | |
| nlist_t *failed_chks; | |
| nlist_t *passed_chks; | |
| /* The name of the test case. */ | |
| char name[FCT_MAX_NAME]; | |
| }; | |
| #define fct_test__name(_TEST_) ((_TEST_)->name) | |
| static fct_test_t* | |
| fct_test_new(char const *name) { | |
| fct_test_t *test =NULL; | |
| test = (fct_test_t*)malloc(sizeof(fct_test_t)); | |
| assert( test != NULL && "out of memory"); | |
| fct_safe_str_cpy(test->name, name, FCT_MAX_NAME); | |
| test->failed_chks = nlist_new(); | |
| test->passed_chks = nlist_new(); | |
| assert( test->failed_chks != NULL && "out of memory"); | |
| assert( test->passed_chks != NULL && "out of memory"); | |
| return test; | |
| } | |
| static nbool_t | |
| fct_test__is_pass(fct_test_t const *test) | |
| { | |
| assert( test != NULL ); | |
| return nlist__size(test->failed_chks) == 0; | |
| } | |
| static void | |
| fct_test__add(fct_test_t *test, fctchk_t *chk) | |
| { | |
| assert( test != NULL ); | |
| assert( chk != NULL ); | |
| if ( fctchk__is_pass(chk) ) | |
| { | |
| nlist__append(test->passed_chks, (void*)chk); | |
| } | |
| else | |
| { | |
| nlist__append(test->failed_chks, (void*)chk); | |
| } | |
| } | |
| /* Returns the number of checks made throughout the test. */ | |
| static int | |
| fct_test__chk_cnt(fct_test_t const *test) | |
| { | |
| assert( test != NULL ); | |
| return nlist__size(test->failed_chks) + nlist__size(test->passed_chks); | |
| } | |
| static void | |
| fct_test__del(fct_test_t *test) | |
| { | |
| if (test == NULL ) { return; } | |
| nlist__del(test->passed_chks, (on_del_t)fctchk__del); | |
| nlist__del(test->failed_chks, (on_del_t)fctchk__del); | |
| free(test); | |
| } | |
| /* | |
| ----------------------------------------------------------- | |
| TEST SUITE (TS) | |
| ----------------------------------------------------------- | |
| */ | |
| /* The different types of 'modes' that a test suite can be in. | |
| While the test suite is iterating through all the tests, its "State" | |
| can change from "setup mode", to "test mode" to "tear down" mode. | |
| These help to indicate what mode are currently in. Think of it as a | |
| basic FSM. | |
| if the count was 0 end | |
| +--------->---------------------> ending_mode-----+ | |
| | ^ | |
| ^ | | |
| start | [if no more tests] | |
| | | | | |
| +-count_mode -> setup_mode -> test_mode -> teardown_mode | |
| ^ | | |
| +-----------<---------------+ | |
| */ | |
| enum ts_mode { | |
| ts_mode_cnt, /* To setup when done counting. */ | |
| ts_mode_setup, /* To test when done setup. */ | |
| ts_mode_teardown, /* To ending mode, when no more tests. */ | |
| ts_mode_test, /* To tear down mode. */ | |
| ts_mode_ending, /* To ... */ | |
| ts_mode_end /* .. The End. */ | |
| }; | |
| /* Types of modes the test could be in. */ | |
| typedef enum { | |
| fct_test_status_SUCCESS, | |
| fct_test_status_FAILURE | |
| } fct_test_status; | |
| struct _fct_ts_t { | |
| /* For counting our 'current' test number, and the total number of | |
| tests. */ | |
| int curr_test_num; | |
| int total_test_num; | |
| /* Keeps track of the current state of the object while it is walking | |
| through its "FSM" */ | |
| enum ts_mode mode; | |
| /* The name of the test suite. */ | |
| char name[FCT_MAX_NAME]; | |
| /* List of tests that where executed within the test suite. */ | |
| nlist_t *test_list; | |
| }; | |
| #define fct_ts__is_setup_mode(ts) ((ts)->mode == ts_mode_setup) | |
| #define fct_ts__is_teardown_mode(ts) ((ts)->mode == ts_mode_teardown) | |
| #define fct_ts__is_test_mode(ts) ((ts)->mode == ts_mode_test) | |
| #define fct_ts__is_ending_mode(ts) ((ts)->mode == ts_mode_ending) | |
| #define fct_ts__is_end(ts) ((ts)->mode == ts_mode_end) | |
| #define fct_ts__is_cnt_mode(ts) ((ts)->mode == ts_mode_cnt) | |
| #define fct_ts__name(ts) ((ts)->name) | |
| static fct_ts_t * | |
| fct_ts_new(char const *name) { | |
| fct_ts_t *ts =NULL; | |
| ts = (fct_ts_t*)calloc(1, sizeof(fct_ts_t)); | |
| assert( ts != NULL ); | |
| fct_safe_str_cpy(ts->name, name, FCT_MAX_NAME); | |
| ts->mode = ts_mode_cnt; | |
| ts->test_list = nlist_new(); | |
| assert( ts->test_list != NULL && "no memory"); | |
| return ts; | |
| } | |
| static void | |
| fct_ts__del(fct_ts_t *ts) { | |
| if ( ts == NULL ) { return; } | |
| free(ts); | |
| } | |
| /* Flag a test suite as complete. It will no longer accept any more tests. */ | |
| #define fct_ts__end(_TS_) ((_TS_)->mode == ts_mode_end) | |
| static nbool_t | |
| fct_ts__is_more_tests(fct_ts_t const *ts) { | |
| assert( ts != NULL ); | |
| assert( !fct_ts__is_end(ts) ); | |
| return ts->curr_test_num < ts->total_test_num; | |
| } | |
| /* Indicates that we have started a test case. */ | |
| static void | |
| fct_ts__test_begin(fct_ts_t *ts) { | |
| assert( !fct_ts__is_end(ts) ); | |
| ++(ts->curr_test_num); | |
| } | |
| /* Takes OWNERSHIP of a test object, and warehouses it for later stat | |
| generation. */ | |
| static void | |
| fct_ts__add_test(fct_ts_t *ts, fct_test_t *test) { | |
| assert( ts != NULL && "invalid arg"); | |
| assert( test != NULL && "invalid arg"); | |
| assert( !fct_ts__is_end(ts) ); | |
| nlist__append(ts->test_list, test); | |
| } | |
| static void | |
| fct_ts__test_end(fct_ts_t *ts) { | |
| assert( ts != NULL ); | |
| assert( fct_ts__is_test_mode(ts) && "not in test mode, can't end!" ); | |
| /* After a test has completed, move to teardown mode. */ | |
| ts->mode = ts_mode_teardown; | |
| } | |
| /* Increments the internal count by 1. */ | |
| static void | |
| fct_ts__inc_total_test_num(fct_ts_t *ts) | |
| { | |
| assert( ts != NULL ); | |
| assert( fct_ts__is_cnt_mode(ts) ); | |
| assert( !fct_ts__is_end(ts) ); | |
| ++(ts->total_test_num); | |
| } | |
| /* Flags the end of the setup, which implies we are going to move into | |
| setup mode. You must be already in setup mode for this to work! */ | |
| static void | |
| fct_ts__setup_end(fct_ts_t *ts) | |
| { | |
| assert( fct_ts__is_setup_mode(ts) ); | |
| assert( !fct_ts__is_end(ts) ); | |
| ts->mode = ts_mode_test; | |
| } | |
| /* This cndtn is set when we have iterated through all the tests, and | |
| there was nothing more to do. */ | |
| static void | |
| fct_ts__ending(fct_ts_t *ts) | |
| { | |
| // We can only go from 'test-mode' to 'end-down' mode. | |
| assert( fct_ts__is_test_mode(ts) ); | |
| assert( !fct_ts__is_end(ts) ); | |
| ts->mode = ts_mode_ending; | |
| } | |
| /* Flags the end of the teardown, which implies we are going to move | |
| into setup mode (for the next 'iteration'). */ | |
| static void | |
| fct_ts__teardown_end(fct_ts_t *ts) | |
| { | |
| assert( fct_ts__is_teardown_mode(ts) ); | |
| assert( !fct_ts__is_end(ts) ); | |
| /* We have to decide if we should keep on testing by moving into tear down | |
| mode or if we have reached the real end and should be moving into the | |
| ending mode. */ | |
| if ( fct_ts__is_more_tests(ts) ) { | |
| ts->mode = ts_mode_setup; | |
| } | |
| else { | |
| ts->mode = ts_mode_ending; | |
| } | |
| } | |
| /* Flags the end of the counting, and proceeding to the first setup. | |
| Consider the special case when a test suite has NO tests in it, in | |
| that case we will have a current count that is zero, in which case | |
| we can skip right to 'ending'. */ | |
| static void | |
| fct_ts__cnt_end(fct_ts_t *ts) | |
| { | |
| assert( ts != NULL ); | |
| assert( fct_ts__is_cnt_mode(ts) ); | |
| assert( !fct_ts__is_end(ts) ); | |
| if (ts->total_test_num == 0 ) { | |
| ts->mode = ts_mode_ending; | |
| } | |
| else { | |
| ts->mode = ts_mode_setup; | |
| } | |
| } | |
| static nbool_t | |
| fct_ts__is_test_cnt(fct_ts_t const *ts, int test_num) | |
| { | |
| assert( ts != NULL ); | |
| assert( 0 <= test_num ); | |
| assert( test_num < ts->total_test_num ); | |
| assert( !fct_ts__is_end(ts) ); | |
| /* As we roll through the tests we increment the count. With this | |
| count we can decide if we need to execute a test or not. */ | |
| return test_num == ts->curr_test_num; | |
| } | |
| /* Returns the # of tests on the FCT TS object. This is the actual | |
| # of tests executed. */ | |
| static int | |
| fct_ts__tst_cnt(fct_ts_t const *ts) | |
| { | |
| assert( ts != NULL ); | |
| assert( !fct_ts__is_end(ts) ); | |
| return nlist__size(ts->test_list); | |
| } | |
| /* Returns the # of tests in the TS object that passed. */ | |
| static int | |
| fct_ts__tst_cnt_passed(fct_ts_t const *ts) | |
| { | |
| int tally =0; | |
| assert( ts != NULL ); | |
| assert( !fct_ts__is_end(ts) ); | |
| NLIST_FOREACH_BGN(fct_test_t*, test, ts->test_list) | |
| { | |
| if ( fct_test__is_pass(test) ) | |
| { | |
| tally += 1; | |
| } | |
| } | |
| NLIST_FOREACH_END(); | |
| return tally; | |
| } | |
| /* Returns the # of checks made throughout a test suite. */ | |
| static int | |
| fct_ts__chk_cnt(fct_ts_t const *ts) | |
| { | |
| int tally =0; | |
| assert( ts != NULL ); | |
| NLIST_FOREACH_BGN(fct_test_t *, test, ts->test_list) | |
| { | |
| tally += fct_test__chk_cnt(test); | |
| } | |
| NLIST_FOREACH_END(); | |
| return tally; | |
| } | |
| /* | |
| -------------------------------------------------------- | |
| FCT KERNAL | |
| -------------------------------------------------------- | |
| The "fctkern" is a singleton that is defined throughout the | |
| system. | |
| */ | |
| struct _fctkern_t { | |
| /* This is an list of loggers that can be used in the fct system. | |
| You/ can attach _MAX_LOGGERS to any framework. */ | |
| nlist_t *logger_list; | |
| /* This is a list of prefix's that can be used to determine if a | |
| test is should be run or not. */ | |
| nlist_t *prefix_list; | |
| /* This is a list of test suites that where generated throughout the | |
| testing process. */ | |
| nlist_t *ts_list; | |
| }; | |
| /* Returns the number of filters defined for the fct kernal. */ | |
| #define fctkern__filter_cnt(_NK_) (nlist__size((_NK_)->prefix_list)) | |
| static void | |
| fctkern__add_logger(fctkern_t *fct, fct_logger_i *logger_owns) | |
| { | |
| assert(fct != NULL && "invalid arg"); | |
| assert(logger_owns != NULL && "invalid arg"); | |
| nlist__append(fct->logger_list, logger_owns); | |
| assert( fct->logger_list != NULL && "memory check"); | |
| } | |
| /* Appends a prefix filter that is used to determine if a test can | |
| be executed or not. If the test starts with the same characters as | |
| the prefix, then it should be "runnable". The prefix filter must be | |
| a non-NULL, non-Blank string. */ | |
| static void | |
| fctkern__add_prefix_filter(fctkern_t const *fct, char const *prefix_filter) | |
| { | |
| char *filter =NULL; | |
| int filter_len =0; | |
| assert( fct != NULL && "invalid arg" ); | |
| assert( prefix_filter != NULL && "invalid arg" ); | |
| assert( strlen(prefix_filter) > 0 && "invalid arg" ); | |
| /* First we make a copy of the prefix, then we store it away | |
| in our little list. */ | |
| filter_len = strlen(prefix_filter); | |
| filter = (char*)malloc(sizeof(char)*(filter_len+1)); | |
| strncpy(filter, prefix_filter, filter_len); | |
| filter[filter_len] = '\0'; | |
| nlist__append(fct->prefix_list, (void*)filter); | |
| } | |
| /* Parses the command line and sets up the framework. The argc and argv | |
| should be directly from the program's main. */ | |
| static void | |
| fctkern_init(fctkern_t *nk, int argc, char *argv[]) | |
| { | |
| fct_logger_i *standard_logger = NULL; | |
| int arg_i =0; | |
| assert( nk != NULL ); | |
| memset(nk, 0, sizeof(fctkern_t)); | |
| nk->logger_list = nlist_new(); | |
| nk->prefix_list = nlist_new(); | |
| nk->ts_list = nlist_new(); | |
| /* Low-budget memory check for now. */ | |
| assert( nk->logger_list != NULL ); | |
| assert( nk->prefix_list != NULL ); | |
| assert( nk->ts_list != NULL ); | |
| standard_logger = (fct_logger_i*) fct_standard_logger__new(); | |
| assert( standard_logger != NULL && "no memory!"); | |
| fctkern__add_logger(nk, standard_logger); | |
| standard_logger = NULL; /* Owned by the nk list. */ | |
| /* Our basic parser. For now we just take each 'argv' and assume | |
| that it is a prefix filter. Notice we start at argument 1, since | |
| we don't care about the *name* of the program. */ | |
| for ( arg_i =1; arg_i < argc; ++arg_i ) | |
| { | |
| fctkern__add_prefix_filter(nk, argv[arg_i]); | |
| } | |
| } | |
| /* Takes OWNERSHIP of the test suite after we have finished executing | |
| its contents. This way we can build up all kinds of summaries at the end | |
| of a run. */ | |
| static void | |
| fctkern__add_ts(fctkern_t *nk, fct_ts_t *ts) { | |
| assert( nk != NULL ); | |
| assert( ts != NULL ); | |
| nlist__append(nk->ts_list, ts); | |
| } | |
| /* Returns FCT_TRUE if the supplied test_name passes the filters set on | |
| this test suite. If there are no filters, we return FCT_TRUE always. */ | |
| static nbool_t | |
| fctkern__pass_filter(fctkern_t *nk, char const *test_name) { | |
| int prefix_i =0; | |
| int prefix_list_size =0; | |
| assert( nk != NULL && "invalid arg"); | |
| assert( test_name != NULL ); | |
| assert( strlen(test_name) > 0 ); | |
| prefix_list_size = fctkern__filter_cnt(nk); | |
| /* If there is no filter list, then we return FCT_TRUE always. */ | |
| if ( prefix_list_size == 0 ) { | |
| return FCT_TRUE; | |
| } | |
| /* Iterate through the prefix filter list, and see if we have | |
| anything that does not pass. All we require is ONE item that | |
| passes the test in order for us to succeed here. */ | |
| for ( prefix_i = 0; prefix_i != prefix_list_size; ++prefix_i ) { | |
| char const *prefix = (char const*)nlist__at(nk->prefix_list, prefix_i); | |
| nbool_t pass = fct_filter_pass(prefix, test_name); | |
| if ( pass ) { | |
| return FCT_TRUE; | |
| } | |
| } | |
| /* Otherwise, we never managed to find a prefix that satisfied the | |
| supplied test name. Therefore we have failed to pass to the filter | |
| list test. */ | |
| return FCT_FALSE; | |
| } | |
| /* Returns the number of tests that were performed. */ | |
| static int | |
| fctkern__tst_cnt(fctkern_t const *nk) | |
| { | |
| int tally =0; | |
| assert( nk != NULL ); | |
| NLIST_FOREACH_BGN(fct_ts_t *, ts, nk->ts_list) | |
| { | |
| tally += fct_ts__tst_cnt(ts); | |
| } | |
| NLIST_FOREACH_END(); | |
| return tally; | |
| } | |
| /* Returns the number of tests that passed. */ | |
| static int | |
| fctkern__tst_cnt_passed(fctkern_t const *nk) | |
| { | |
| int tally =0; | |
| assert( nk != NULL ); | |
| NLIST_FOREACH_BGN(fct_ts_t*, ts, nk->ts_list) | |
| { | |
| tally += fct_ts__tst_cnt_passed(ts); | |
| } | |
| NLIST_FOREACH_END(); | |
| return tally; | |
| } | |
| /* Returns the number of tests that failed. */ | |
| static int | |
| fctkern__tst_cnt_failed(fctkern_t const *nk) | |
| { | |
| /* Keep it simple for now and just do a little math. */ | |
| int total =0; | |
| int passed =0; | |
| int failed =0; | |
| assert( nk != NULL ); | |
| total = fctkern__tst_cnt(nk); | |
| passed = fctkern__tst_cnt_passed(nk); | |
| failed = total - passed; | |
| return failed; | |
| } | |
| /* Returns the number of checks made throughout the entire test. */ | |
| static int | |
| fctkern__chk_cnt(fctkern_t const *nk) | |
| { | |
| int tally =0; | |
| assert( nk != NULL ); | |
| NLIST_FOREACH_BGN(fct_ts_t *, ts, nk->ts_list) | |
| { | |
| tally += fct_ts__chk_cnt(ts); | |
| } | |
| NLIST_FOREACH_END(); | |
| return tally; | |
| } | |
| /* Indicates the very end of all the tests. */ | |
| static void | |
| fctkern__end(fctkern_t *fct) | |
| { | |
| fct_unused(fct); | |
| } | |
| /* Cleans up the contents of a fctkern. NULL does nothing. */ | |
| static void | |
| fctkern__final(fctkern_t *fct) | |
| { | |
| if ( fct == NULL ) { return; } | |
| nlist__del(fct->logger_list, (on_del_t)fct_logger__del); | |
| /* The prefix list is a list of malloc'd strings. */ | |
| nlist__del(fct->prefix_list, (on_del_t)free); | |
| nlist__del(fct->ts_list, (on_del_t)fct_ts__del); | |
| } | |
| static void | |
| fctkern__log_suite_start(fctkern_t *kern, fct_ts_t const *ts) | |
| { | |
| assert( kern != NULL ); | |
| assert( ts != NULL ); | |
| NLIST_FOREACH_BGN(fct_logger_i*, logger, kern->logger_list) | |
| { | |
| fct_logger__on_test_suite_start(logger, ts); | |
| } | |
| NLIST_FOREACH_END(); | |
| } | |
| static void | |
| fctkern__log_suite_end(fctkern_t *kern, fct_ts_t const *ts) | |
| { | |
| assert( kern != NULL ); | |
| assert( ts != NULL ); | |
| NLIST_FOREACH_BGN(fct_logger_i*, logger, kern->logger_list) | |
| { | |
| fct_logger__on_test_suite_end(logger, ts); | |
| } | |
| NLIST_FOREACH_END(); | |
| } | |
| static void | |
| fctkern__log_chk(fctkern_t *kern, fctchk_t const *chk) | |
| { | |
| assert( kern != NULL ); | |
| assert( chk != NULL ); | |
| NLIST_FOREACH_BGN(fct_logger_i*, logger, kern->logger_list) | |
| { | |
| fct_logger__on_cndtn(logger, chk); | |
| } | |
| NLIST_FOREACH_END(); | |
| } | |
| /* Called whenever a test is started. */ | |
| static void | |
| fctkern__log_test_start(fctkern_t *kern, fct_test_t const *test) | |
| { | |
| assert( kern != NULL ); | |
| assert( test != NULL ); | |
| NLIST_FOREACH_BGN(fct_logger_i*, logger, kern->logger_list) | |
| { | |
| fct_logger__on_test_start(logger, test); | |
| } | |
| NLIST_FOREACH_END(); | |
| } | |
| static void | |
| fctkern__log_test_end(fctkern_t *kern, fct_test_t const *test) | |
| { | |
| assert( kern != NULL ); | |
| assert( test != NULL ); | |
| NLIST_FOREACH_BGN(fct_logger_i*, logger, kern->logger_list) | |
| { | |
| fct_logger__on_test_end(logger, test); | |
| } | |
| NLIST_FOREACH_END(); | |
| } | |
| static void | |
| fctkern__log_start(fctkern_t *kern) | |
| { | |
| assert( kern != NULL ); | |
| NLIST_FOREACH_BGN(fct_logger_i*, logger, kern->logger_list) | |
| { | |
| fct_logger__on_fct_start(logger, kern); | |
| } | |
| NLIST_FOREACH_END(); | |
| } | |
| static void | |
| fctkern__log_end(fctkern_t *kern) | |
| { | |
| assert( kern != NULL ); | |
| NLIST_FOREACH_BGN(fct_logger_i*, logger, kern->logger_list) | |
| { | |
| fct_logger__on_fct_end(logger, kern); | |
| } | |
| NLIST_FOREACH_END(); | |
| } | |
| /* | |
| ----------------------------------------------------------- | |
| LOGGER INTERFACE | |
| Defines an interface to a logging system. A logger | |
| must define the following functions in order to hook | |
| into the logging system. | |
| See the "Standard Logger" and "Minimal Logger" as examples | |
| of the implementation. | |
| ----------------------------------------------------------- | |
| */ | |
| typedef void (*fct_logger_on_cndtn_fn)(fct_logger_i *self, | |
| fctchk_t const *chk); | |
| #define _fct_logger_head \ | |
| fct_logger_on_cndtn_fn on_cndtn;\ | |
| void (*on_test_start)(fct_logger_i *logger, fct_test_t const *test);\ | |
| void (*on_test_end)(fct_logger_i *logger, fct_test_t const *test);\ | |
| void (*on_test_suite_start)(fct_logger_i *logger, fct_ts_t const *ts);\ | |
| void (*on_test_suite_end)(fct_logger_i *logger, fct_ts_t const *ts);\ | |
| void (*on_fct_start)(fct_logger_i *logger, fctkern_t const *kern);\ | |
| void (*on_fct_end)(fct_logger_i *logger, fctkern_t const *kern);\ | |
| void (*on_delete)(fct_logger_i *logger)\ | |
| struct _fct_logger_i { | |
| _fct_logger_head; | |
| }; | |
| /* Initializes the elements of a logger interface so they are at their | |
| standard values. */ | |
| static void | |
| fct_logger__init(fct_logger_i *logger) | |
| { | |
| assert( logger != NULL ); | |
| logger->on_cndtn =NULL; | |
| logger->on_test_start =NULL; | |
| logger->on_test_end =NULL; | |
| logger->on_test_suite_start =NULL; | |
| logger->on_test_suite_end =NULL; | |
| logger->on_fct_start =NULL; | |
| logger->on_fct_end =NULL; | |
| logger->on_delete =NULL; | |
| } | |
| static void | |
| fct_logger__del(fct_logger_i *logger) | |
| { | |
| if ( logger == NULL ) { return; } | |
| if ( logger->on_delete) { logger->on_delete(logger); } | |
| } | |
| static void | |
| fct_logger__on_test_start(fct_logger_i *logger, fct_test_t const *test) | |
| { | |
| assert( logger != NULL && "invalid arg"); | |
| assert( test != NULL && "invalid arg"); | |
| if ( logger->on_test_start != NULL ) | |
| { | |
| logger->on_test_start(logger, test); | |
| } | |
| } | |
| static void | |
| fct_logger__on_test_end(fct_logger_i *logger, fct_test_t const *test) | |
| { | |
| assert( logger != NULL && "invalid arg"); | |
| assert( test != NULL && "invalid arg"); | |
| if ( logger->on_test_end != NULL ) | |
| { | |
| logger->on_test_end(logger, test); | |
| } | |
| } | |
| static void | |
| fct_logger__on_test_suite_start(fct_logger_i *logger, fct_ts_t const *ts) | |
| { | |
| assert( logger != NULL && "invalid arg"); | |
| assert( ts != NULL && "invalid arg"); | |
| if ( logger->on_test_suite_start != NULL ) | |
| { | |
| logger->on_test_suite_start(logger, ts); | |
| } | |
| } | |
| static void | |
| fct_logger__on_test_suite_end(fct_logger_i *logger, fct_ts_t const *ts) | |
| { | |
| assert( logger != NULL && "invalid arg"); | |
| assert( ts != NULL && "invalid arg"); | |
| if ( logger->on_test_suite_end != NULL ) | |
| { | |
| logger->on_test_suite_end(logger, ts); | |
| } | |
| } | |
| static void | |
| fct_logger__on_cndtn(fct_logger_i *logger, fctchk_t const *chk) | |
| { | |
| assert( logger != NULL && "invalid arg"); | |
| assert( chk != NULL && "invalid arg"); | |
| if ( logger->on_cndtn ) | |
| { | |
| logger->on_cndtn(logger, chk); | |
| } | |
| } | |
| /* When we start all our tests. */ | |
| static void | |
| fct_logger__on_fct_start(fct_logger_i *logger, fctkern_t const *kern) | |
| { | |
| assert( logger != NULL ); | |
| assert( kern != NULL ); | |
| if ( logger->on_fct_start != NULL ) | |
| { | |
| logger->on_fct_start(logger, kern); | |
| } | |
| } | |
| /* When we have reached the end of ALL of our testing. */ | |
| static void | |
| fct_logger__on_fct_end(fct_logger_i *logger, fctkern_t const *kern) | |
| { | |
| assert( logger != NULL ); | |
| assert( kern != NULL ); | |
| if ( logger->on_fct_end ) | |
| { | |
| logger->on_fct_end(logger, kern); | |
| } | |
| } | |
| /* | |
| ----------------------------------------------------------- | |
| MINIMAL LOGGER | |
| ----------------------------------------------------------- | |
| */ | |
| /* Minimal logger, reports the minimum amount of information needed | |
| to determine "something is happening". */ | |
| struct _fct_minimal_logger_t { | |
| _fct_logger_head; | |
| }; | |
| static void | |
| fct_minimal_logger__on_cndtn(fct_logger_i *self, fctchk_t const *chk) | |
| { | |
| fct_unused(self); | |
| printf(fctchk__is_pass(chk) ? "." : "!"); | |
| } | |
| static void | |
| fct_minimal_logger__del(fct_logger_i *self) | |
| { | |
| free(self); | |
| } | |
| static fct_minimal_logger_t * | |
| fct_minimal_logger__new(void) | |
| { | |
| fct_minimal_logger_t *self = (fct_minimal_logger_t*)\ | |
| calloc(1,sizeof(fct_minimal_logger_t)); | |
| if ( self == NULL ) { return NULL; } | |
| fct_logger__init((fct_logger_i*)self); | |
| self->on_cndtn = fct_minimal_logger__on_cndtn; | |
| self->on_delete = fct_minimal_logger__del; | |
| return self; | |
| } | |
| /* | |
| ----------------------------------------------------------- | |
| STANDARD LOGGER | |
| ----------------------------------------------------------- | |
| */ | |
| struct _fct_standard_logger_t { | |
| _fct_logger_head; | |
| /* Start time. For now we use the low-accuracy time_t version. */ | |
| fct_timer_t timer; | |
| /* A list of char*'s that needs to be cleaned up. */ | |
| nlist_t *failed_cndtns_list; | |
| }; | |
| /* When a failure occurrs, we will record the details so we can display | |
| them when the log "finishes" up. */ | |
| static void | |
| fct_standard_logger__on_cndtn(fct_logger_i *logger_, fctchk_t const *chk) | |
| { | |
| fct_standard_logger_t *logger = (fct_standard_logger_t*)logger_; | |
| assert( logger != NULL ); | |
| assert( chk != NULL ); | |
| /* Only record failures. */ | |
| if ( !fctchk__is_pass(chk) ) | |
| { | |
| /* For now we will truncate the string to some set amount, later | |
| we can work out a dynamic string object. */ | |
| char *str = (char*)malloc(sizeof(char)*FCT_MAX_LOG_LINE); | |
| assert( str != NULL ); | |
| fct_snprintf( | |
| str, | |
| FCT_MAX_LOG_LINE, | |
| "%s(%d): %s", | |
| fctchk__file(chk), | |
| fctchk__lineno(chk), | |
| fctchk__cndtn(chk) | |
| ); | |
| /* Append it to the listing ... */ | |
| nlist__append(logger->failed_cndtns_list, (void*)str); | |
| } | |
| } | |
| static void | |
| fct_standard_logger__on_test_start(fct_logger_i *logger_, | |
| fct_test_t const *test) | |
| { | |
| fct_unused(logger_); | |
| printf("%s ... ", fct_test__name(test)); | |
| } | |
| static void | |
| fct_standard_logger__on_test_end(fct_logger_i *logger_, | |
| fct_test_t const *test) | |
| { | |
| nbool_t is_pass; | |
| fct_unused(logger_); | |
| is_pass = fct_test__is_pass(test); | |
| printf("%s\n", (is_pass) ? "PASS" : "FAIL" ); | |
| } | |
| static void | |
| fct_standard_logger__on_test_suite_start(fct_logger_i *logger_, | |
| fct_ts_t const *ts) | |
| { | |
| fct_unused(logger_); | |
| fct_unused(ts); | |
| } | |
| static void | |
| fct_standard_logger__on_test_suite_end(fct_logger_i *logger_, | |
| fct_ts_t const *ts) | |
| { | |
| fct_unused(logger_); | |
| fct_unused(ts); | |
| } | |
| static void | |
| fct_standard_logger__on_fct_start(fct_logger_i *logger_, | |
| fctkern_t const *nk) | |
| { | |
| fct_standard_logger_t *logger = (fct_standard_logger_t*)logger_; | |
| fct_unused(nk); | |
| fct_timer__start(&(logger->timer)); | |
| } | |
| static void | |
| fct_standard_logger__on_fct_end(fct_logger_i *logger_, fctkern_t const *nk) | |
| { | |
| fct_standard_logger_t *logger = (fct_standard_logger_t*)logger_; | |
| nbool_t is_success =1; | |
| double elasped_time =0; | |
| int num_tests =0; | |
| int num_passed =0; | |
| fct_timer__stop(&(logger->timer)); | |
| is_success = nlist__size(logger->failed_cndtns_list) ==0; | |
| if ( !is_success ) | |
| { | |
| printf("\n--------------------------------------------------------\n"); | |
| printf("FAILED TESTS\n\n"); | |
| NLIST_FOREACH_BGN(char *, cndtn_str, logger->failed_cndtns_list) | |
| { | |
| printf("%s\n", cndtn_str); | |
| } | |
| NLIST_FOREACH_END(); | |
| printf("\n"); | |
| } | |
| printf("\n--------------------------------------------------------\n"); | |
| num_tests = fctkern__tst_cnt(nk); | |
| num_passed = fctkern__tst_cnt_passed(nk); | |
| printf( | |
| "%s (%d/%d tests", | |
| (is_success) ? "PASSED" : "FAILED", | |
| num_passed, | |
| num_tests | |
| ); | |
| elasped_time = fct_timer__duration(&(logger->timer)); | |
| if ( elasped_time > 0.0000001 ) | |
| { | |
| printf(" in %.6fs)\n", elasped_time); | |
| } | |
| else | |
| { | |
| /* Don't bother displaying the time to execute. */ | |
| printf(")\n"); | |
| } | |
| } | |
| static void | |
| fct_standard_logger__del(fct_logger_i *logger_) | |
| { | |
| fct_standard_logger_t *logger = (fct_standard_logger_t*)logger_; | |
| NLIST_FOREACH_BGN(char *, cndtn_str, logger->failed_cndtns_list) | |
| { | |
| free(cndtn_str); | |
| } | |
| NLIST_FOREACH_END(); | |
| free(logger); | |
| logger_ =NULL; | |
| } | |
| fct_standard_logger_t * | |
| fct_standard_logger__new(void) | |
| { | |
| fct_standard_logger_t *logger = (fct_standard_logger_t *)calloc( | |
| 1, sizeof(fct_standard_logger_t) | |
| ); | |
| if ( logger == NULL ) | |
| { | |
| return NULL; | |
| } | |
| fct_logger__init((fct_logger_i*)logger); | |
| logger->on_cndtn = fct_standard_logger__on_cndtn; | |
| logger->on_test_start = fct_standard_logger__on_test_start; | |
| logger->on_test_end = fct_standard_logger__on_test_end; | |
| logger->on_test_suite_start = fct_standard_logger__on_test_suite_start; | |
| logger->on_test_suite_end = fct_standard_logger__on_test_suite_end; | |
| logger->on_fct_start = fct_standard_logger__on_fct_start; | |
| logger->on_fct_end = fct_standard_logger__on_fct_end; | |
| logger->on_delete = fct_standard_logger__del; | |
| logger->failed_cndtns_list = nlist_new(); | |
| assert( logger->failed_cndtns_list != NULL ); | |
| fct_timer__init(&(logger->timer)); | |
| return logger; | |
| } | |
| /* | |
| ------------------------------------------------------------ | |
| MAGIC MACROS | |
| ------------------------------------------------------------ | |
| */ | |
| #define FCT_BGN() \ | |
| int \ | |
| main(int argc, char *argv[])\ | |
| {\ | |
| fctkern_t fctkern__;\ | |
| fctkern_init(&fctkern__, argc, argv);\ | |
| fctkern__log_start(&fctkern__); | |
| #define FCT_END()\ | |
| {\ | |
| int num_failed__ =0;\ | |
| num_failed__ = fctkern__tst_cnt_failed((&fctkern__));\ | |
| fctkern__log_end(&fctkern__);\ | |
| fctkern__end(&fctkern__);\ | |
| fctkern__final(&fctkern__);\ | |
| return num_failed__;\ | |
| }\ | |
| } | |
| #define FCT_FIXTURE_SUITE_BGN(_NAME_) \ | |
| {\ | |
| fct_ts_t *ts__ = fct_ts_new( #_NAME_ );\ | |
| fctkern__log_suite_start((&fctkern__), ts__);\ | |
| for (;;)\ | |
| {\ | |
| int fct_test_num__ = -1;\ | |
| _fct_cmt("Strict compiler warnings will complain in 'blank' suites.")\ | |
| _fct_cmt("so we are going to do a 'noop' to trick them.")\ | |
| fct_test_num__ = fct_test_num__;\ | |
| if ( fct_ts__is_ending_mode(ts__) )\ | |
| {\ | |
| _fct_cmt("flag the test suite as complete.");\ | |
| fct_ts__end(ts__);\ | |
| break;\ | |
| } | |
| /* Closes off a "Fixture" test suite. */ | |
| #define FCT_FIXTURE_SUITE_END() \ | |
| if ( fct_ts__is_cnt_mode(ts__) )\ | |
| {\ | |
| fct_ts__cnt_end(ts__);\ | |
| }\ | |
| }\ | |
| fctkern__add_ts((&fctkern__), ts__);\ | |
| fctkern__log_suite_end((&fctkern__), ts__);\ | |
| ts__ = NULL;\ | |
| } | |
| #define FCT_SETUP_BGN()\ | |
| if ( fct_ts__is_setup_mode(ts__) ) { | |
| #define FCT_SETUP_END() \ | |
| fct_ts__setup_end(ts__); } | |
| #define FCT_TEARDOWN_BGN() \ | |
| if ( fct_ts__is_teardown_mode(ts__) ) {\ | |
| #define FCT_TEARDOWN_END() \ | |
| fct_ts__teardown_end(ts__); \ | |
| continue; \ | |
| } | |
| /* Lets you create a test suite, where maybe you don't want a fixture. We | |
| do it by 'stubbing' out the setup/teardown logic. */ | |
| #define FCT_SUITE_BGN(Name) \ | |
| FCT_FIXTURE_SUITE_BGN(Name) {\ | |
| FCT_SETUP_BGN() {_fct_cmt("stubbed"); } FCT_SETUP_END()\ | |
| FCT_TEARDOWN_BGN() {_fct_cmt("stubbed");} FCT_TEARDOWN_END()\ | |
| #define FCT_SUITE_END() } FCT_FIXTURE_SUITE_END() | |
| /* Depending on whether or not we are counting the tests, we will have to | |
| first determine if the test is the "current" count. Then we have to determine | |
| if we can pass the filter. Finally we will execute everything so that when a | |
| check fails, we can "break" out to the end of the test. */ | |
| #define FCT_TEST_BGN(_NAME_) \ | |
| {\ | |
| char const *test_name__ = #_NAME_;\ | |
| ++fct_test_num__;\ | |
| if ( fct_ts__is_cnt_mode(ts__) )\ | |
| {\ | |
| fct_ts__inc_total_test_num(ts__);\ | |
| }\ | |
| else if ( fct_ts__is_test_mode(ts__) \ | |
| && fct_ts__is_test_cnt(ts__, fct_test_num__) )\ | |
| {\ | |
| int is_pass__;\ | |
| is_pass__ = FCT_FALSE;\ | |
| fct_ts__test_begin(ts__);\ | |
| if ( fctkern__pass_filter(&fctkern__, test_name__ ) )\ | |
| {\ | |
| fct_test_t *test__ = fct_test_new( test_name__ );\ | |
| fctkern__log_test_start(&fctkern__, test__);\ | |
| for (;;) \ | |
| { | |
| #define FCT_TEST_END() \ | |
| break;\ | |
| }\ | |
| fct_ts__add_test(ts__, test__);\ | |
| fctkern__log_test_end(&fctkern__, test__);\ | |
| }\ | |
| fct_ts__test_end(ts__);\ | |
| continue;\ | |
| }\ | |
| } | |
| /* | |
| --------------------------------------------------------- | |
| CHECKING MACROS | |
| ---------------------------------------------------------- | |
| For now we only have the one "positive" check macro. In the future I plan | |
| to add more macros that check for different types of common conditions. | |
| */ | |
| #define fct_chk(_CNDTN_) \ | |
| {\ | |
| fctchk_t *chk =NULL;\ | |
| is_pass__ = (_CNDTN_);\ | |
| chk = fctchk_new(#_CNDTN_, __FILE__, __LINE__, is_pass__);\ | |
| fct_test__add(test__, chk);\ | |
| fctkern__log_chk(&fctkern__, chk);\ | |
| if ( !is_pass__ ) { break; }\ | |
| } | |
| /* | |
| --------------------------------------------------------- | |
| GUT CHECK MACROS | |
| ---------------------------------------------------------- | |
| The following macros are used to help check the "guts" of | |
| the FCT, and to confirm that it all works according to spec. | |
| */ | |
| /* Generates a message to STDERR and exits the application with a | |
| non-zero number. */ | |
| #define _FCT_GUTCHK(_CNDTN_) \ | |
| if ( !(_CNDTN_) ) {\ | |
| fprintf(stderr, "gutchk fail: '" #_CNDTN_ "' was not true.\n");\ | |
| exit(1);\ | |
| }\ | |
| else {\ | |
| fprintf(stdout, "gutchk pass: '" #_CNDTN_ "'\n");\ | |
| } | |
| /* | |
| --------------------------------------------------------- | |
| CLOSING STATEMENTS | |
| ---------------------------------------------------------- | |
| */ | |
| /* This is defined at the start of the file. We are undefining it | |
| here so it doesn't conflict with existing. */ | |
| #if defined(WIN32) | |
| # undef _CRT_SECURE_NO_WARNINGS | |
| #endif | |
| #endif /* !FCT_INCLUDED__IMB */ |