- Added CMake makefiles as alternative to regular Makefiles.
- Added preliminary Code Coverage tests for AES, ARC4, Base64, MPI, SHA-family, MD-family and HMAC-SHA-family.
diff --git a/tests/fct.h b/tests/fct.h
new file mode 100644
index 0000000..f6c1251
--- /dev/null
+++ b/tests/fct.h
@@ -0,0 +1,1688 @@
+/*
+====================================================================
+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 */