1 /*
2 ====================================================================
3 Copyright (c) 2008 Ian Blumel.  All rights reserved.
4 
5 FCTX (Fast C Test) Unit Testing Framework
6 
7 Copyright (c) 2008, Ian Blumel (ian.blumel@gmail.com)
8 All rights reserved.
9 
10 This license is based on the BSD License.
11 
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions are
14 met:
15 
16     * Redistributions of source code must retain the above copyright
17     notice, this list of conditions and the following disclaimer.
18 
19     * Redistributions in binary form must reproduce the above copyright
20     notice, this list of conditions and the following disclaimer in
21     the documentation and/or other materials provided with the
22     distribution.
23 
24     * Neither the name of, Ian Blumel, nor the names of its
25     contributors may be used to endorse or promote products derived
26     from this software without specific prior written permission.
27 
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
29 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
31 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
32 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
34 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
35 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
36 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 ====================================================================
40 
41 File: fct.h
42 */
43 
44 #if !defined(FCT_INCLUDED__IMB)
45 #define FCT_INCLUDED__IMB
46 
47 /* Configuration Values. You can over-ride these values in your own
48 header, then include this header. For example, in your file, myfct.h,
49 
50     #define FCT_DEFAULT_LOGGER "standard"
51     #include "fct.h"
52 
53 then if your unit tests included, myfct.h, you would default to work
54 with a standard logger. */
55 
56 #if !defined(FCT_DEFAULT_LOGGER)
57 #   define FCT_DEFAULT_LOGGER  "standard"
58 #endif /* !FCT_DEFAULT_LOGGER */
59 
60 #define FCT_VERSION_MAJOR 1
61 #define FCT_VERSION_MINOR 6
62 #define FCT_VERSION_MICRO 1
63 
64 #define _FCT_QUOTEME(x) #x
65 #define FCT_QUOTEME(x) _FCT_QUOTEME(x)
66 
67 #define FCT_VERSION_STR (FCT_QUOTEME(FCT_VERSION_MAJOR) "."\
68                          FCT_QUOTEME(FCT_VERSION_MINOR) "."\
69                          FCT_QUOTEME(FCT_VERSION_MICRO))
70 
71 #include <string.h>
72 #include <assert.h>
73 #include <stdarg.h>
74 #include <stdlib.h>
75 #include <stdio.h>
76 #include <time.h>
77 #include <float.h>
78 #include <math.h>
79 #include <ctype.h>
80 
81 #define FCT_MAX_NAME           256
82 #define FCT_MAX_LOG_LINE       256
83 
84 #define nbool_t int
85 #define FCT_TRUE   1
86 #define FCT_FALSE  0
87 
88 #define FCTMIN(x, y) ( x < y) ? (x) : (y)
89 
90 #ifndef __INTEL_COMPILER
91 /* Use regular assertions for non-Intel compilers */
92 #define FCT_ASSERT(expr) assert(expr)
93 #else
94 /* Silence Intel warnings on assert(expr && "str") or assert("str") */
95 #define FCT_ASSERT(expr) do {             \
96     _Pragma("warning(push,disable:279)"); \
97     assert(expr);                         \
98     _Pragma("warning(pop)");              \
99     } while (0)
100 #endif
101 
102 #if defined(__cplusplus)
103 #define FCT_EXTERN_C extern "C"
104 #else
105 #define FCT_EXTERN_C
106 #endif
107 
108 /* Forward declarations. The following forward declarations are required
109 because there is a inter-relationship between certain objects that
110 just can not be untwined. */
111 typedef struct _fct_logger_evt_t fct_logger_evt_t;
112 typedef struct _fct_logger_i fct_logger_i;
113 typedef struct _fct_logger_types_t fct_logger_types_t;
114 typedef struct _fct_standard_logger_t fct_standard_logger_t;
115 typedef struct _fct_junit_logger_t fct_junit_logger_t;
116 typedef struct _fct_minimal_logger_t fct_minimal_logger_t;
117 typedef struct _fctchk_t fctchk_t;
118 typedef struct _fct_test_t fct_test_t;
119 typedef struct _fct_ts_t fct_ts_t;
120 typedef struct _fctkern_t fctkern_t;
121 
122 /* Forward declare some functions used throughout. */
123 static fct_logger_i*
124 fct_standard_logger_new(void);
125 
126 static fct_logger_i*
127 fct_minimal_logger_new(void);
128 
129 static fct_junit_logger_t *
130 fct_junit_logger_new(void);
131 
132 static void
133 fct_logger__del(fct_logger_i *logger);
134 
135 static void
136 fct_logger__on_chk(fct_logger_i *self, fctchk_t const *chk);
137 
138 static void
139 fct_logger__on_test_start(fct_logger_i *logger, fct_test_t const *test);
140 
141 static void
142 fct_logger__on_test_end(fct_logger_i *logger, fct_test_t *test);
143 
144 static void
145 fct_logger__on_test_suite_start(fct_logger_i *logger, fct_ts_t const *ts);
146 
147 static void
148 fct_logger__on_test_suite_end(fct_logger_i *logger, fct_ts_t const *ts);
149 
150 static void
151 fct_logger__on_test_suite_skip(
152     fct_logger_i *logger,
153     char const *condition,
154     char const *name
155 );
156 
157 static void
158 fct_logger__on_test_skip(
159     fct_logger_i *logger,
160     char const *condition,
161     char const *name
162 );
163 
164 
165 static void
166 fct_logger__on_warn(fct_logger_i *logger, char const *warn);
167 
168 
169 
170 /* Explicitly indicate a no-op */
171 #define fct_pass()
172 
173 #define fct_unused(x)  (void)(x)
174 
175 /* This is just a little trick to let me put comments inside of macros. I
176 really only want to bother with this when we are "unwinding" the macros
177 for debugging purposes. */
178 #if defined(FCT_CONF_UNWIND)
179 #	define _fct_cmt(string)		{char*_=string;}
180 #else
181 #	define _fct_cmt(string)
182 #endif
183 
184 /*
185 --------------------------------------------------------
186 UTILITIES
187 --------------------------------------------------------
188 */
189 
190 
191 /* STDIO and STDERR redirect support */
192 #define FCT_PIPE_RESERVE_BYTES_DEFAULT  512
193 static int fct_stdout_pipe[2];
194 static int fct_stderr_pipe[2];
195 static int fct_saved_stdout;
196 static int fct_saved_stderr;
197 
198 /* Platform indepedent pipe functions. TODO: Look to figure this out in a way
199 that follows the ISO C++ conformant naming convention. */
200 #if defined(WIN32)
201 #    include <io.h>
202 #    include <fcntl.h>
203 #    define _fct_pipe(_PFDS_) \
204         _pipe((_PFDS_), FCT_PIPE_RESERVE_BYTES_DEFAULT, _O_TEXT)
205 #    define _fct_dup  _dup
206 #    define _fct_dup2 _dup2
207 #    define _fct_close _close
208 #    define _fct_read  _read
209 /* Until I can figure a better way to do this, rely on magic numbers. */
210 #    define STDOUT_FILENO 1
211 #    define STDERR_FILENO 2
212 #else
213 #    include <unistd.h>
214 #    define _fct_pipe  pipe
215 #    define _fct_dup   dup
216 #    define _fct_dup2  dup2
217 #    define _fct_close close
218 #    define _fct_read  read
219 #endif /* WIN32 */
220 
221 
222 
223 
224 static void
fct_switch_std_to_buffer(int std_pipe[2],FILE * out,int fileno_,int * save_handle)225 fct_switch_std_to_buffer(int std_pipe[2], FILE *out, int fileno_, int *save_handle)
226 {
227     fflush(out);
228     *save_handle = _fct_dup(fileno_);
229     if ( _fct_pipe(std_pipe) != 0 )
230     {
231         exit(1);
232     }
233     _fct_dup2(std_pipe[1], fileno_);
234     _fct_close(std_pipe[1]);
235 }
236 
237 
238 static void
fct_switch_std_to_std(FILE * out,int fileno_,int save_handle)239 fct_switch_std_to_std(FILE *out, int fileno_, int save_handle)
240 {
241     fflush(out);
242     _fct_dup2(save_handle, fileno_);
243 }
244 
245 
246 #define FCT_SWITCH_STDOUT_TO_BUFFER() \
247     fct_switch_std_to_buffer(fct_stdout_pipe, stdout, STDOUT_FILENO, &fct_saved_stdout)
248 #define FCT_SWITCH_STDOUT_TO_STDOUT() \
249     fct_switch_std_to_std(stdout, STDOUT_FILENO, fct_saved_stdout)
250 #define FCT_SWITCH_STDERR_TO_BUFFER() \
251     fct_switch_std_to_buffer(fct_stderr_pipe, stderr, STDERR_FILENO, &fct_saved_stderr)
252 #define FCT_SWITCH_STDERR_TO_STDERR() \
253     fct_switch_std_to_std(stderr, STDERR_FILENO, fct_saved_stderr)
254 
255 
256 /* Utility for truncated, safe string copies. The NUM
257 should be the length of DST plus the null-termintor. */
258 static void
fctstr_safe_cpy(char * dst,char const * src,size_t num)259 fctstr_safe_cpy(char *dst, char const *src, size_t num)
260 {
261     FCT_ASSERT( dst != NULL );
262     FCT_ASSERT( src != NULL );
263     FCT_ASSERT( num > 0 );
264 #if defined(WIN32) && _MSC_VER >= 1400
265     strncpy_s(dst, num, src, _TRUNCATE);
266 #else
267     strncpy(dst, src, num);
268 #endif
269     dst[num-1] = '\0';
270 }
271 
272 /* Isolate the vsnprintf implementation */
273 static int
fct_vsnprintf(char * buffer,size_t buffer_len,char const * format,va_list args)274 fct_vsnprintf(char *buffer,
275               size_t buffer_len,
276               char const *format,
277               va_list args)
278 {
279     int count =0;
280     /* Older microsoft compilers where not ANSI compliant with this
281     function and you had to use _vsnprintf. I will assume that newer
282     Microsoft Compilers start implementing vsnprintf. */
283 #if defined(_MSC_VER) && (_MSC_VER < 1400)
284     count = _vsnprintf(buffer, buffer_len, format, args);
285 #elif defined(_MSC_VER) && (_MSC_VER >= 1400)
286     count = vsnprintf_s(buffer, buffer_len, _TRUNCATE, format, args);
287 #else
288     count = vsnprintf(buffer, buffer_len, format, args);
289 #endif
290     return count;
291 }
292 
293 
294 /* Isolate the snprintf implemenation. */
295 static int
fct_snprintf(char * buffer,size_t buffer_len,char const * format,...)296 fct_snprintf(char *buffer, size_t buffer_len, char const *format, ...)
297 {
298     int count =0;
299     va_list args;
300     va_start(args, format);
301     count =fct_vsnprintf(buffer, buffer_len, format, args);
302     va_end(args);
303     return count;
304 }
305 
306 
307 /* Helper to for cloning strings on the heap. Returns NULL for
308 an out of memory condition. */
309 static char*
fctstr_clone(char const * s)310 fctstr_clone(char const *s)
311 {
312     char *k =NULL;
313     size_t klen =0;
314     FCT_ASSERT( s != NULL && "invalid arg");
315     klen = strlen(s)+1;
316     k = (char*)malloc(sizeof(char)*klen+1);
317     fctstr_safe_cpy(k, s, klen);
318     return k;
319 }
320 
321 
322 /* Clones and returns a lower case version of the original string. */
323 static char*
fctstr_clone_lower(char const * s)324 fctstr_clone_lower(char const *s)
325 {
326     char *k =NULL;
327     size_t klen =0;
328     size_t i;
329     if ( s == NULL )
330     {
331         return NULL;
332     }
333     klen = strlen(s)+1;
334     k = (char*)malloc(sizeof(char)*klen+1);
335     for ( i=0; i != klen; ++i )
336     {
337         k[i] = (char)tolower(s[i]);
338     }
339     return k;
340 }
341 
342 
343 /* A very, very simple "filter". This just compares the supplied prefix
344 against the test_str, to see if they both have the same starting
345 characters. If they do we return true, otherwise we return false. If the
346 prefix is a blank string or NULL, then it will return FCT_TRUE.*/
347 static nbool_t
fct_filter_pass(char const * prefix,char const * test_str)348 fct_filter_pass(char const *prefix, char const *test_str)
349 {
350     nbool_t is_match = FCT_FALSE;
351     char const *prefix_p;
352     char const *test_str_p;
353 
354     /* If you got nothing to test against, why test? */
355     FCT_ASSERT( test_str != NULL );
356 
357     /* When the prefix is NULL or blank, we always return FCT_TRUE. */
358     if ( prefix == NULL  || prefix[0] == '\0' )
359     {
360         return FCT_TRUE;
361     }
362 
363     /* Iterate through both character arrays at the same time. We are
364     going to play a game and see if we can beat the house. */
365     for ( prefix_p = prefix, test_str_p = test_str;
366             *prefix_p != '\0' && *test_str_p != '\0';
367             ++prefix_p, ++test_str_p )
368     {
369         is_match = *prefix_p == *test_str_p;
370         if ( !is_match )
371         {
372             break;   /* Quit the first time we don't match. */
373         }
374     }
375 
376     /* If the iterator for the test_str is pointing at the null char, and
377     the iterator for the prefix string is not, then the prefix string is
378     larger than the actual test string, and therefore we failed to pass the
379     filter. */
380     if ( *test_str_p == '\0' && *prefix_p != '\0' )
381     {
382         return FCT_FALSE;
383     }
384 
385     /* is_match will be set to the either FCT_TRUE if we kicked of the loop
386     early because our filter ran out of characters or FCT_FALSE if we
387     encountered a mismatch before our filter ran out of characters. */
388     return is_match;
389 }
390 
391 
392 /* Routine checks if two strings are equal. Taken from
393 http://publications.gbdirect.co.uk/c_book/chapter5/character_handling.html
394 */
395 static int
fctstr_eq(char const * s1,char const * s2)396 fctstr_eq(char const *s1, char const *s2)
397 {
398     if ( s1 == s2 )
399     {
400         return 1;
401     }
402     if ( (s1 == NULL && s2 != NULL)
403             || (s1 != NULL && s2 == NULL) )
404     {
405         return 0;
406     }
407     while (*s1 == *s2)
408     {
409         if (*s1 == '\0')
410             return 1;
411         s1++;
412         s2++;
413     }
414     /* Difference detected! */
415     return 0;
416 }
417 
418 
419 static int
fctstr_ieq(char const * s1,char const * s2)420 fctstr_ieq(char const *s1, char const *s2)
421 {
422     if ( s1 == s2 )
423     {
424         return 1;
425     }
426     if ( (s1 == NULL && s2 != NULL)
427             || (s1 != NULL && s2 == NULL) )
428     {
429         return 0;
430     }
431     while (tolower(*s1) == tolower(*s2))
432     {
433         if (*s1 == '\0')
434             return 1;
435         s1++;
436         s2++;
437     }
438     /* Difference detected! */
439     return 0;
440 }
441 
442 
443 /* Returns 1 if the STR contains the CHECK_INCL substring. NULL's
444 are handled, and NULL always INCLUDES NULL. This check is case
445 sensitive. If two strings point to the same place they are
446 included. */
447 static int
fctstr_incl(char const * str,char const * check_incl)448 fctstr_incl(char const *str, char const *check_incl)
449 {
450     static char const *blank_s = "";
451     char const *found = NULL;
452     if ( str == NULL )
453     {
454         str = blank_s;
455     }
456     if ( check_incl == NULL )
457     {
458         check_incl = blank_s;
459     }
460     if ( str == check_incl )
461     {
462         return 1;
463     }
464     found = strstr(str, check_incl);
465     return found != NULL;
466 }
467 
468 
469 /* Does a case insensitive include check. */
470 static int
fctstr_iincl(char const * str,char const * check_incl)471 fctstr_iincl(char const *str, char const *check_incl)
472 {
473     /* Going to do this with a memory allocation to save coding
474     time. In the future this can be rewritten. Both clone_lower
475     and _incl are NULL tolerant. */
476     char *lstr = fctstr_clone_lower(str);
477     char *lcheck_incl = fctstr_clone_lower(check_incl);
478     int found = fctstr_incl(lstr, lcheck_incl);
479     free(lstr);
480     free(lcheck_incl);
481     return found;
482 }
483 
484 
485 /* Returns true if STR starts with CHECK. NULL and NULL is consider
486 true. */
487 static int
fctstr_startswith(char const * str,char const * check)488 fctstr_startswith(char const *str, char const *check)
489 {
490     char const *sp;
491     if ( str == NULL && check == NULL )
492     {
493         return 1;
494     }
495     else if ( ((str == NULL) && (check != NULL))
496               || ((str != NULL) && (check == NULL)) )
497     {
498         return 0;
499     }
500     sp = strstr(str, check);
501     return sp == str;
502 }
503 
504 
505 /* Case insenstive variant of fctstr_startswith. */
506 static int
fctstr_istartswith(char const * str,char const * check)507 fctstr_istartswith(char const *str, char const *check)
508 {
509     /* Taking the lazy approach for now. */
510     char *istr = fctstr_clone_lower(str);
511     char *icheck = fctstr_clone_lower(check);
512     /* TODO: check for memory. */
513     int startswith = fctstr_startswith(istr, icheck);
514     free(istr);
515     free(icheck);
516     return startswith;
517 }
518 
519 
520 /* Returns true if the given string ends with the given
521 check. Treats NULL as a blank string, and as such, will
522 pass the ends with (a blank string endswith a blank string). */
523 static int
fctstr_endswith(char const * str,char const * check)524 fctstr_endswith(char const *str, char const *check)
525 {
526     size_t check_i;
527     size_t str_i;
528     if ( str == NULL && check == NULL )
529     {
530         return 1;
531     }
532     else if ( ((str == NULL) && (check != NULL))
533               || ((str != NULL) && (check == NULL)) )
534     {
535         return 0;
536     }
537     check_i = strlen(check);
538     str_i = strlen(str);
539     if ( str_i < check_i )
540     {
541         return 0;   /* Can't do it string is too small. */
542     }
543     for ( ; check_i != 0; --check_i, --str_i)
544     {
545         if ( str[str_i] != check[check_i] )
546         {
547             return 0; /* Found a case where they are not equal. */
548         }
549     }
550     /* Exahausted check against string, can only be true. */
551     return 1;
552 }
553 
554 
555 static int
fctstr_iendswith(char const * str,char const * check)556 fctstr_iendswith(char const *str, char const *check)
557 {
558     size_t check_i;
559     size_t str_i;
560     if ( str == NULL && check == NULL )
561     {
562         return 1;
563     }
564     else if ( ((str == NULL) && (check != NULL))
565               || ((str != NULL) && (check == NULL)) )
566     {
567         return 0;
568     }
569     check_i = strlen(check);
570     str_i = strlen(str);
571     if ( str_i < check_i )
572     {
573         return 0;   /* Can't do it string is too small. */
574     }
575     for ( ; check_i != 0; --check_i, --str_i)
576     {
577         if ( tolower(str[str_i]) != tolower(check[check_i]) )
578         {
579             return 0; /* Found a case where they are not equal. */
580         }
581     }
582     /* Exahausted check against string, can only be true. */
583     return 1;
584 }
585 
586 
587 /* Use this with the _end variant to get the
588 
589 STARTSWITH ........................................ END
590 
591 effect. Assumes that the line will be maxwidth in characters. The
592 maxwidth can't be greater than FCT_DOTTED_MAX_LEN. */
593 #define FCT_DOTTED_MAX_LEN  256
594 static void
fct_dotted_line_start(size_t maxwidth,char const * startwith)595 fct_dotted_line_start(size_t maxwidth, char const *startwith)
596 {
597     char line[FCT_DOTTED_MAX_LEN];
598     size_t len =0;
599     size_t line_len =0;
600 
601     memset(line, '.', sizeof(char)*maxwidth);
602     len = strlen(startwith);
603     line_len = FCTMIN(maxwidth-1, len);
604     memcpy(line, startwith, sizeof(char)*line_len);
605     if ( len < maxwidth-1)
606     {
607         line[len] = ' ';
608     }
609     line[maxwidth-1] = '\0';
610     fputs(line, stdout);
611 }
612 
613 
614 static void
fct_dotted_line_end(char const * endswith)615 fct_dotted_line_end(char const *endswith)
616 {
617     printf(" %s\n", endswith);
618 }
619 
620 
621 /*
622 --------------------------------------------------------
623 TIMER
624 --------------------------------------------------------
625 This is a low-res implementation at the moment.
626 
627 We will improve this in the future, and isolate the
628 implementation from the rest of the code.
629 */
630 
631 typedef struct _fct_timer_t fct_timer_t;
632 struct _fct_timer_t
633 {
634     clock_t start;
635     clock_t stop;
636     double duration;
637 };
638 
639 
640 static void
fct_timer__init(fct_timer_t * timer)641 fct_timer__init(fct_timer_t *timer)
642 {
643     FCT_ASSERT(timer != NULL);
644     memset(timer, 0, sizeof(fct_timer_t));
645 }
646 
647 
648 static void
fct_timer__start(fct_timer_t * timer)649 fct_timer__start(fct_timer_t *timer)
650 {
651     FCT_ASSERT(timer != NULL);
652     timer->start = clock();
653 }
654 
655 
656 static void
fct_timer__stop(fct_timer_t * timer)657 fct_timer__stop(fct_timer_t *timer)
658 {
659     FCT_ASSERT(timer != NULL);
660     timer->stop = clock();
661     timer->duration = (double) (timer->stop - timer->start) / CLOCKS_PER_SEC;
662 }
663 
664 
665 /* Returns the time in seconds. */
666 static double
fct_timer__duration(fct_timer_t const * timer)667 fct_timer__duration(fct_timer_t const *timer)
668 {
669     FCT_ASSERT( timer != NULL );
670     return timer->duration;
671 }
672 
673 
674 /*
675 --------------------------------------------------------
676 GENERIC LIST
677 --------------------------------------------------------
678 */
679 
680 /* For now we will just keep it at a linear growth rate. */
681 #define FCT_LIST_GROWTH_FACTOR   2
682 
683 /* Starting size for the list, to keep it simple we will start
684 at a reasonable size. */
685 #define FCT_LIST_DEFAULT_START_SZ      8
686 
687 /* Helper macros for quickly iterating through a list. You should be able
688 to do something like,
689 
690   FCT_NLIST_FOREACH_BGN(fct_logger_i*, logger, my_list)
691   {
692      fct_logger__on_blah(logger);
693   }
694   FCT_NLIST_FOREACH_END();
695 
696 */
697 #define FCT_NLIST_FOREACH_BGN(Type, Var, List)\
698 {\
699    if ( List != NULL ) {\
700       size_t item_i##Var;\
701       size_t num_items##Var = fct_nlist__size(List);\
702       for( item_i##Var =0; item_i##Var != num_items##Var; ++item_i##Var )\
703       {\
704          Type Var = (Type) fct_nlist__at((List), item_i##Var);
705 
706 #define FCT_NLIST_FOREACH_END() }}}
707 
708 /* Used to manage a list of loggers. This works mostly like
709 the STL vector, where the array grows as more items are
710 appended. */
711 typedef struct _fct_nlist_t fct_nlist_t;
712 struct _fct_nlist_t
713 {
714     /* Item's are stored as pointers to void. */
715     void **itm_list;
716 
717     /* Indicates the number of element's in the array. */
718     size_t avail_itm_num;
719 
720     /* Indicates the number of actually elements in the array. */
721     size_t used_itm_num;
722 };
723 typedef void (*fct_nlist_on_del_t)(void*);
724 
725 
726 /* Clears the contents of the list, and sets the list count to 0. The
727 actual count remains unchanged. If on_del is supplied it is executed
728 against each list element. */
729 static void
fct_nlist__clear(fct_nlist_t * list,fct_nlist_on_del_t on_del)730 fct_nlist__clear(fct_nlist_t *list, fct_nlist_on_del_t on_del)
731 {
732     size_t itm_i__ =0;
733     FCT_ASSERT( list != NULL );
734     if ( on_del != NULL )
735     {
736         for ( itm_i__=0; itm_i__ != list->used_itm_num; ++itm_i__ )
737         {
738             on_del(list->itm_list[itm_i__]);
739         }
740     }
741     list->used_itm_num =0;
742 }
743 
744 
745 /* If you used init, then close with final. This is useful for
746 working with structures that live on the stack. */
747 static void
fct_nlist__final(fct_nlist_t * list,fct_nlist_on_del_t on_del)748 fct_nlist__final(fct_nlist_t *list, fct_nlist_on_del_t on_del)
749 {
750     FCT_ASSERT( list != NULL );
751     fct_nlist__clear(list, on_del);
752     free(list->itm_list);
753 }
754 
755 
756 static int
fct_nlist__init2(fct_nlist_t * list,size_t start_sz)757 fct_nlist__init2(fct_nlist_t *list, size_t start_sz)
758 {
759     FCT_ASSERT( list != NULL );
760     if ( start_sz == 0 )
761     {
762         list->itm_list = NULL;
763     }
764     else
765     {
766         list->itm_list = (void**)malloc(sizeof(void*)*start_sz);
767         if ( list->itm_list == NULL )
768         {
769             return 0;
770         }
771     }
772     /* If these are both 0, then they are equal and that means
773     that the first append operation will allocate memory. The beauty
774     here is that if the list remains empty, then we save a malloc.
775     Empty lists are relatively common in FCT (consider an error list). */
776     list->avail_itm_num = start_sz;
777     list->used_itm_num =0;
778     return 1;
779 }
780 
781 
782 /* Initializes a list. Useful for populating existing structures.
783 Returns 0 if there was an error allocating memory. Returns 1 otherwise. */
784 #define fct_nlist__init(_LIST_PTR_) \
785    (fct_nlist__init2((_LIST_PTR_), FCT_LIST_DEFAULT_START_SZ))
786 
787 
788 /* Returns the number of elements within the list. */
789 static size_t
fct_nlist__size(fct_nlist_t const * list)790 fct_nlist__size(fct_nlist_t const *list)
791 {
792     FCT_ASSERT( list != NULL );
793     return list->used_itm_num;
794 }
795 
796 
797 /* Returns the item at idx, asserts otherwise. */
798 static void*
fct_nlist__at(fct_nlist_t const * list,size_t idx)799 fct_nlist__at(fct_nlist_t const *list, size_t idx)
800 {
801     FCT_ASSERT( list != NULL );
802     FCT_ASSERT( idx < list->used_itm_num );
803     return list->itm_list[idx];
804 }
805 
806 
807 static void
fct_nlist__append(fct_nlist_t * list,void * itm)808 fct_nlist__append(fct_nlist_t *list, void *itm)
809 {
810     FCT_ASSERT( list != NULL );
811     /* If we ran out of room, then the last increment should be equal to the
812     available space, in this case we need to grow a little more. If this
813     list started as size 0, then we should encounter the same effect as
814     "running out of room." */
815     if ( list->used_itm_num == list->avail_itm_num )
816     {
817         /* Use multiple and add, since the avail_itm_num could be 0. */
818         list->avail_itm_num = list->avail_itm_num*FCT_LIST_GROWTH_FACTOR+\
819                               FCT_LIST_GROWTH_FACTOR;
820         list->itm_list = (void**)realloc(
821                              list->itm_list, sizeof(void*)*list->avail_itm_num
822                          );
823         FCT_ASSERT( list->itm_list != NULL && "memory check");
824     }
825 
826     list->itm_list[list->used_itm_num] = itm;
827     ++(list->used_itm_num);
828 }
829 
830 
831 
832 /*
833 -----------------------------------------------------------
834 A SINGLE CHECK
835 -----------------------------------------------------------
836 This defines a single check. It indicates what the check was,
837 and where it occurred. A "Test" object will have-a bunch
838 of "checks".
839 */
840 
841 struct _fctchk_t
842 {
843     /* This string that represents the condition. */
844     char cndtn[FCT_MAX_LOG_LINE];
845 
846     /* These indicate where the condition occurred. */
847     char file[FCT_MAX_LOG_LINE];
848 
849     int lineno;
850 
851     nbool_t is_pass;
852 
853     /* This is a message that we can "format into", if
854     no format string is specified this should be
855     equivalent to the cntdn. */
856     char msg[FCT_MAX_LOG_LINE];
857 };
858 
859 #define fctchk__is_pass(_CHK_) ((_CHK_)->is_pass)
860 #define fctchk__file(_CHK_)    ((_CHK_)->file)
861 #define fctchk__lineno(_CHK_)  ((_CHK_)->lineno)
862 #define fctchk__cndtn(_CHK_)   ((_CHK_)->cndtn)
863 #define fctchk__msg(_CHK_)     ((_CHK_)->msg)
864 
865 static fctchk_t*
fctchk_new(int is_pass,char const * cndtn,char const * file,int lineno,char const * format,va_list args)866 fctchk_new(int is_pass,
867            char const *cndtn,
868            char const *file,
869            int lineno,
870            char const *format,
871            va_list args)
872 {
873     fctchk_t *chk = NULL;
874 
875     FCT_ASSERT( cndtn != NULL );
876     FCT_ASSERT( file != NULL );
877     FCT_ASSERT( lineno > 0 );
878 
879     chk = (fctchk_t*)calloc(1, sizeof(fctchk_t));
880     if ( chk == NULL )
881     {
882         return NULL;
883     }
884 
885     fctstr_safe_cpy(chk->cndtn, cndtn, FCT_MAX_LOG_LINE);
886     fctstr_safe_cpy(chk->file, file, FCT_MAX_LOG_LINE);
887     chk->lineno = lineno;
888 
889     chk->is_pass =is_pass;
890 
891     if ( format != NULL )
892     {
893         fct_vsnprintf(chk->msg, FCT_MAX_LOG_LINE, format, args);
894     }
895     else
896     {
897         /* Default to make the condition be the message, if there was no format
898         specified. */
899         fctstr_safe_cpy(chk->msg, cndtn, FCT_MAX_LOG_LINE);
900     }
901 
902     return chk;
903 }
904 
905 
906 /* Cleans up a "check" object. If the `chk` is NULL, this function does
907 nothing. */
908 static void
fctchk__del(fctchk_t * chk)909 fctchk__del(fctchk_t *chk)
910 {
911     if ( chk == NULL )
912     {
913         return;
914     }
915     free( chk );
916 }
917 
918 
919 /*
920 -----------------------------------------------------------
921 A TEST
922 -----------------------------------------------------------
923 A suite will have-a list of tests. Where each test will have-a
924 list of failed and passed checks.
925 */
926 
927 struct _fct_test_t
928 {
929     /* List of failed and passed "checks" (fctchk_t). Two seperate
930     lists make it faster to determine how many checks passed and how
931     many checks failed. */
932     fct_nlist_t failed_chks;
933     fct_nlist_t passed_chks;
934 
935     /* To store the test run time */
936     fct_timer_t timer;
937 
938     /* The name of the test case. */
939     char name[FCT_MAX_NAME];
940 };
941 
942 #define fct_test__name(_TEST_) ((_TEST_)->name)
943 
944 /* Clears the failed tests ... partly for internal testing. */
945 #define fct_test__clear_failed(test) \
946     fct_nlist__clear(test->failed_chks, (fct_nlist_on_del_t)fctchk__del);\
947 
948 
949 static void
fct_test__del(fct_test_t * test)950 fct_test__del(fct_test_t *test)
951 {
952     if (test == NULL )
953     {
954         return;
955     }
956     fct_nlist__final(&(test->passed_chks), (fct_nlist_on_del_t)fctchk__del);
957     fct_nlist__final(&(test->failed_chks), (fct_nlist_on_del_t)fctchk__del);
958     free(test);
959 }
960 
961 
962 static fct_test_t*
fct_test_new(char const * name)963 fct_test_new(char const *name)
964 {
965     nbool_t ok =FCT_FALSE;
966     fct_test_t *test =NULL;
967 
968     test = (fct_test_t*)malloc(sizeof(fct_test_t));
969     if ( test == NULL )
970     {
971         return NULL;
972     }
973 
974     fctstr_safe_cpy(test->name, name, FCT_MAX_NAME);
975 
976     /* Failures are an exception, so lets not allocate up
977     the list until we need to. */
978     fct_nlist__init2(&(test->failed_chks), 0);
979     if (!fct_nlist__init(&(test->passed_chks)))
980     {
981         ok =FCT_FALSE;
982         goto finally;
983     }
984 
985     fct_timer__init(&(test->timer));
986 
987     ok =FCT_TRUE;
988 finally:
989     if ( !ok )
990     {
991         fct_test__del(test);
992         test =NULL;
993     }
994     return test;
995 }
996 
997 
998 static void
fct_test__start_timer(fct_test_t * test)999 fct_test__start_timer(fct_test_t *test)
1000 {
1001     FCT_ASSERT( test != NULL );
1002     fct_timer__start(&(test->timer));
1003 }
1004 
1005 
1006 static void
fct_test__stop_timer(fct_test_t * test)1007 fct_test__stop_timer(fct_test_t *test)
1008 {
1009     FCT_ASSERT( test != NULL );
1010     fct_timer__stop(&(test->timer));
1011 }
1012 
1013 
1014 static double
fct_test__duration(fct_test_t const * test)1015 fct_test__duration(fct_test_t const *test)
1016 {
1017     FCT_ASSERT( test != NULL );
1018     return fct_timer__duration(&(test->timer));
1019 }
1020 
1021 
1022 static nbool_t
fct_test__is_pass(fct_test_t const * test)1023 fct_test__is_pass(fct_test_t const *test)
1024 {
1025     FCT_ASSERT( test != NULL );
1026     return fct_nlist__size(&(test->failed_chks)) == 0;
1027 }
1028 
1029 
1030 static void
fct_test__add(fct_test_t * test,fctchk_t * chk)1031 fct_test__add(fct_test_t *test, fctchk_t *chk)
1032 {
1033 
1034     FCT_ASSERT( test != NULL );
1035     FCT_ASSERT( chk != NULL );
1036 
1037     if ( fctchk__is_pass(chk) )
1038     {
1039         fct_nlist__append(&(test->passed_chks), (void*)chk);
1040     }
1041     else
1042     {
1043         fct_nlist__append(&(test->failed_chks), (void*)chk);
1044     }
1045 }
1046 
1047 /* Returns the number of checks made throughout the test. */
1048 static size_t
fct_test__chk_cnt(fct_test_t const * test)1049 fct_test__chk_cnt(fct_test_t const *test)
1050 {
1051     FCT_ASSERT( test != NULL );
1052     return fct_nlist__size(&(test->failed_chks)) \
1053            + fct_nlist__size(&(test->passed_chks));
1054 }
1055 
1056 
1057 /*
1058 -----------------------------------------------------------
1059 TEST SUITE (TS)
1060 -----------------------------------------------------------
1061 */
1062 
1063 
1064 /* The different types of 'modes' that a test suite can be in.
1065 
1066 While the test suite is iterating through all the tests, its "State"
1067 can change from "setup mode", to "test mode" to "tear down" mode.
1068 These help to indicate what mode are currently in. Think of it as a
1069 basic FSM.
1070 
1071             if the count was 0                                 end
1072            +--------->---------------------> ending_mode-----+-+
1073            |                                       ^         |
1074            ^                                       |         ^
1075 start      |                              [if no more tests] |
1076   |        |                                       |         |
1077   +-count_mode -> setup_mode -> test_mode -> teardown_mode->-+
1078                    |  ^                           |          |
1079                    |  +-----------<---------------+          |
1080                    +----------->---[if fct_req fails]--------+
1081 
1082 */
1083 enum ts_mode
1084 {
1085     ts_mode_cnt,         /* To setup when done counting. */
1086     ts_mode_setup,       /* To test when done setup. */
1087     ts_mode_teardown,    /* To ending mode, when no more tests. */
1088     ts_mode_test,        /* To tear down mode. */
1089     ts_mode_ending,      /* To ... */
1090     ts_mode_end,         /* .. The End. */
1091     ts_mode_abort        /* Abort */
1092 };
1093 
1094 /* Types of states the test could be in. */
1095 typedef enum
1096 {
1097     fct_test_status_SUCCESS,
1098     fct_test_status_FAILURE
1099 } fct_test_status;
1100 
1101 
1102 struct _fct_ts_t
1103 {
1104     /* For counting our 'current' test number, and the total number of
1105     tests. */
1106     int  curr_test_num;
1107     int  total_test_num;
1108 
1109     /* Keeps track of the current state of the object while it is walking
1110     through its "FSM" */
1111     enum ts_mode mode;
1112 
1113     /* The name of the test suite. */
1114     char name[FCT_MAX_NAME];
1115 
1116     /* List of tests that where executed within the test suite. */
1117     fct_nlist_t test_list;
1118 };
1119 
1120 
1121 #define fct_ts__is_setup_mode(ts)     ((ts)->mode == ts_mode_setup)
1122 #define fct_ts__is_teardown_mode(ts)  ((ts)->mode == ts_mode_teardown)
1123 #define fct_ts__is_test_mode(ts)      ((ts)->mode == ts_mode_test)
1124 #define fct_ts__is_ending_mode(ts)    ((ts)->mode == ts_mode_ending)
1125 #define fct_ts__is_end(ts)            ((ts)->mode == ts_mode_end)
1126 #define fct_ts__is_cnt_mode(ts)       ((ts)->mode == ts_mode_cnt)
1127 #define fct_ts__is_abort_mode(ts)     ((ts)->mode == ts_mode_abort)
1128 
1129 /* This cndtn is set when we have iterated through all the tests, and
1130 there was nothing more to do. */
1131 #define fct_ts__ending(ts)          ((ts)->mode = ts_mode_ending)
1132 
1133 /* Flag a test suite as complete. It will no longer accept any more tests. */
1134 #define fct_ts__end(ts)  ((ts)->mode = ts_mode_end)
1135 
1136 #define fct_ts__name(ts)              ((ts)->name)
1137 
1138 
1139 static void
fct_ts__del(fct_ts_t * ts)1140 fct_ts__del(fct_ts_t *ts)
1141 {
1142     if ( ts == NULL )
1143     {
1144         return;
1145     }
1146     fct_nlist__final(&(ts->test_list), (fct_nlist_on_del_t)fct_test__del);
1147     free(ts);
1148 }
1149 
1150 static fct_ts_t *
fct_ts_new(char const * name)1151 fct_ts_new(char const *name)
1152 {
1153     fct_ts_t *ts =NULL;
1154     ts = (fct_ts_t*)calloc(1, sizeof(fct_ts_t));
1155     FCT_ASSERT( ts != NULL );
1156 
1157     fctstr_safe_cpy(ts->name, name, FCT_MAX_NAME);
1158     ts->mode = ts_mode_cnt;
1159     fct_nlist__init(&(ts->test_list));
1160     return ts;
1161 }
1162 
1163 
1164 
1165 static nbool_t
fct_ts__is_more_tests(fct_ts_t const * ts)1166 fct_ts__is_more_tests(fct_ts_t const *ts)
1167 {
1168     FCT_ASSERT( ts != NULL );
1169     FCT_ASSERT( !fct_ts__is_end(ts) );
1170     return ts->curr_test_num < ts->total_test_num;
1171 }
1172 
1173 
1174 /* Indicates that we have started a test case. */
1175 static void
fct_ts__test_begin(fct_ts_t * ts)1176 fct_ts__test_begin(fct_ts_t *ts)
1177 {
1178     FCT_ASSERT( !fct_ts__is_end(ts) );
1179     ++(ts->curr_test_num);
1180 }
1181 
1182 
1183 /* Takes OWNERSHIP of a test object, and warehouses it for later stat
1184 generation. */
1185 static void
fct_ts__add_test(fct_ts_t * ts,fct_test_t * test)1186 fct_ts__add_test(fct_ts_t *ts, fct_test_t *test)
1187 {
1188     FCT_ASSERT( ts != NULL && "invalid arg");
1189     FCT_ASSERT( test != NULL && "invalid arg");
1190     FCT_ASSERT( !fct_ts__is_end(ts) );
1191     fct_nlist__append(&(ts->test_list), test);
1192 }
1193 
1194 
1195 static void
fct_ts__test_end(fct_ts_t * ts)1196 fct_ts__test_end(fct_ts_t *ts)
1197 {
1198     FCT_ASSERT( ts != NULL );
1199     /* After a test has completed, move to teardown mode. */
1200     ts->mode = ts_mode_teardown;
1201 }
1202 
1203 
1204 /* Increments the internal count by 1. */
1205 static void
fct_ts__inc_total_test_num(fct_ts_t * ts)1206 fct_ts__inc_total_test_num(fct_ts_t *ts)
1207 {
1208     FCT_ASSERT( ts != NULL );
1209     FCT_ASSERT( fct_ts__is_cnt_mode(ts) );
1210     FCT_ASSERT( !fct_ts__is_end(ts) );
1211     ++(ts->total_test_num);
1212 }
1213 
1214 
1215 /* Flags the end of the setup, which implies we are going to move into
1216 setup mode. You must be already in setup mode for this to work! */
1217 static void
fct_ts__setup_end(fct_ts_t * ts)1218 fct_ts__setup_end(fct_ts_t *ts)
1219 {
1220     if ( ts->mode != ts_mode_abort )
1221     {
1222         ts->mode = ts_mode_test;
1223     }
1224 }
1225 
1226 
1227 static fct_test_t *
fct_ts__make_abort_test(fct_ts_t * ts)1228 fct_ts__make_abort_test(fct_ts_t *ts)
1229 {
1230     char setup_testname[FCT_MAX_LOG_LINE+1] = {'\0'};
1231     char const *suitename = fct_ts__name(ts);
1232     fct_snprintf(setup_testname, FCT_MAX_LOG_LINE, "setup_%s", suitename);
1233     return fct_test_new(setup_testname);
1234 }
1235 
1236 /* Flags a pre-mature abort of a setup (like a failed fct_req). */
1237 static void
fct_ts__setup_abort(fct_ts_t * ts)1238 fct_ts__setup_abort(fct_ts_t *ts)
1239 {
1240     FCT_ASSERT( ts != NULL );
1241     ts->mode = ts_mode_abort;
1242 }
1243 
1244 /* Flags the end of the teardown, which implies we are going to move
1245 into setup mode (for the next 'iteration'). */
1246 static void
fct_ts__teardown_end(fct_ts_t * ts)1247 fct_ts__teardown_end(fct_ts_t *ts)
1248 {
1249     if ( ts->mode == ts_mode_abort )
1250     {
1251         return; /* Because we are aborting . */
1252     }
1253     /* We have to decide if we should keep on testing by moving into tear down
1254     mode or if we have reached the real end and should be moving into the
1255     ending mode. */
1256     if ( fct_ts__is_more_tests(ts) )
1257     {
1258         ts->mode = ts_mode_setup;
1259     }
1260     else
1261     {
1262         ts->mode = ts_mode_ending;
1263     }
1264 }
1265 
1266 
1267 /* Flags the end of the counting, and proceeding to the first setup.
1268 Consider the special case when a test suite has NO tests in it, in
1269 that case we will have a current count that is zero, in which case
1270 we can skip right to 'ending'. */
1271 static void
fct_ts__cnt_end(fct_ts_t * ts)1272 fct_ts__cnt_end(fct_ts_t *ts)
1273 {
1274     FCT_ASSERT( ts != NULL );
1275     FCT_ASSERT( fct_ts__is_cnt_mode(ts) );
1276     FCT_ASSERT( !fct_ts__is_end(ts) );
1277     if (ts->total_test_num == 0  )
1278     {
1279         ts->mode = ts_mode_ending;
1280     }
1281     else
1282     {
1283         ts->mode = ts_mode_setup;
1284     }
1285 }
1286 
1287 
1288 static nbool_t
fct_ts__is_test_cnt(fct_ts_t const * ts,int test_num)1289 fct_ts__is_test_cnt(fct_ts_t const *ts, int test_num)
1290 {
1291     FCT_ASSERT( ts != NULL );
1292     FCT_ASSERT( 0 <= test_num );
1293     FCT_ASSERT( test_num < ts->total_test_num );
1294     FCT_ASSERT( !fct_ts__is_end(ts) );
1295 
1296     /* As we roll through the tests we increment the count. With this
1297     count we can decide if we need to execute a test or not. */
1298     return test_num == ts->curr_test_num;
1299 }
1300 
1301 
1302 /* Returns the # of tests on the FCT TS object. This is the actual
1303 # of tests executed. */
1304 static size_t
fct_ts__tst_cnt(fct_ts_t const * ts)1305 fct_ts__tst_cnt(fct_ts_t const *ts)
1306 {
1307     FCT_ASSERT( ts != NULL );
1308     FCT_ASSERT(
1309         fct_ts__is_end(ts)
1310         && "can't count number of tests executed until the test suite ends"
1311     );
1312     return fct_nlist__size(&(ts->test_list));
1313 }
1314 
1315 
1316 /* Returns the # of tests in the TS object that passed. */
1317 static size_t
fct_ts__tst_cnt_passed(fct_ts_t const * ts)1318 fct_ts__tst_cnt_passed(fct_ts_t const *ts)
1319 {
1320     size_t tally =0;
1321 
1322     FCT_ASSERT( ts != NULL );
1323     FCT_ASSERT( fct_ts__is_end(ts) );
1324 
1325     FCT_NLIST_FOREACH_BGN(fct_test_t*, test, &(ts->test_list))
1326     {
1327         if ( fct_test__is_pass(test) )
1328         {
1329             tally += 1;
1330         }
1331     }
1332     FCT_NLIST_FOREACH_END();
1333     return tally;
1334 }
1335 
1336 
1337 /* Returns the # of checks made throughout a test suite. */
1338 static size_t
fct_ts__chk_cnt(fct_ts_t const * ts)1339 fct_ts__chk_cnt(fct_ts_t const *ts)
1340 {
1341     size_t tally =0;
1342 
1343     FCT_ASSERT( ts != NULL );
1344 
1345     FCT_NLIST_FOREACH_BGN(fct_test_t *, test, &(ts->test_list))
1346     {
1347         tally += fct_test__chk_cnt(test);
1348     }
1349     FCT_NLIST_FOREACH_END();
1350     return tally;
1351 }
1352 
1353 /* Currently the duration is simply a sum of all the tests. */
1354 static double
fct_ts__duration(fct_ts_t const * ts)1355 fct_ts__duration(fct_ts_t const *ts)
1356 {
1357     double tally =0.0;
1358     FCT_ASSERT( ts != NULL );
1359     FCT_NLIST_FOREACH_BGN(fct_test_t *, test, &(ts->test_list))
1360     {
1361         tally += fct_test__duration(test);
1362     }
1363     FCT_NLIST_FOREACH_END();
1364     return tally;
1365 }
1366 
1367 
1368 /*
1369 --------------------------------------------------------
1370 FCT COMMAND LINE OPTION INITIALIZATION (fctcl_init)
1371 --------------------------------------------------------
1372 
1373 Structure used for command line initialization. To keep it clear that we do
1374 not delete the char*'s present on this structure.
1375 */
1376 
1377 
1378 typedef enum
1379 {
1380     FCTCL_STORE_UNDEFINED,
1381     FCTCL_STORE_TRUE,
1382     FCTCL_STORE_VALUE
1383 } fctcl_store_t;
1384 
1385 
1386 typedef struct _fctcl_init_t
1387 {
1388     /* What to parse for this option. --long versus -s. */
1389     char const *long_opt;     /* i.e. --help */
1390     char const *short_opt;    /* i.e. -h */
1391 
1392     /* What action to take when the option is activated. */
1393     fctcl_store_t action;
1394 
1395     /* The help string for the action. */
1396     char const *help;
1397 } fctcl_init_t;
1398 
1399 
1400 /* Use when defining the option list. */
1401 #define FCTCL_INIT_NULL  \
1402     {NULL, NULL, FCTCL_STORE_UNDEFINED, NULL}
1403 
1404 
1405 /*
1406 --------------------------------------------------------
1407 FCT COMMAND LINE OPTION (fctcl)
1408 --------------------------------------------------------
1409 
1410 Specifies the command line configuration options. Use this
1411 to help initialize the fct_clp (command line parser).
1412 */
1413 
1414 
1415 /* Handy strings for storing "true" and "false". We can reference
1416 these strings throughout the parse operation and not have to
1417 worry about dealing with memory. */
1418 #define FCTCL_TRUE_STR "1"
1419 
1420 
1421 typedef struct _fctcl_t
1422 {
1423     /* What to parse for this option. --long versus -s. */
1424     char *long_opt;     /* i.e. --help */
1425     char *short_opt;    /* i.e. -h */
1426 
1427     /* What action to take when the option is activated. */
1428     fctcl_store_t action;
1429 
1430     /* The help string for the action. */
1431     char *help;
1432 
1433     /* The result. */
1434     char *value;
1435 } fctcl_t;
1436 
1437 
1438 #define fctcl_new()  ((fctcl_t*)calloc(1, sizeof(fctcl_t)))
1439 
1440 
1441 static void
fctcl__del(fctcl_t * clo)1442 fctcl__del(fctcl_t *clo)
1443 {
1444     if ( clo == NULL )
1445     {
1446         return;
1447     }
1448     if ( clo->long_opt )
1449     {
1450         free(clo->long_opt);
1451     }
1452     if ( clo->short_opt)
1453     {
1454         free(clo->short_opt);
1455     }
1456     if ( clo->value )
1457     {
1458         free(clo->value);
1459     }
1460     if ( clo->help )
1461     {
1462         free(clo->help);
1463     }
1464     free(clo);
1465 }
1466 
1467 
1468 static fctcl_t*
fctcl_new2(fctcl_init_t const * clo_init)1469 fctcl_new2(fctcl_init_t const *clo_init)
1470 {
1471     fctcl_t *clone = NULL;
1472     int ok =0;
1473     clone = fctcl_new();
1474     if ( clone == NULL )
1475     {
1476         return NULL;
1477     }
1478     clone->action = clo_init->action;
1479     if ( clo_init->help == NULL )
1480     {
1481         clone->help = NULL;
1482     }
1483     else
1484     {
1485         clone->help = fctstr_clone(clo_init->help);
1486         if ( clone->help == NULL )
1487         {
1488             ok =0;
1489             goto finally;
1490         }
1491     }
1492     if ( clo_init->long_opt == NULL )
1493     {
1494         clone->long_opt = NULL;
1495     }
1496     else
1497     {
1498         clone->long_opt = fctstr_clone(clo_init->long_opt);
1499         if ( clone->long_opt == NULL )
1500         {
1501             ok = 0;
1502             goto finally;
1503         }
1504     }
1505     if ( clo_init->short_opt == NULL )
1506     {
1507         clone->short_opt = NULL;
1508     }
1509     else
1510     {
1511         clone->short_opt = fctstr_clone(clo_init->short_opt);
1512         if ( clone->short_opt == NULL )
1513         {
1514             ok =0;
1515             goto finally;
1516         }
1517     }
1518     ok = 1;
1519 finally:
1520     if ( !ok )
1521     {
1522         fctcl__del(clone);
1523         clone = NULL;
1524     }
1525     return clone;
1526 }
1527 
1528 
1529 static int
fctcl__is_option(fctcl_t const * clo,char const * option)1530 fctcl__is_option(fctcl_t const *clo, char const *option)
1531 {
1532     FCT_ASSERT( clo != NULL );
1533     if ( option == NULL )
1534     {
1535         return 0;
1536     }
1537     return ((clo->long_opt != NULL
1538              && fctstr_eq(clo->long_opt, option))
1539             ||
1540             (clo->short_opt != NULL
1541              && fctstr_eq(clo->short_opt, option))
1542            );
1543 }
1544 
1545 
1546 #define fctcl__set_value(_CLO_, _VAL_) \
1547     (_CLO_)->value = fctstr_clone((_VAL_));
1548 
1549 /*
1550 --------------------------------------------------------
1551 FCT COMMAND PARSER (fct_clp)
1552 --------------------------------------------------------
1553 */
1554 
1555 #define FCT_CLP_MAX_ERR_MSG_LEN     256
1556 
1557 typedef struct _fct_clp_t
1558 {
1559     /* List of command line options. */
1560     fct_nlist_t clo_list;
1561 
1562     /* List of parameters (not options). */
1563     fct_nlist_t param_list;
1564 
1565     char error_msg[FCT_CLP_MAX_ERR_MSG_LEN];
1566     int is_error;
1567 } fct_clp_t;
1568 
1569 
1570 static void
fct_clp__final(fct_clp_t * clp)1571 fct_clp__final(fct_clp_t *clp)
1572 {
1573     fct_nlist__final(&(clp->clo_list), (fct_nlist_on_del_t)fctcl__del);
1574     fct_nlist__final(&(clp->param_list), (fct_nlist_on_del_t)free);
1575 }
1576 
1577 
1578 /* Add an configuration options. */
1579 static int
fct_clp__add_options(fct_clp_t * clp,fctcl_init_t const * options)1580 fct_clp__add_options(fct_clp_t *clp, fctcl_init_t const *options)
1581 {
1582     fctcl_init_t const *pclo =NULL;
1583     int ok;
1584     for ( pclo = options; pclo->action != FCTCL_STORE_UNDEFINED; ++pclo )
1585     {
1586         fctcl_t *cpy = fctcl_new2(pclo);
1587         if ( cpy == NULL )
1588         {
1589             ok = 0;
1590             goto finally;
1591         }
1592         fct_nlist__append(&(clp->clo_list), (void*)cpy);
1593     }
1594     ok =1;
1595 finally:
1596     return ok;
1597 }
1598 
1599 /* Returns false if we ran out of memory. */
1600 static int
fct_clp__init(fct_clp_t * clp,fctcl_init_t const * options)1601 fct_clp__init(fct_clp_t *clp, fctcl_init_t const *options)
1602 {
1603     int ok =0;
1604     FCT_ASSERT( clp != NULL );
1605     /* It is just much saner to manage a clone of the options. Then we know
1606     who is in charge of the memory. */
1607     ok = fct_nlist__init(&(clp->clo_list));
1608     if ( !ok )
1609     {
1610         goto finally;
1611     }
1612     if ( options != NULL )
1613     {
1614         ok = fct_clp__add_options(clp, options);
1615         if ( !ok )
1616         {
1617             goto finally;
1618         }
1619     }
1620     ok = fct_nlist__init(&(clp->param_list));
1621     if ( !ok )
1622     {
1623         goto finally;
1624     }
1625     ok =1;
1626 finally:
1627     if ( !ok )
1628     {
1629         fct_clp__final(clp);
1630     }
1631     return ok;
1632 }
1633 
1634 
1635 /* Parses the command line arguments. Use fct_clp__is_error and
1636 fct_clp__get_error to figure out if something went awry. */
1637 static void
fct_clp__parse(fct_clp_t * clp,int argc,char const * argv[])1638 fct_clp__parse(fct_clp_t *clp, int argc, char const *argv[])
1639 {
1640     int argi =1;
1641     int is_option =0;
1642     char *arg =NULL;
1643     char *token =NULL;
1644     char *next_token =NULL;
1645 
1646     clp->error_msg[0] = '\0';
1647     clp->is_error =0;
1648 
1649     while ( argi < argc )
1650     {
1651         is_option =0;
1652         token =NULL;
1653         next_token = NULL;
1654         arg = fctstr_clone(argv[argi]);
1655 
1656 #if defined(_MSC_VER) && _MSC_VER > 1300
1657         token = strtok_s(arg, "=", &next_token);
1658 #else
1659         token = strtok(arg, "=");
1660         next_token = strtok(NULL, "=");
1661 #endif
1662 
1663         FCT_NLIST_FOREACH_BGN(fctcl_t*, pclo, &(clp->clo_list))
1664         {
1665             /* Need to reset for each search. strtok below is destructive. */
1666             if ( fctcl__is_option(pclo, token) )
1667             {
1668                 is_option =1;
1669                 if ( pclo->action == FCTCL_STORE_VALUE )
1670                 {
1671                     /* If this is --xxxx=value then the next strtok should succeed.
1672                     Otherwise, we need to chew up the next argument. */
1673                     if ( next_token != NULL && strlen(next_token) > 0 )
1674                     {
1675                         fctcl__set_value(pclo, next_token);
1676                     }
1677                     else
1678                     {
1679                         ++argi; /* Chew up the next value */
1680                         if ( argi >= argc )
1681                         {
1682                             /* error */
1683                             fct_snprintf(
1684                                 clp->error_msg,
1685                                 FCT_CLP_MAX_ERR_MSG_LEN,
1686                                 "missing argument for %s",
1687                                 token
1688                             );
1689                             clp->is_error =1;
1690                             break;
1691                         }
1692                         fctcl__set_value(pclo, argv[argi]);
1693                     }
1694                 }
1695                 else if (pclo->action == FCTCL_STORE_TRUE)
1696                 {
1697                     fctcl__set_value(pclo, FCTCL_TRUE_STR);
1698                 }
1699                 else
1700                 {
1701                     FCT_ASSERT("undefined action requested");
1702                 }
1703                 break;  /* No need to parse this argument further. */
1704             }
1705         }
1706         FCT_NLIST_FOREACH_END();
1707         /* If we have an error, exit. */
1708         if ( clp->is_error )
1709         {
1710             break;
1711         }
1712         /* If we walked through all the options, and didn't find
1713         anything, then we must have a parameter. Forget the fact that
1714         an unknown option will be treated like a parameter... */
1715         if ( !is_option )
1716         {
1717             fct_nlist__append(&(clp->param_list), arg);
1718             arg =NULL;  /* Owned by the nlist */
1719         }
1720         ++argi;
1721         if ( arg != NULL )
1722         {
1723             free(arg);
1724             arg =NULL;
1725         }
1726     }
1727 }
1728 
1729 
1730 static fctcl_t const*
fct_clp__get_clo(fct_clp_t const * clp,char const * option)1731 fct_clp__get_clo(fct_clp_t const *clp, char const *option)
1732 {
1733     fctcl_t const *found =NULL;
1734 
1735     FCT_NLIST_FOREACH_BGN(fctcl_t const*, pclo, &(clp->clo_list))
1736     {
1737         if ( fctcl__is_option(pclo, option) )
1738         {
1739             found = pclo;
1740             break;
1741         }
1742     }
1743     FCT_NLIST_FOREACH_END();
1744     return found;
1745 }
1746 
1747 
1748 #define fct_clp__optval(_CLP_, _OPTION_) \
1749     fct_clp__optval2((_CLP_), (_OPTION_), NULL)
1750 
1751 
1752 /* Returns the value parsed at the command line, and equal to OPTION.
1753 If the value wasn't parsed, the DEFAULT_VAL is returned instead. */
1754 static char const*
fct_clp__optval2(fct_clp_t * clp,char const * option,char const * default_val)1755 fct_clp__optval2(fct_clp_t *clp, char const *option, char const *default_val)
1756 {
1757     fctcl_t const *clo =NULL;
1758     FCT_ASSERT( clp != NULL );
1759     FCT_ASSERT( option != NULL );
1760     clo = fct_clp__get_clo(clp, option);
1761     if ( clo == NULL || clo->value == NULL)
1762     {
1763         return default_val;
1764     }
1765     return clo->value;
1766 }
1767 
1768 
1769 
1770 /* Mainly used for unit tests. */
1771 static int
fct_clp__is_param(fct_clp_t * clp,char const * param)1772 fct_clp__is_param(fct_clp_t *clp, char const *param)
1773 {
1774     if ( clp == NULL || param == NULL )
1775     {
1776         return 0;
1777     }
1778     FCT_NLIST_FOREACH_BGN(char *, aparam, &(clp->param_list))
1779     {
1780         if ( fctstr_eq(aparam, param) )
1781         {
1782             return 1;
1783         }
1784     }
1785     FCT_NLIST_FOREACH_END();
1786     return 0;
1787 }
1788 
1789 
1790 #define fct_clp__is_error(_CLP_) ((_CLP_)->is_error)
1791 #define fct_clp__get_error(_CLP_) ((_CLP_)->error_msg);
1792 
1793 #define fct_clp__num_clo(_CLP_) \
1794     (fct_nlist__size(&((_CLP_)->clo_list)))
1795 
1796 #define fct_clp__param_cnt(_CLP_) \
1797     (fct_nlist__size(&((_CLP_)->param_list)))
1798 
1799 /* Returns a *reference* to the parameter at _IDX_. Do not modify
1800 its contents. */
1801 #define fct_clp__param_at(_CLP_, _IDX_) \
1802     ((char const*)fct_nlist__at(&((_CLP_)->param_list), (_IDX_)))
1803 
1804 
1805 /* Returns true if the given option was on the command line.
1806 Use either the long or short option name to check against. */
1807 #define fct_clp__is(_CLP_, _OPTION_) \
1808     (fct_clp__optval((_CLP_), (_OPTION_)) != NULL)
1809 
1810 
1811 
1812 /*
1813 --------------------------------------------------------
1814 FCT NAMESPACE
1815 --------------------------------------------------------
1816 
1817 The macros below start to pollute the watch window with
1818 lots of "system" variables. This NAMESPACE is an
1819 attempt to hide all the "system" variables in one place.
1820 */
1821 typedef struct _fct_namespace_t
1822 {
1823     /* The currently active test suite. */
1824     fct_ts_t *ts_curr;
1825     int ts_is_skip_suite;
1826     char const *ts_skip_cndtn;
1827 
1828     /* Current test name. */
1829     char const* curr_test_name;
1830     fct_test_t *curr_test;
1831     const char *test_skip_cndtn;
1832     int test_is_skip;
1833 
1834     /* Counts the number of tests in a test suite. */
1835     int test_num;
1836 
1837     /* Set at the end of the test suites. */
1838     size_t num_total_failed;
1839 } fct_namespace_t;
1840 
1841 
1842 static void
fct_namespace_init(fct_namespace_t * ns)1843 fct_namespace_init(fct_namespace_t *ns)
1844 {
1845     FCT_ASSERT( ns != NULL && "invalid argument!");
1846     memset(ns, 0, sizeof(fct_namespace_t));
1847 }
1848 
1849 
1850 /*
1851 --------------------------------------------------------
1852 FCT KERNAL
1853 --------------------------------------------------------
1854 
1855 The "fctkern" is a singleton that is defined throughout the
1856 system.
1857 */
1858 
1859 struct _fctkern_t
1860 {
1861     /* Holds variables used throughout MACRO MAGIC. In order to reduce
1862     the "noise" in the watch window during a debug trace. */
1863     fct_namespace_t ns;
1864 
1865     /* Command line parsing. */
1866     fct_clp_t cl_parser;
1867 
1868     /* Hold onto the command line arguments. */
1869     int cl_argc;
1870     char const **cl_argv;
1871     /* Track user options. */
1872     fctcl_init_t const *cl_user_opts;
1873 
1874     /* Tracks the delay parsing. */
1875     int cl_is_parsed;
1876 
1877     /* This is an list of loggers that can be used in the fct system. */
1878     fct_nlist_t logger_list;
1879 
1880     /* Array of custom types, you have built-in system ones and you
1881     have optionally supplied user ones.. */
1882     fct_logger_types_t *lt_usr;
1883     fct_logger_types_t *lt_sys;
1884 
1885     /* This is a list of prefix's that can be used to determine if a
1886     test is should be run or not. */
1887     fct_nlist_t prefix_list;
1888 
1889     /* This is a list of test suites that where generated throughout the
1890     testing process. */
1891     fct_nlist_t ts_list;
1892 
1893     /* Records what we expect to fail. */
1894     size_t num_expected_failures;
1895 };
1896 
1897 
1898 #define FCT_OPT_VERSION       "--version"
1899 #define FCT_OPT_VERSION_SHORT "-v"
1900 #define FCT_OPT_HELP          "--help"
1901 #define FCT_OPT_HELP_SHORT    "-h"
1902 #define FCT_OPT_LOGGER        "--logger"
1903 #define FCT_OPT_LOGGER_SHORT  "-l"
1904 static fctcl_init_t FCT_CLP_OPTIONS[] =
1905 {
1906     /* Totally unsafe, since we are assuming we can clean out this data,
1907     what I need to do is have an "initialization" object, full of
1908     const objects. But for now, this should work. */
1909     {
1910         FCT_OPT_VERSION,
1911         FCT_OPT_VERSION_SHORT,
1912         FCTCL_STORE_TRUE,
1913         "Displays the FCTX version number and exits."
1914     },
1915     {
1916         FCT_OPT_HELP,
1917         FCT_OPT_HELP_SHORT,
1918         FCTCL_STORE_TRUE,
1919         "Shows this help."
1920     },
1921     {
1922         FCT_OPT_LOGGER,
1923         FCT_OPT_LOGGER_SHORT,
1924         FCTCL_STORE_VALUE,
1925         NULL
1926     },
1927     FCTCL_INIT_NULL /* Sentinel */
1928 };
1929 
1930 typedef fct_logger_i* (*fct_logger_new_fn)(void);
1931 struct _fct_logger_types_t
1932 {
1933     char const *name;
1934     fct_logger_new_fn logger_new_fn;
1935     char const *desc;
1936 };
1937 
1938 static fct_logger_types_t FCT_LOGGER_TYPES[] =
1939 {
1940     {
1941         "standard",
1942         (fct_logger_new_fn)fct_standard_logger_new,
1943         "the basic fctx logger"
1944     },
1945     {
1946         "minimal",
1947         (fct_logger_new_fn)fct_minimal_logger_new,
1948         "the least amount of logging information."
1949     },
1950     {
1951         "junit",
1952         (fct_logger_new_fn)fct_junit_logger_new,
1953         "junit compatable xml"
1954     },
1955     {NULL, (fct_logger_new_fn)NULL, NULL} /* Sentinel */
1956 };
1957 
1958 
1959 /* Returns the number of filters defined for the fct kernal. */
1960 #define fctkern__filter_cnt(_NK_) (fct_nlist__size(&((_NK_)->prefix_list)))
1961 
1962 
1963 static void
fctkern__add_logger(fctkern_t * nk,fct_logger_i * logger_owns)1964 fctkern__add_logger(fctkern_t *nk, fct_logger_i *logger_owns)
1965 {
1966     FCT_ASSERT(nk != NULL && "invalid arg");
1967     FCT_ASSERT(logger_owns != NULL && "invalid arg");
1968     fct_nlist__append(&(nk->logger_list), logger_owns);
1969 }
1970 
1971 
1972 static void
fctkern__write_help(fctkern_t * nk,FILE * out)1973 fctkern__write_help(fctkern_t *nk, FILE *out)
1974 {
1975     fct_clp_t *clp = &(nk->cl_parser);
1976     fprintf(out, "test.exe [options] prefix_filter ...\n\n");
1977     FCT_NLIST_FOREACH_BGN(fctcl_t*, clo, &(clp->clo_list))
1978     {
1979         if ( clo->short_opt != NULL )
1980         {
1981             fprintf(out, "%s, %s\n", clo->short_opt, clo->long_opt);
1982         }
1983         else
1984         {
1985             fprintf(out, "%s\n", clo->long_opt);
1986         }
1987         if ( !fctstr_ieq(clo->long_opt, FCT_OPT_LOGGER) )
1988         {
1989             /* For now lets not get to fancy with the text wrapping. */
1990             fprintf(out, "  %s\n", clo->help);
1991         }
1992         else
1993         {
1994             fct_logger_types_t *types[2];
1995             int type_i;
1996             fct_logger_types_t *itr;
1997             types[0] = nk->lt_sys;
1998             types[1] = nk->lt_usr;
1999             fputs("  Sets the logger. The types of loggers currently "
2000                   "available are,\n", out);
2001             for (type_i =0; type_i != 2; ++type_i )
2002             {
2003                 for ( itr=types[type_i]; itr && itr->name != NULL; ++itr )
2004                 {
2005                     fprintf(out, "   =%s : %s\n", itr->name, itr->desc);
2006                 }
2007             }
2008             fprintf(out, "  default is '%s'.\n", FCT_DEFAULT_LOGGER);
2009         }
2010     }
2011     FCT_NLIST_FOREACH_END();
2012     fputs("\n", out);
2013 }
2014 
2015 
2016 /* Appends a prefix filter that is used to determine if a test can
2017 be executed or not. If the test starts with the same characters as
2018 the prefix, then it should be "runnable". The prefix filter must be
2019 a non-NULL, non-Blank string. */
2020 static void
fctkern__add_prefix_filter(fctkern_t * nk,char const * prefix_filter)2021 fctkern__add_prefix_filter(fctkern_t *nk, char const *prefix_filter)
2022 {
2023     char *filter =NULL;
2024     size_t filter_len =0;
2025     FCT_ASSERT( nk != NULL && "invalid arg" );
2026     FCT_ASSERT( prefix_filter != NULL && "invalid arg" );
2027     FCT_ASSERT( strlen(prefix_filter) > 0 && "invalid arg" );
2028     /* First we make a copy of the prefix, then we store it away
2029     in our little list. */
2030     filter_len = strlen(prefix_filter);
2031     filter = (char*)malloc(sizeof(char)*(filter_len+1));
2032     fctstr_safe_cpy(filter, prefix_filter, filter_len+1);
2033     fct_nlist__append(&(nk->prefix_list), (void*)filter);
2034 }
2035 
2036 
2037 /* Cleans up the contents of a fctkern. NULL does nothing. */
2038 static void
fctkern__final(fctkern_t * nk)2039 fctkern__final(fctkern_t *nk)
2040 {
2041     if ( nk == NULL )
2042     {
2043         return;
2044     }
2045     fct_clp__final(&(nk->cl_parser));
2046     fct_nlist__final(&(nk->logger_list), (fct_nlist_on_del_t)fct_logger__del);
2047     /* The prefix list is a list of malloc'd strings. */
2048     fct_nlist__final(&(nk->prefix_list), (fct_nlist_on_del_t)free);
2049     fct_nlist__final(&(nk->ts_list), (fct_nlist_on_del_t)fct_ts__del);
2050 }
2051 
2052 
2053 #define fctkern__cl_is_parsed(_NK_) ((_NK_)->cl_is_parsed)
2054 
2055 
2056 static int
fctkern__cl_is(fctkern_t * nk,char const * opt_str)2057 fctkern__cl_is(fctkern_t *nk, char const *opt_str)
2058 {
2059     FCT_ASSERT( opt_str != NULL );
2060     return opt_str[0] != '\0'
2061            && fct_clp__is(&(nk->cl_parser), opt_str);
2062 }
2063 
2064 
2065 /* Returns the command line value given by OPT_STR. If OPT_STR was not defined
2066 at the command line, DEF_STR is returned (you can use NULL for the DEF_STR).
2067 The result returned should not be mofidied, and MAY even be the same pointer
2068 to DEF_STR. */
2069 static char const *
fctkern__cl_val2(fctkern_t * nk,char const * opt_str,char const * def_str)2070 fctkern__cl_val2(fctkern_t *nk, char const *opt_str, char const *def_str)
2071 {
2072     FCT_ASSERT( opt_str != NULL );
2073     if ( nk == NULL )
2074     {
2075         return NULL;
2076     }
2077     return fct_clp__optval2(&(nk->cl_parser), opt_str, def_str);
2078 }
2079 
2080 
2081 /* Selects a logger from the list based on the selection name.
2082 May return NULL if the name doesn't exist in the list. */
2083 static fct_logger_i*
fckern_sel_log(fct_logger_types_t * search,char const * sel_logger)2084 fckern_sel_log(fct_logger_types_t *search, char const *sel_logger)
2085 {
2086     fct_logger_types_t *iter;
2087     FCT_ASSERT(search != NULL);
2088     FCT_ASSERT(sel_logger != NULL);
2089     FCT_ASSERT(strlen(sel_logger) > 0);
2090     for ( iter = search; iter->name != NULL; ++iter)
2091     {
2092         if ( fctstr_ieq(iter->name, sel_logger) )
2093         {
2094             return iter->logger_new_fn();
2095         }
2096     }
2097     return NULL;
2098 }
2099 
2100 static int
fctkern__cl_parse_config_logger(fctkern_t * nk)2101 fctkern__cl_parse_config_logger(fctkern_t *nk)
2102 {
2103     fct_logger_i *logger =NULL;
2104     char const *sel_logger =NULL;
2105     char const *def_logger =FCT_DEFAULT_LOGGER;
2106     sel_logger = fctkern__cl_val2(nk, FCT_OPT_LOGGER, def_logger);
2107     FCT_ASSERT(sel_logger != NULL && "should never be NULL");
2108     /* First search the user selected types, then search the
2109     built-in types. */
2110     if ( nk->lt_usr != NULL )
2111     {
2112         logger = fckern_sel_log(nk->lt_usr, sel_logger);
2113     }
2114     if ( nk->lt_sys != NULL && logger == NULL )
2115     {
2116         logger = fckern_sel_log(nk->lt_sys, sel_logger);
2117     }
2118     if ( logger == NULL )
2119     {
2120         /* No logger configured, you must have supplied an invalid selection. */
2121         fprintf(stderr, "error: unknown logger selected - '%s'", sel_logger);
2122         return 0;
2123     }
2124     fctkern__add_logger(nk, logger);
2125     logger = NULL;  /* owned by nk. */
2126     return 1;
2127 }
2128 
2129 
2130 
2131 /* Call this if you want to (re)parse the command line options with a new
2132 set of options. Returns -1 if you are to abort with EXIT_SUCCESS, returns
2133 0 if you are to abort with EXIT_FAILURE and returns 1 if you are to continue. */
2134 static int
fctkern__cl_parse(fctkern_t * nk)2135 fctkern__cl_parse(fctkern_t *nk)
2136 {
2137     int status =0;
2138     size_t num_params =0;
2139     size_t param_i =0;
2140     if ( nk == NULL )
2141     {
2142         return 0;
2143     }
2144     if ( nk->cl_user_opts != NULL )
2145     {
2146         if ( !fct_clp__add_options(&(nk->cl_parser), nk->cl_user_opts) )
2147         {
2148             status =0;
2149             goto finally;
2150         }
2151     }
2152     /* You want to add the "house options" after the user defined ones. The
2153     options are stored as a list so it means that any option listed after
2154     the above ones won't get parsed. */
2155     if ( !fct_clp__add_options(&(nk->cl_parser), FCT_CLP_OPTIONS) )
2156     {
2157         status =0;
2158         goto finally;
2159     }
2160     fct_clp__parse(&(nk->cl_parser), nk->cl_argc, nk->cl_argv);
2161     if ( fct_clp__is_error(&(nk->cl_parser)) )
2162     {
2163         char *err = fct_clp__get_error(&(nk->cl_parser));
2164         fprintf(stderr, "error: %s", err);
2165         status =0;
2166         goto finally;
2167     }
2168     num_params = fct_clp__param_cnt(&(nk->cl_parser));
2169     for ( param_i =0; param_i != num_params; ++param_i )
2170     {
2171         char const *param = fct_clp__param_at(&(nk->cl_parser), param_i);
2172         fctkern__add_prefix_filter(nk, param);
2173     }
2174     if ( fctkern__cl_is(nk, FCT_OPT_VERSION) )
2175     {
2176         (void)printf("Built using FCTX version %s.\n", FCT_VERSION_STR);
2177         status = -1;
2178         goto finally;
2179     }
2180     if ( fctkern__cl_is(nk, FCT_OPT_HELP) )
2181     {
2182         fctkern__write_help(nk, stdout);
2183         status = -1;
2184         goto finally;
2185     }
2186     if ( !fctkern__cl_parse_config_logger(nk) )
2187     {
2188         status = -1;
2189         goto finally;
2190     }
2191     status =1;
2192     nk->cl_is_parsed =1;
2193 finally:
2194     return status;
2195 }
2196 
2197 
2198 
2199 /* Parses the command line and sets up the framework. The argc and argv
2200 should be directly from the program's main. */
2201 static int
fctkern__init(fctkern_t * nk,int argc,const char * argv[])2202 fctkern__init(fctkern_t *nk, int argc, const char *argv[])
2203 {
2204     if ( argc == 0 && argv == NULL )
2205     {
2206         return 0;
2207     }
2208     memset(nk, 0, sizeof(fctkern_t));
2209     fct_clp__init(&(nk->cl_parser), NULL);
2210     fct_nlist__init(&(nk->logger_list));
2211     nk->lt_usr = NULL;  /* Supplied via 'install' mechanics. */
2212     nk->lt_sys = FCT_LOGGER_TYPES;
2213     fct_nlist__init2(&(nk->prefix_list), 0);
2214     fct_nlist__init2(&(nk->ts_list), 0);
2215     nk->cl_is_parsed =0;
2216     /* Save a copy of the arguments. We do a delay parse of the command
2217     line arguments in order to allow the client code to optionally configure
2218     the command line parser.*/
2219     nk->cl_argc = argc;
2220     nk->cl_argv = argv;
2221     fct_namespace_init(&(nk->ns));
2222     return 1;
2223 }
2224 
2225 
2226 /* Takes OWNERSHIP of the test suite after we have finished executing
2227 its contents. This way we can build up all kinds of summaries at the end
2228 of a run. */
2229 static void
fctkern__add_ts(fctkern_t * nk,fct_ts_t * ts)2230 fctkern__add_ts(fctkern_t *nk, fct_ts_t *ts)
2231 {
2232     FCT_ASSERT( nk != NULL );
2233     FCT_ASSERT( ts != NULL );
2234     fct_nlist__append(&(nk->ts_list), ts);
2235 }
2236 
2237 
2238 /* Returns FCT_TRUE if the supplied test_name passes the filters set on
2239 this test suite. If there are no filters, we return FCT_TRUE always. */
2240 static nbool_t
fctkern__pass_filter(fctkern_t * nk,char const * test_name)2241 fctkern__pass_filter(fctkern_t *nk, char const *test_name)
2242 {
2243     size_t prefix_i =0;
2244     size_t prefix_list_size =0;
2245     FCT_ASSERT( nk != NULL && "invalid arg");
2246     FCT_ASSERT( test_name != NULL );
2247     FCT_ASSERT( strlen(test_name) > 0 );
2248     prefix_list_size = fctkern__filter_cnt(nk);
2249     /* If there is no filter list, then we return FCT_TRUE always. */
2250     if ( prefix_list_size == 0 )
2251     {
2252         return FCT_TRUE;
2253     }
2254     /* Iterate through the prefix filter list, and see if we have
2255     anything that does not pass. All we require is ONE item that
2256     passes the test in order for us to succeed here. */
2257     for ( prefix_i = 0; prefix_i != prefix_list_size; ++prefix_i )
2258     {
2259         char const *prefix = (char const*)fct_nlist__at(
2260                                  &(nk->prefix_list), prefix_i
2261                              );
2262         nbool_t pass = fct_filter_pass(prefix, test_name);
2263         if ( pass )
2264         {
2265             return FCT_TRUE;
2266         }
2267     }
2268     /* Otherwise, we never managed to find a prefix that satisfied the
2269     supplied test name. Therefore we have failed to pass to the filter
2270     list test. */
2271     return FCT_FALSE;
2272 }
2273 
2274 
2275 /* Returns the number of tests that were performed. */
2276 static size_t
fctkern__tst_cnt(fctkern_t const * nk)2277 fctkern__tst_cnt(fctkern_t const *nk)
2278 {
2279     size_t tally =0;
2280     FCT_ASSERT( nk != NULL );
2281     FCT_NLIST_FOREACH_BGN(fct_ts_t *, ts, &(nk->ts_list))
2282     {
2283         tally += fct_ts__tst_cnt(ts);
2284     }
2285     FCT_NLIST_FOREACH_END();
2286     return tally;
2287 }
2288 
2289 
2290 /* Returns the number of tests that passed. */
2291 static size_t
fctkern__tst_cnt_passed(fctkern_t const * nk)2292 fctkern__tst_cnt_passed(fctkern_t const *nk)
2293 {
2294     size_t tally =0;
2295     FCT_ASSERT( nk != NULL );
2296 
2297     FCT_NLIST_FOREACH_BGN(fct_ts_t*, ts, &(nk->ts_list))
2298     {
2299         tally += fct_ts__tst_cnt_passed(ts);
2300     }
2301     FCT_NLIST_FOREACH_END();
2302 
2303     return tally;
2304 }
2305 
2306 
2307 /* Returns the number of tests that failed. */
2308 #define fctkern__tst_cnt_failed(nk) \
2309     (fctkern__tst_cnt(nk) - fctkern__tst_cnt_passed(nk))
2310 
2311 
2312 /* Returns the number of checks made throughout the entire test. */
2313 #if defined(FCT_USE_TEST_COUNT)
2314 static size_t
fctkern__chk_cnt(fctkern_t const * nk)2315 fctkern__chk_cnt(fctkern_t const *nk)
2316 {
2317     size_t tally =0;
2318     FCT_ASSERT( nk != NULL );
2319 
2320     FCT_NLIST_FOREACH_BGN(fct_ts_t *, ts, &(nk->ts_list))
2321     {
2322         tally += fct_ts__chk_cnt(ts);
2323     }
2324     FCT_NLIST_FOREACH_END();
2325     return tally;
2326 }
2327 #endif /* FCT_USE_TEST_COUNT */
2328 
2329 
2330 /* Indicates the very end of all the tests. */
2331 #define fctkern__end(nk) /* unused */
2332 
2333 
2334 static void
fctkern__log_suite_start(fctkern_t * nk,fct_ts_t const * ts)2335 fctkern__log_suite_start(fctkern_t *nk, fct_ts_t const *ts)
2336 {
2337     FCT_ASSERT( nk != NULL );
2338     FCT_ASSERT( ts != NULL );
2339     FCT_NLIST_FOREACH_BGN(fct_logger_i*, logger, &(nk->logger_list))
2340     {
2341         fct_logger__on_test_suite_start(logger, ts);
2342     }
2343     FCT_NLIST_FOREACH_END();
2344 }
2345 
2346 
2347 static void
fctkern__log_suite_end(fctkern_t * nk,fct_ts_t const * ts)2348 fctkern__log_suite_end(fctkern_t *nk, fct_ts_t const *ts)
2349 {
2350     FCT_ASSERT( nk != NULL );
2351     FCT_ASSERT( ts != NULL );
2352     FCT_NLIST_FOREACH_BGN(fct_logger_i*, logger, &(nk->logger_list))
2353     {
2354         fct_logger__on_test_suite_end(logger, ts);
2355     }
2356     FCT_NLIST_FOREACH_END();
2357 }
2358 
2359 
2360 static void
fctkern__log_suite_skip(fctkern_t * nk,char const * condition,char const * name)2361 fctkern__log_suite_skip(fctkern_t *nk, char const *condition, char const *name)
2362 {
2363     if ( nk == NULL )
2364     {
2365         return;
2366     }
2367     FCT_NLIST_FOREACH_BGN(fct_logger_i*, logger, &(nk->logger_list))
2368     {
2369         fct_logger__on_test_suite_skip(logger, condition, name);
2370     }
2371     FCT_NLIST_FOREACH_END();
2372 }
2373 
2374 
2375 static void
fctkern__log_test_skip(fctkern_t * nk,char const * condition,char const * name)2376 fctkern__log_test_skip(fctkern_t *nk, char const *condition, char const *name)
2377 {
2378     FCT_NLIST_FOREACH_BGN(fct_logger_i*, logger, &(nk->logger_list))
2379     {
2380         fct_logger__on_test_skip(logger, condition, name);
2381     }
2382     FCT_NLIST_FOREACH_END();
2383 }
2384 
2385 
2386 /* Use this for displaying information about a "Check" (i.e.
2387 a condition). */
2388 static void
fctkern__log_chk(fctkern_t * nk,fctchk_t const * chk)2389 fctkern__log_chk(fctkern_t *nk, fctchk_t const *chk)
2390 {
2391     FCT_ASSERT( nk != NULL );
2392     FCT_ASSERT( chk != NULL );
2393     FCT_NLIST_FOREACH_BGN(fct_logger_i*, logger, &(nk->logger_list))
2394     {
2395         fct_logger__on_chk(logger, chk);
2396     }
2397     FCT_NLIST_FOREACH_END();
2398 }
2399 
2400 
2401 /* Use this for displaying warning messages. */
2402 static void
fctkern__log_warn(fctkern_t * nk,char const * warn)2403 fctkern__log_warn(fctkern_t *nk, char const *warn)
2404 {
2405     FCT_ASSERT( nk != NULL );
2406     FCT_ASSERT( warn != NULL );
2407     FCT_NLIST_FOREACH_BGN(fct_logger_i*, logger, &(nk->logger_list))
2408     {
2409         fct_logger__on_warn(logger, warn);
2410     }
2411     FCT_NLIST_FOREACH_END();
2412 }
2413 
2414 
2415 /* Called whenever a test is started. */
2416 static void
fctkern__log_test_start(fctkern_t * nk,fct_test_t const * test)2417 fctkern__log_test_start(fctkern_t *nk, fct_test_t const *test)
2418 {
2419     FCT_ASSERT( nk != NULL );
2420     FCT_ASSERT( test != NULL );
2421     FCT_NLIST_FOREACH_BGN(fct_logger_i*, logger, &(nk->logger_list))
2422     {
2423         fct_logger__on_test_start(logger, test);
2424     }
2425     FCT_NLIST_FOREACH_END();
2426 }
2427 
2428 
2429 static void
fctkern__log_test_end(fctkern_t * nk,fct_test_t * test)2430 fctkern__log_test_end(fctkern_t *nk, fct_test_t *test)
2431 {
2432     FCT_ASSERT( nk != NULL );
2433     FCT_ASSERT( test != NULL );
2434     FCT_NLIST_FOREACH_BGN(fct_logger_i*, logger, &(nk->logger_list))
2435     {
2436         fct_logger__on_test_end(logger, test);
2437     }
2438     FCT_NLIST_FOREACH_END();
2439 }
2440 
2441 
2442 #define fctkern__log_start(_NK_) \
2443    {\
2444        FCT_NLIST_FOREACH_BGN(fct_logger_i*, logger, &((_NK_)->logger_list))\
2445        {\
2446           fct_logger__on_fctx_start(logger, (_NK_));\
2447        }\
2448        FCT_NLIST_FOREACH_END();\
2449    }
2450 
2451 
2452 #define fctkern__log_end(_NK_) \
2453     {\
2454        FCT_NLIST_FOREACH_BGN(fct_logger_i*, logger, &((_NK_)->logger_list))\
2455        {\
2456           fct_logger__on_fctx_end(logger, (_NK_));\
2457        }\
2458        FCT_NLIST_FOREACH_END();\
2459     }
2460 
2461 
2462 
2463 
2464 /*
2465 -----------------------------------------------------------
2466 LOGGER INTERFACE
2467 
2468 Defines an interface to a logging system. A logger
2469 must define the following functions in order to hook
2470 into the logging system.
2471 
2472 See the "Standard Logger" and "Minimal Logger" as examples
2473 of the implementation.
2474 -----------------------------------------------------------
2475 */
2476 
2477 /* Common event argument. The values of the each event may or may not be
2478 defined depending on the event in question. */
2479 struct _fct_logger_evt_t
2480 {
2481     fctkern_t const *kern;
2482     fctchk_t const *chk;
2483     fct_test_t const *test;
2484     fct_ts_t const *ts;
2485     char const *msg;
2486     char const *cndtn;
2487     char const *name;
2488 };
2489 
2490 
2491 typedef struct _fct_logger_i_vtable_t
2492 {
2493     /* 1
2494      * Fired when an "fct_chk*" (check) function is completed. The event
2495      * will contain a reference to the "chk" object created.
2496      * */
2497     void (*on_chk)(fct_logger_i *logger, fct_logger_evt_t const *e);
2498 
2499     /* 2
2500      * Fired when a test starts and before any checks are made. The
2501      * event will have its "test" object set. */
2502     void (*on_test_start)(
2503         fct_logger_i *logger,
2504         fct_logger_evt_t const *e
2505     );
2506     /* 3 */
2507     void (*on_test_end)(
2508         fct_logger_i *logger,
2509         fct_logger_evt_t const *e
2510     );
2511     /* 4 */
2512     void (*on_test_suite_start)(
2513         fct_logger_i *logger,
2514         fct_logger_evt_t const *e
2515     );
2516     /* 5 */
2517     void (*on_test_suite_end)(
2518         fct_logger_i *logger,
2519         fct_logger_evt_t const *e
2520     );
2521     /* 6 */
2522     void (*on_fctx_start)(
2523         fct_logger_i *logger,
2524         fct_logger_evt_t const *e
2525     );
2526     /* 7 */
2527     void (*on_fctx_end)(
2528         fct_logger_i *logger,
2529         fct_logger_evt_t const *e
2530     );
2531     /* 8
2532     Called when the logger object must "clean up". */
2533     void (*on_delete)(
2534         fct_logger_i *logger,
2535         fct_logger_evt_t const *e
2536     );
2537     /* 9 */
2538     void (*on_warn)(
2539         fct_logger_i *logger,
2540         fct_logger_evt_t const *e
2541     );
2542     /* -- new in 1.2 -- */
2543     /* 10 */
2544     void (*on_test_suite_skip)(
2545         fct_logger_i *logger,
2546         fct_logger_evt_t const *e
2547     );
2548     /* 11 */
2549     void (*on_test_skip)(
2550         fct_logger_i *logger,
2551         fct_logger_evt_t const *e
2552     );
2553 } fct_logger_i_vtable_t;
2554 
2555 #define _fct_logger_head \
2556     fct_logger_i_vtable_t vtable; \
2557     fct_logger_evt_t evt
2558 
2559 struct _fct_logger_i
2560 {
2561     _fct_logger_head;
2562 };
2563 
2564 
2565 static void
fct_logger__stub(fct_logger_i * l,fct_logger_evt_t const * e)2566 fct_logger__stub(fct_logger_i *l, fct_logger_evt_t const *e)
2567 {
2568     fct_unused(l);
2569     fct_unused(e);
2570 }
2571 
2572 
2573 static fct_logger_i_vtable_t fct_logger_default_vtable =
2574 {
2575     fct_logger__stub,   /* 1.  on_chk */
2576     fct_logger__stub,   /* 2.  on_test_start */
2577     fct_logger__stub,   /* 3.  on_test_end */
2578     fct_logger__stub,   /* 4.  on_test_suite_start */
2579     fct_logger__stub,   /* 5.  on_test_suite_end */
2580     fct_logger__stub,   /* 6.  on_fctx_start */
2581     fct_logger__stub,   /* 7.  on_fctx_end */
2582     fct_logger__stub,   /* 8.  on_delete */
2583     fct_logger__stub,   /* 9.  on_warn */
2584     fct_logger__stub,   /* 10. on_test_suite_skip */
2585     fct_logger__stub,   /* 11. on_test_skip */
2586 };
2587 
2588 
2589 /* Initializes the elements of a logger interface so they are at their
2590 standard values. */
2591 static void
fct_logger__init(fct_logger_i * logger)2592 fct_logger__init(fct_logger_i *logger)
2593 {
2594     FCT_ASSERT( logger != NULL );
2595     memcpy(
2596         &(logger->vtable),
2597         &fct_logger_default_vtable,
2598         sizeof(fct_logger_i_vtable_t)
2599     );
2600     memset(&(logger->evt),0, sizeof(fct_logger_evt_t));
2601 }
2602 
2603 static void
fct_logger__del(fct_logger_i * logger)2604 fct_logger__del(fct_logger_i *logger)
2605 {
2606     if ( logger )
2607     {
2608         logger->vtable.on_delete(logger, &(logger->evt));
2609     }
2610 }
2611 
2612 
2613 static void
fct_logger__on_test_start(fct_logger_i * logger,fct_test_t const * test)2614 fct_logger__on_test_start(fct_logger_i *logger, fct_test_t const *test)
2615 {
2616     logger->evt.test = test;
2617     logger->vtable.on_test_start(logger, &(logger->evt));
2618 }
2619 
2620 
2621 static void
fct_logger__on_test_end(fct_logger_i * logger,fct_test_t * test)2622 fct_logger__on_test_end(fct_logger_i *logger, fct_test_t *test)
2623 {
2624     logger->evt.test = test;
2625     logger->vtable.on_test_end(logger, &(logger->evt));
2626 }
2627 
2628 
2629 static void
fct_logger__on_test_suite_start(fct_logger_i * logger,fct_ts_t const * ts)2630 fct_logger__on_test_suite_start(fct_logger_i *logger, fct_ts_t const *ts)
2631 {
2632     logger->evt.ts = ts;
2633     logger->vtable.on_test_suite_start(logger, &(logger->evt));
2634 }
2635 
2636 
2637 static void
fct_logger__on_test_suite_end(fct_logger_i * logger,fct_ts_t const * ts)2638 fct_logger__on_test_suite_end(fct_logger_i *logger, fct_ts_t const *ts)
2639 {
2640     logger->evt.ts = ts;
2641     logger->vtable.on_test_suite_end(logger, &(logger->evt));
2642 }
2643 
2644 
2645 static void
fct_logger__on_test_suite_skip(fct_logger_i * logger,char const * condition,char const * name)2646 fct_logger__on_test_suite_skip(
2647     fct_logger_i *logger,
2648     char const *condition,
2649     char const *name
2650 )
2651 {
2652     logger->evt.cndtn = condition;
2653     logger->evt.name = name;
2654     logger->vtable.on_test_suite_skip(logger, &(logger->evt));
2655 }
2656 
2657 
2658 static void
fct_logger__on_test_skip(fct_logger_i * logger,char const * condition,char const * name)2659 fct_logger__on_test_skip(
2660     fct_logger_i *logger,
2661     char const *condition,
2662     char const *name
2663 )
2664 {
2665     logger->evt.cndtn = condition;
2666     logger->evt.name = name;
2667     logger->vtable.on_test_skip(logger, &(logger->evt));
2668 }
2669 
2670 
2671 static void
fct_logger__on_chk(fct_logger_i * logger,fctchk_t const * chk)2672 fct_logger__on_chk(fct_logger_i *logger, fctchk_t const *chk)
2673 {
2674     logger->evt.chk = chk;
2675     logger->vtable.on_chk(logger, &(logger->evt));
2676 }
2677 
2678 /* When we start all our tests. */
2679 #define fct_logger__on_fctx_start(LOGGER, KERN) \
2680    (LOGGER)->evt.kern = (KERN);\
2681    (LOGGER)->vtable.on_fctx_start((LOGGER), &((LOGGER)->evt));
2682 
2683 
2684 /* When we have reached the end of ALL of our testing. */
2685 #define fct_logger__on_fctx_end(LOGGER, KERN) \
2686     (LOGGER)->evt.kern = (KERN);\
2687     (LOGGER)->vtable.on_fctx_end((LOGGER), &((LOGGER)->evt));
2688 
2689 
2690 static void
fct_logger__on_warn(fct_logger_i * logger,char const * msg)2691 fct_logger__on_warn(fct_logger_i *logger, char const *msg)
2692 {
2693     logger->evt.msg = msg;
2694     logger->vtable.on_warn(logger, &(logger->evt));
2695 }
2696 
2697 
2698 /* Commmon routine to record strings representing failures. The
2699 chk should be a failure before we call this, and the list is a list
2700 of char*'s that will eventually be free'd by the logger. */
2701 static void
fct_logger_record_failure(fctchk_t const * chk,fct_nlist_t * fail_list)2702 fct_logger_record_failure(fctchk_t const* chk, fct_nlist_t* fail_list)
2703 {
2704     /* For now we will truncate the string to some set amount, later
2705     we can work out a dynamic string object. */
2706     char *str = (char*)malloc(sizeof(char)*FCT_MAX_LOG_LINE);
2707     FCT_ASSERT( str != NULL );
2708     fct_snprintf(
2709         str,
2710         FCT_MAX_LOG_LINE,
2711         "%s(%d):\n    %s",
2712         fctchk__file(chk),
2713         fctchk__lineno(chk),
2714         fctchk__msg(chk)
2715     );
2716     /* Append it to the listing ... */
2717     fct_nlist__append(fail_list, (void*)str);
2718 }
2719 
2720 
2721 /* Another common routine, to print the failures at the end of a run. */
2722 static void
fct_logger_print_failures(fct_nlist_t const * fail_list)2723 fct_logger_print_failures(fct_nlist_t const *fail_list)
2724 {
2725     puts(
2726         "\n----------------------------------------------------------------------------\n"
2727     );
2728     puts("FAILED TESTS\n\n");
2729     FCT_NLIST_FOREACH_BGN(char *, cndtn_str, fail_list)
2730     {
2731         printf("%s\n", cndtn_str);
2732     }
2733     FCT_NLIST_FOREACH_END();
2734 
2735     puts("\n");
2736 }
2737 
2738 
2739 
2740 
2741 /*
2742 -----------------------------------------------------------
2743 MINIMAL LOGGER
2744 -----------------------------------------------------------
2745 
2746 At the moment the MINIMAL LOGGER is currently disabled. Hope
2747 to bring it back online soon. The only reason it is
2748 disabled is that we don't currently have the ability to specify
2749 loggers.
2750 */
2751 
2752 
2753 /* Minimal logger, reports the minimum amount of information needed
2754 to determine "something is happening". */
2755 struct _fct_minimal_logger_t
2756 {
2757     _fct_logger_head;
2758     /* A list of char*'s that needs to be cleaned up. */
2759     fct_nlist_t failed_cndtns_list;
2760 };
2761 
2762 
2763 static void
fct_minimal_logger__on_chk(fct_logger_i * self_,fct_logger_evt_t const * e)2764 fct_minimal_logger__on_chk(
2765     fct_logger_i *self_,
2766     fct_logger_evt_t const *e
2767 )
2768 {
2769     fct_minimal_logger_t *self = (fct_minimal_logger_t*)self_;
2770     if ( fctchk__is_pass(e->chk) )
2771     {
2772         fputs(".", stdout);
2773     }
2774     else
2775     {
2776         fputs("x", stdout);
2777         fct_logger_record_failure(e->chk, &(self->failed_cndtns_list));
2778 
2779     }
2780 }
2781 
2782 static void
fct_minimal_logger__on_fctx_end(fct_logger_i * self_,fct_logger_evt_t const * e)2783 fct_minimal_logger__on_fctx_end(
2784     fct_logger_i *self_,
2785     fct_logger_evt_t const *e
2786 )
2787 {
2788     fct_minimal_logger_t *self = (fct_minimal_logger_t*)self_;
2789     fct_unused(e);
2790     if ( fct_nlist__size(&(self->failed_cndtns_list)) >0 )
2791     {
2792         fct_logger_print_failures(&(self->failed_cndtns_list));
2793     }
2794 }
2795 
2796 
2797 static void
fct_minimal_logger__on_delete(fct_logger_i * self_,fct_logger_evt_t const * e)2798 fct_minimal_logger__on_delete(
2799     fct_logger_i *self_,
2800     fct_logger_evt_t const *e
2801 )
2802 {
2803     fct_minimal_logger_t *self = (fct_minimal_logger_t*)self_;
2804     fct_unused(e);
2805     fct_nlist__final(&(self->failed_cndtns_list), free);
2806     free(self);
2807 
2808 }
2809 
2810 
2811 fct_logger_i*
fct_minimal_logger_new(void)2812 fct_minimal_logger_new(void)
2813 {
2814     fct_minimal_logger_t *self = (fct_minimal_logger_t*)\
2815                                  calloc(1,sizeof(fct_minimal_logger_t));
2816     if ( self == NULL )
2817     {
2818         return NULL;
2819     }
2820     fct_logger__init((fct_logger_i*)self);
2821     self->vtable.on_chk = fct_minimal_logger__on_chk;
2822     self->vtable.on_fctx_end = fct_minimal_logger__on_fctx_end;
2823     self->vtable.on_delete = fct_minimal_logger__on_delete;
2824     fct_nlist__init2(&(self->failed_cndtns_list), 0);
2825     return (fct_logger_i*)self;
2826 }
2827 
2828 
2829 /*
2830 -----------------------------------------------------------
2831 STANDARD LOGGER
2832 -----------------------------------------------------------
2833 */
2834 
2835 struct _fct_standard_logger_t
2836 {
2837     _fct_logger_head;
2838 
2839     /* Start time. For now we use the low-accuracy time_t version. */
2840     fct_timer_t timer;
2841 
2842     /* A list of char*'s that needs to be cleaned up. */
2843     fct_nlist_t failed_cndtns_list;
2844 };
2845 
2846 
2847 #define FCT_STANDARD_LOGGER_MAX_LINE 68
2848 
2849 
2850 /* When a failure occurrs, we will record the details so we can display
2851 them when the log "finishes" up. */
2852 static void
fct_standard_logger__on_chk(fct_logger_i * logger_,fct_logger_evt_t const * e)2853 fct_standard_logger__on_chk(
2854     fct_logger_i *logger_,
2855     fct_logger_evt_t const *e
2856 )
2857 {
2858     fct_standard_logger_t *logger = (fct_standard_logger_t*)logger_;
2859     /* Only record failures. */
2860     if ( !fctchk__is_pass(e->chk) )
2861     {
2862         fct_logger_record_failure(e->chk, &(logger->failed_cndtns_list));
2863     }
2864 }
2865 
2866 
2867 static void
fct_standard_logger__on_test_skip(fct_logger_i * logger_,fct_logger_evt_t const * e)2868 fct_standard_logger__on_test_skip(
2869     fct_logger_i* logger_,
2870     fct_logger_evt_t const *e
2871 )
2872 {
2873     char const *condition = e->cndtn;
2874     char const *name = e->name;
2875     char msg[256] = {'\0'};
2876     fct_unused(logger_);
2877     fct_unused(condition);
2878     fct_snprintf(msg, sizeof(msg), "%s (%s)", name, condition);
2879     msg[sizeof(msg)-1] = '\0';
2880     fct_dotted_line_start(FCT_STANDARD_LOGGER_MAX_LINE, msg);
2881     fct_dotted_line_end("- SKIP -");
2882 }
2883 
2884 
2885 static void
fct_standard_logger__on_test_start(fct_logger_i * logger_,fct_logger_evt_t const * e)2886 fct_standard_logger__on_test_start(
2887     fct_logger_i *logger_,
2888     fct_logger_evt_t const *e
2889 )
2890 {
2891     fct_unused(logger_);
2892     fct_dotted_line_start(
2893         FCT_STANDARD_LOGGER_MAX_LINE,
2894         fct_test__name(e->test)
2895     );
2896 }
2897 
2898 
2899 static void
fct_standard_logger__on_test_end(fct_logger_i * logger_,fct_logger_evt_t const * e)2900 fct_standard_logger__on_test_end(
2901     fct_logger_i *logger_,
2902     fct_logger_evt_t const *e
2903 )
2904 {
2905     nbool_t is_pass;
2906     fct_unused(logger_);
2907     is_pass = fct_test__is_pass(e->test);
2908     fct_dotted_line_end((is_pass) ? "PASS" : "FAIL ***" );
2909 }
2910 
2911 
2912 static void
fct_standard_logger__on_fctx_start(fct_logger_i * logger_,fct_logger_evt_t const * e)2913 fct_standard_logger__on_fctx_start(
2914     fct_logger_i *logger_,
2915     fct_logger_evt_t const *e
2916 )
2917 {
2918     fct_standard_logger_t *logger = (fct_standard_logger_t*)logger_;
2919     fct_unused(e);
2920     fct_timer__start(&(logger->timer));
2921 }
2922 
2923 
2924 static void
fct_standard_logger__on_fctx_end(fct_logger_i * logger_,fct_logger_evt_t const * e)2925 fct_standard_logger__on_fctx_end(
2926     fct_logger_i *logger_,
2927     fct_logger_evt_t const *e
2928 )
2929 {
2930     fct_standard_logger_t *logger = (fct_standard_logger_t*)logger_;
2931     nbool_t is_success =1;
2932     double elasped_time =0;
2933     size_t num_tests =0;
2934     size_t num_passed =0;
2935 
2936     fct_timer__stop(&(logger->timer));
2937 
2938     is_success = fct_nlist__size(&(logger->failed_cndtns_list)) ==0;
2939 
2940     if (  !is_success )
2941     {
2942         fct_logger_print_failures(&(logger->failed_cndtns_list));
2943     }
2944     puts(
2945         "\n----------------------------------------------------------------------------\n"
2946     );
2947     num_tests = fctkern__tst_cnt(e->kern);
2948     num_passed = fctkern__tst_cnt_passed(e->kern);
2949     printf(
2950         "%s (%lu/%lu tests",
2951         (is_success) ? "PASSED" : "FAILED",
2952         (unsigned long) num_passed,
2953         (unsigned long) num_tests
2954     );
2955     elasped_time = fct_timer__duration(&(logger->timer));
2956     if ( elasped_time > 0.0000001 )
2957     {
2958         printf(" in %.6fs)\n", elasped_time);
2959     }
2960     else
2961     {
2962         /* Don't bother displaying the time to execute. */
2963         puts(")\n");
2964     }
2965 }
2966 
2967 
2968 static void
fct_standard_logger__on_delete(fct_logger_i * logger_,fct_logger_evt_t const * e)2969 fct_standard_logger__on_delete(
2970     fct_logger_i *logger_,
2971     fct_logger_evt_t const *e
2972 )
2973 {
2974     fct_standard_logger_t *logger = (fct_standard_logger_t*)logger_;
2975     fct_unused(e);
2976     fct_nlist__final(&(logger->failed_cndtns_list), free);
2977     free(logger);
2978     logger_ =NULL;
2979 }
2980 
2981 
2982 static void
fct_standard_logger__on_warn(fct_logger_i * logger_,fct_logger_evt_t const * e)2983 fct_standard_logger__on_warn(
2984     fct_logger_i* logger_,
2985     fct_logger_evt_t const *e
2986 )
2987 {
2988     fct_unused(logger_);
2989     (void)printf("WARNING: %s", e->msg);
2990 }
2991 
2992 
2993 fct_logger_i*
fct_standard_logger_new(void)2994 fct_standard_logger_new(void)
2995 {
2996     fct_standard_logger_t *logger = (fct_standard_logger_t *)calloc(
2997                                         1, sizeof(fct_standard_logger_t)
2998                                     );
2999     if ( logger == NULL )
3000     {
3001         return NULL;
3002     }
3003     fct_logger__init((fct_logger_i*)logger);
3004     logger->vtable.on_chk = fct_standard_logger__on_chk;
3005     logger->vtable.on_test_start = fct_standard_logger__on_test_start;
3006     logger->vtable.on_test_end = fct_standard_logger__on_test_end;
3007     logger->vtable.on_fctx_start = fct_standard_logger__on_fctx_start;
3008     logger->vtable.on_fctx_end = fct_standard_logger__on_fctx_end;
3009     logger->vtable.on_delete = fct_standard_logger__on_delete;
3010     logger->vtable.on_warn = fct_standard_logger__on_warn;
3011     logger->vtable.on_test_skip = fct_standard_logger__on_test_skip;
3012     fct_nlist__init2(&(logger->failed_cndtns_list), 0);
3013     fct_timer__init(&(logger->timer));
3014     return (fct_logger_i*)logger;
3015 }
3016 
3017 
3018 /*
3019 -----------------------------------------------------------
3020 JUNIT LOGGER
3021 -----------------------------------------------------------
3022 */
3023 
3024 
3025 /* JUnit logger */
3026 struct _fct_junit_logger_t
3027 {
3028     _fct_logger_head;
3029 };
3030 
3031 
3032 static void
fct_junit_logger__on_test_suite_start(fct_logger_i * l,fct_logger_evt_t const * e)3033 fct_junit_logger__on_test_suite_start(
3034     fct_logger_i *l,
3035     fct_logger_evt_t const *e
3036 )
3037 {
3038     fct_unused(l);
3039     fct_unused(e);
3040     FCT_SWITCH_STDOUT_TO_BUFFER();
3041     FCT_SWITCH_STDERR_TO_BUFFER();
3042 }
3043 
3044 
3045 static void
fct_junit_logger__on_test_suite_end(fct_logger_i * logger_,fct_logger_evt_t const * e)3046 fct_junit_logger__on_test_suite_end(
3047     fct_logger_i *logger_,
3048     fct_logger_evt_t const *e
3049 )
3050 {
3051     fct_ts_t const *ts = e->ts; /* Test Suite */
3052     nbool_t is_pass;
3053     double elasped_time = 0;
3054     char std_buffer[1024];
3055     int read_length;
3056     int first_out_line;
3057 
3058     fct_unused(logger_);
3059 
3060     elasped_time = fct_ts__duration(ts);
3061 
3062     FCT_SWITCH_STDOUT_TO_STDOUT();
3063     FCT_SWITCH_STDERR_TO_STDERR();
3064 
3065     /* opening testsuite tag */
3066     printf("\t<testsuite errors=\"%lu\" failures=\"0\" tests=\"%lu\" "
3067            "name=\"%s\" time=\"%.4f\">\n",
3068            (unsigned long)   fct_ts__tst_cnt(ts)
3069            - fct_ts__tst_cnt_passed(ts),
3070            (unsigned long) fct_ts__tst_cnt(ts),
3071            fct_ts__name(ts),
3072            elasped_time);
3073 
3074     FCT_NLIST_FOREACH_BGN(fct_test_t*, test, &(ts->test_list))
3075     {
3076         is_pass = fct_test__is_pass(test);
3077 
3078         /* opening testcase tag */
3079         if (is_pass)
3080         {
3081             printf("\t\t<testcase name=\"%s\" time=\"%.3f\"",
3082                    fct_test__name(test),
3083                    fct_test__duration(test)
3084                   );
3085         }
3086         else
3087         {
3088             printf("\t\t<testcase name=\"%s\" time=\"%.3f\">\n",
3089                    fct_test__name(test),
3090                    fct_test__duration(test)
3091                   );
3092         }
3093 
3094         FCT_NLIST_FOREACH_BGN(fctchk_t*, chk, &(test->failed_chks))
3095         {
3096             /* error tag */
3097             printf("\t\t\t<error message=\"%s\" "
3098                    "type=\"fctx\">", chk->msg);
3099             printf("file:%s, line:%d", chk->file, chk->lineno);
3100             printf("</error>\n");
3101         }
3102         FCT_NLIST_FOREACH_END();
3103 
3104         /* closing testcase tag */
3105         if (is_pass)
3106         {
3107             printf(" />\n");
3108         }
3109         else
3110         {
3111             printf("\t\t</testcase>\n");
3112         }
3113     }
3114     FCT_NLIST_FOREACH_END();
3115 
3116     /* print the std streams */
3117     first_out_line = 1;
3118     printf("\t\t<system-out>\n\t\t\t<![CDATA[");
3119     while ( (read_length = _fct_read(fct_stdout_pipe[0], std_buffer, 1024)) > 0)
3120     {
3121         if (first_out_line)
3122         {
3123             printf("\n");
3124             first_out_line = 0;
3125         }
3126         printf("%.*s", read_length, std_buffer);
3127     }
3128     printf("]]>\n\t\t</system-out>\n");
3129 
3130     first_out_line = 1;
3131     printf("\t\t<system-err>\n\t\t\t<![CDATA[");
3132     while ((read_length = _fct_read(fct_stderr_pipe[0], std_buffer, 1024)) > 0)
3133     {
3134         if (first_out_line)
3135         {
3136             printf("\n");
3137             first_out_line = 0;
3138         }
3139         printf("%.*s", read_length, std_buffer);
3140     }
3141     printf("]]>\n\t\t</system-err>\n");
3142 
3143     /* closing testsuite tag */
3144     printf("\t</testsuite>\n");
3145 }
3146 
3147 static void
fct_junit_logger__on_fct_start(fct_logger_i * logger_,fct_logger_evt_t const * e)3148 fct_junit_logger__on_fct_start(
3149     fct_logger_i *logger_,
3150     fct_logger_evt_t const *e
3151 )
3152 {
3153     fct_unused(logger_);
3154     fct_unused(e);
3155     printf("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
3156     printf("<testsuites>\n");
3157 }
3158 
3159 static void
fct_junit_logger__on_fctx_end(fct_logger_i * logger_,fct_logger_evt_t const * e)3160 fct_junit_logger__on_fctx_end(
3161     fct_logger_i *logger_,
3162     fct_logger_evt_t const *e
3163 )
3164 {
3165     fct_unused(logger_);
3166     fct_unused(e);
3167     printf("</testsuites>\n");
3168 }
3169 
3170 static void
fct_junit_logger__on_delete(fct_logger_i * logger_,fct_logger_evt_t const * e)3171 fct_junit_logger__on_delete(
3172     fct_logger_i *logger_,
3173     fct_logger_evt_t const *e
3174 )
3175 {
3176     fct_junit_logger_t *logger = (fct_junit_logger_t*)logger_;
3177     fct_unused(e);
3178     free(logger);
3179     logger_ =NULL;
3180 }
3181 
3182 
3183 fct_junit_logger_t *
fct_junit_logger_new(void)3184 fct_junit_logger_new(void)
3185 {
3186     fct_junit_logger_t *logger =
3187         (fct_junit_logger_t *)calloc(1, sizeof(fct_junit_logger_t));
3188     if ( logger == NULL )
3189     {
3190         return NULL;
3191     }
3192     fct_logger__init((fct_logger_i*)logger);
3193     logger->vtable.on_test_suite_start = fct_junit_logger__on_test_suite_start;
3194     logger->vtable.on_test_suite_end = fct_junit_logger__on_test_suite_end;
3195     logger->vtable.on_fctx_start = fct_junit_logger__on_fct_start;
3196     logger->vtable.on_fctx_end = fct_junit_logger__on_fctx_end;
3197     logger->vtable.on_delete = fct_junit_logger__on_delete;
3198     return logger;
3199 }
3200 
3201 
3202 /*
3203 ------------------------------------------------------------
3204 MACRO MAGIC
3205 ------------------------------------------------------------
3206 This is where the show begins!
3207 */
3208 
3209 /* This macro invokes a bunch of functions that need to be referenced in
3210 order to avoid  a "unreferenced local function has been removed" warning.
3211 The logical acrobatics below try and make it appear to the compiler that
3212 they are needed, but at runtime, only the cheap, first call is made. */
3213 #define FCT_REFERENCE_FUNCS() \
3214     {\
3215         int check = 0 && fctstr_ieq(NULL, NULL);\
3216         if ( check ) {\
3217             _fct_cmt("not to be executed");\
3218             (void)_fct_chk_empty_str(NULL);\
3219             (void)_fct_chk_full_str(NULL);\
3220             (void)fct_test__start_timer(NULL);\
3221             (void)fct_test__stop_timer(NULL);\
3222             (void)fct_ts_new(NULL);\
3223             (void)fct_ts__test_begin(NULL);\
3224             (void)fct_ts__add_test(NULL, NULL);\
3225             (void)fct_ts__test_end(NULL);\
3226             (void)fct_ts__inc_total_test_num(NULL);\
3227             (void)fct_ts__make_abort_test(NULL);\
3228             (void)fct_ts__setup_abort(NULL);\
3229             (void)fct_ts__setup_end(NULL);\
3230             (void)fct_ts__teardown_end(NULL);\
3231             (void)fct_ts__cnt_end(NULL);\
3232             (void)fct_ts__is_test_cnt(NULL, 0);\
3233             (void)fct_xchk_fn(0, "");\
3234             (void)fct_xchk2_fn(NULL, 0, "");\
3235             (void)fctkern__cl_parse(NULL);\
3236             (void)fctkern__add_ts(NULL, NULL);\
3237             (void)fctkern__pass_filter(NULL, NULL);\
3238             (void)fctkern__log_suite_start(NULL, NULL);\
3239             (void)fctkern__log_suite_end(NULL, NULL);\
3240             (void)fctkern__log_test_skip(NULL, NULL, NULL);\
3241             (void)fctkern__log_test_start(NULL, NULL);\
3242             (void)fctkern__log_test_end(NULL, NULL);\
3243             (void)fctstr_endswith(NULL,NULL);\
3244             (void)fctstr_iendswith(NULL,NULL);\
3245             (void)fctstr_ieq(NULL,NULL);\
3246             (void)fctstr_incl(NULL, NULL);\
3247             (void)fctstr_iincl(NULL, NULL);\
3248             (void)fctstr_iendswith(NULL,NULL);\
3249             (void)fctstr_istartswith(NULL,NULL);\
3250             (void)fctstr_clone_lower(NULL);\
3251             (void)fctstr_startswith(NULL,NULL);\
3252             (void)fctkern__init(NULL, 0, NULL);\
3253             (void)fctkern__cl_is(NULL, "");\
3254             (void)fctkern__cl_val2(NULL, NULL, NULL);\
3255             fctkern__log_suite_skip(NULL, NULL, NULL);\
3256             (void)fct_clp__is_param(NULL,NULL);\
3257 	    _fct_cmt("should never construct an object");\
3258             (void)fct_test_new(NULL);\
3259             (void)fct_ts__chk_cnt(NULL);\
3260         }\
3261     }
3262 
3263 
3264 #define FCT_INIT(_ARGC_, _ARGV_)                                    \
3265    fctkern_t  fctkern__;                                            \
3266    fctkern_t* fctkern_ptr__ = &fctkern__;                           \
3267    FCT_REFERENCE_FUNCS();                                           \
3268    if ( !fctkern__init(fctkern_ptr__, argc, (const char **)argv) ) {\
3269         (void)fprintf(                                              \
3270             stderr, "FATAL ERROR: Unable to initialize FCTX Kernel."\
3271         );                                                          \
3272         exit(EXIT_FAILURE);                                         \
3273    }                                                                \
3274 
3275 
3276 #define FCT_FINAL()                                                \
3277    fctkern_ptr__->ns.num_total_failed = fctkern__tst_cnt_failed(   \
3278             (fctkern_ptr__)                                        \
3279            );                                                      \
3280    fctkern__log_end(fctkern_ptr__);                                \
3281    fctkern__end(fctkern_ptr__);                                    \
3282    fctkern__final(fctkern_ptr__);                                  \
3283    FCT_ASSERT( !((int)fctkern_ptr__->ns.num_total_failed < 0)      \
3284                && "or we got truncated!");                         \
3285    if ( fctkern_ptr__->ns.num_total_failed ==                      \
3286         fctkern_ptr__->num_expected_failures) {                    \
3287        fctkern_ptr__->ns.num_total_failed = 0;                     \
3288    }                                                               \
3289 
3290 
3291 
3292 #define FCT_NUM_FAILED()       \
3293     fctkern_ptr__->ns.num_total_failed \
3294 
3295 
3296 
3297 /* Typically used internally only, this mentions to FCTX that you EXPECT
3298 to _NUM_FAILS_. If you the expected matches the actual, a 0 value is returned
3299 from the program. */
3300 #define FCT_EXPECTED_FAILURES(_NUM_FAILS_) \
3301     ((fctkern_ptr__->num_expected_failures = (_NUM_FAILS_)))
3302 
3303 
3304 #define FCT_BGN_FN(_FNNAME_)            \
3305     int _FNNAME_(int argc, char* argv[])\
3306     {                                   \
3307         FCT_INIT(argc, argv)
3308 
3309 #define FCT_END_FN() FCT_END()
3310 
3311 /* This defines our start. The fctkern__ is a kernal object
3312 that lives throughout the lifetime of our program. The
3313 fctkern_ptr__ makes it easier to abstract out macros.  */
3314 #define FCT_BGN() FCT_BGN_FN(main)
3315 
3316 
3317 /* Silence Intel complaints about unspecified operand order in user's code */
3318 #ifndef __INTEL_COMPILER
3319 # define FCT_END_WARNINGFIX_BGN
3320 # define FCT_END_WARNINGFIX_END
3321 #else
3322 # define FCT_END_WARNINGFIX_BGN _Pragma("warning(push,disable:981)");
3323 # define FCT_END_WARNINGFIX_END _Pragma("warning(pop)");
3324 #endif
3325 
3326 /* Ends the test suite by returning the number failed. The "chk_cnt" call is
3327 made in order allow strict compilers to pass when it encounters unreferenced
3328 functions. */
3329 #define FCT_END()             \
3330    {                          \
3331       FCT_END_WARNINGFIX_BGN  \
3332       FCT_FINAL();            \
3333       return FCT_NUM_FAILED();\
3334       FCT_END_WARNINGFIX_END  \
3335    }\
3336 }
3337 
3338 #define fctlog_install(_CUST_LOGGER_LIST_) \
3339     fctkern_ptr__->lt_usr = (_CUST_LOGGER_LIST_)
3340 
3341 /* Re-parses the command line options with the addition of user defined
3342 options. */
3343 #define fctcl_install(_CLO_INIT_) \
3344     {\
3345         fctkern_ptr__->cl_user_opts = (_CLO_INIT_);\
3346         _fct_cmt("Delay parse in order to allow for user customization.");\
3347         if ( !fctkern__cl_is_parsed((fctkern_ptr__)) ) {\
3348               int status = fctkern__cl_parse((fctkern_ptr__));\
3349               _fct_cmt("Need to parse command line before we start logger.");\
3350               fctkern__log_start((fctkern_ptr__));\
3351               switch( status ) {\
3352               case -1:\
3353               case 0:\
3354                   fctkern__final(fctkern_ptr__);\
3355                   exit( (status == 0) ? (EXIT_FAILURE) : (EXIT_SUCCESS) );\
3356                   break;\
3357               default:\
3358                   fct_pass();\
3359               }\
3360           }\
3361     }
3362 
3363 
3364 #define fctcl_is(_OPT_STR_) (fctkern__cl_is(fctkern_ptr__, (_OPT_STR_)))
3365 
3366 #define fctcl_val(_OPT_STR_) (fctcl_val2((_OPT_STR_), NULL))
3367 
3368 #define fctcl_val2(_OPT_STR_, _DEF_STR_) \
3369    (fctkern__cl_val2(fctkern_ptr__, (_OPT_STR_), (_DEF_STR_)))
3370 
3371 
3372 /* We delay the first parse of the command line until we get the first
3373 test fixture. This allows the user to possibly add their own parse
3374 specification. */
3375 #define FCT_FIXTURE_SUITE_BGN(_NAME_) \
3376    {\
3377       fctkern_ptr__->ns.ts_curr = fct_ts_new( #_NAME_ );\
3378       _fct_cmt("Delay parse in order to allow for user customization.");\
3379       if ( !fctkern__cl_is_parsed((fctkern_ptr__)) ) {\
3380           int status = fctkern__cl_parse((fctkern_ptr__));\
3381           _fct_cmt("Need to parse command line before we start logger.");\
3382           fctkern__log_start((fctkern_ptr__));\
3383           switch( status ) {\
3384           case -1:\
3385           case 0:\
3386               fct_ts__del((fctkern_ptr__->ns.ts_curr));\
3387               fctkern__final(fctkern_ptr__);\
3388               exit( (status == 0) ? (EXIT_FAILURE) : (EXIT_SUCCESS) );\
3389               break;\
3390           default:\
3391               fct_pass();\
3392           }\
3393       }\
3394       if ( fctkern_ptr__->ns.ts_curr == NULL ) {\
3395          fctkern__log_warn((fctkern_ptr__), "out of memory");\
3396       }\
3397       else\
3398       {\
3399          fctkern__log_suite_start((fctkern_ptr__), fctkern_ptr__->ns.ts_curr);\
3400          for (;;)\
3401          {\
3402              fctkern_ptr__->ns.test_num = -1;\
3403              if ( fct_ts__is_ending_mode(fctkern_ptr__->ns.ts_curr) \
3404                   || fct_ts__is_abort_mode(fctkern_ptr__->ns.ts_curr) )\
3405              {\
3406                _fct_cmt("flag the test suite as complete.");\
3407                fct_ts__end(fctkern_ptr__->ns.ts_curr);\
3408                break;\
3409              }
3410 
3411 
3412 
3413 /*  Closes off a "Fixture" test suite. */
3414 #define FCT_FIXTURE_SUITE_END() \
3415              if ( fct_ts__is_cnt_mode(fctkern_ptr__->ns.ts_curr) )\
3416              {\
3417                 fct_ts__cnt_end(fctkern_ptr__->ns.ts_curr);\
3418              }\
3419           }\
3420           fctkern__add_ts((fctkern_ptr__), fctkern_ptr__->ns.ts_curr);\
3421           fctkern__log_suite_end((fctkern_ptr__), fctkern_ptr__->ns.ts_curr);\
3422           fct_ts__end(fctkern_ptr__->ns.ts_curr);\
3423           fctkern_ptr__->ns.ts_curr = NULL;\
3424           }\
3425       }
3426 
3427 #define FCT_FIXTURE_SUITE_BGN_IF(_CONDITION_, _NAME_) \
3428     fctkern_ptr__->ns.ts_is_skip_suite = !(_CONDITION_);\
3429     fctkern_ptr__->ns.ts_skip_cndtn = #_CONDITION_;\
3430     if ( fctkern_ptr__->ns.ts_is_skip_suite ) {\
3431        fctkern__log_suite_skip((fctkern_ptr__), #_CONDITION_, #_NAME_);\
3432     }\
3433     FCT_FIXTURE_SUITE_BGN(_NAME_);
3434 
3435 #define FCT_FIXTURE_SUITE_END_IF() \
3436     FCT_FIXTURE_SUITE_END();\
3437     fctkern_ptr__->ns.ts_is_skip_suite =0;\
3438     fctkern_ptr__->ns.ts_skip_cndtn =NULL;\
3439 
3440 #define FCT_SETUP_BGN()\
3441    if ( fct_ts__is_setup_mode(fctkern_ptr__->ns.ts_curr) ) {
3442 
3443 #define FCT_SETUP_END() \
3444    fct_ts__setup_end(fctkern_ptr__->ns.ts_curr); }
3445 
3446 #define FCT_TEARDOWN_BGN() \
3447    if ( fct_ts__is_teardown_mode(fctkern_ptr__->ns.ts_curr) ) {\
3448 
3449 #define FCT_TEARDOWN_END() \
3450    fct_ts__teardown_end(fctkern_ptr__->ns.ts_curr); \
3451    continue; \
3452    }
3453 
3454 /* Lets you create a test suite, where maybe you don't want a fixture. We
3455 do it by 'stubbing' out the setup/teardown logic. */
3456 #define FCT_SUITE_BGN(Name) \
3457    FCT_FIXTURE_SUITE_BGN(Name) {\
3458    FCT_SETUP_BGN() {_fct_cmt("stubbed"); } FCT_SETUP_END()\
3459    FCT_TEARDOWN_BGN() {_fct_cmt("stubbed");} FCT_TEARDOWN_END()\
3460 
3461 #define FCT_SUITE_END() } FCT_FIXTURE_SUITE_END()
3462 
3463 #define FCT_SUITE_BGN_IF(_CONDITION_, _NAME_) \
3464     FCT_FIXTURE_SUITE_BGN_IF(_CONDITION_, (_NAME_)) {\
3465     FCT_SETUP_BGN() {_fct_cmt("stubbed"); } FCT_SETUP_END()\
3466     FCT_TEARDOWN_BGN() {_fct_cmt("stubbed");} FCT_TEARDOWN_END()\
3467 
3468 #define FCT_SUITE_END_IF() } FCT_FIXTURE_SUITE_END_IF()
3469 
3470 typedef enum
3471 {
3472     FCT_TEST_END_FLAG_Default    = 0x0000,
3473     FCT_TEST_END_FLAG_ClearFail  = 0x0001
3474 } FCT_TEST_END_FLAG;
3475 
3476 
3477 #define FCT_TEST_BGN_IF(_CONDITION_, _NAME_) { \
3478     fctkern_ptr__->ns.test_is_skip = !(_CONDITION_);\
3479     fctkern_ptr__->ns.test_skip_cndtn = #_CONDITION_;\
3480     FCT_TEST_BGN(_NAME_) {\
3481 
3482 #define FCT_TEST_END_IF() \
3483     } FCT_TEST_END();\
3484     fctkern_ptr__->ns.test_is_skip = 0;\
3485     fctkern_ptr__->ns.test_skip_cndtn = NULL;\
3486     }
3487 
3488 
3489 /* Depending on whether or not we are counting the tests, we will have to
3490 first determine if the test is the "current" count. Then we have to determine
3491 if we can pass the filter. Finally we will execute everything so that when a
3492 check fails, we can "break" out to the end of the test. And in between all
3493 that we do a memory check and fail a test if we can't build a fct_test
3494 object (should be rare). */
3495 #define FCT_TEST_BGN(_NAME_) \
3496          {\
3497             fctkern_ptr__->ns.curr_test_name = #_NAME_;\
3498             ++(fctkern_ptr__->ns.test_num);\
3499             if ( fct_ts__is_cnt_mode(fctkern_ptr__->ns.ts_curr) )\
3500             {\
3501                fct_ts__inc_total_test_num(fctkern_ptr__->ns.ts_curr);\
3502             }\
3503             else if ( fct_ts__is_test_mode(fctkern_ptr__->ns.ts_curr) \
3504                       && fct_ts__is_test_cnt(fctkern_ptr__->ns.ts_curr, fctkern_ptr__->ns.test_num) )\
3505             {\
3506                fct_ts__test_begin(fctkern_ptr__->ns.ts_curr);\
3507                if ( fctkern__pass_filter(fctkern_ptr__,  fctkern_ptr__->ns.curr_test_name ) )\
3508                {\
3509                   fctkern_ptr__->ns.curr_test = fct_test_new( fctkern_ptr__->ns.curr_test_name );\
3510                   if ( fctkern_ptr__->ns.curr_test  == NULL ) {\
3511                     fctkern__log_warn(fctkern_ptr__, "out of memory");\
3512                   } else if ( fctkern_ptr__->ns.ts_is_skip_suite \
3513                               || fctkern_ptr__->ns.test_is_skip ) {\
3514                        fct_ts__test_begin(fctkern_ptr__->ns.ts_curr);\
3515                        fctkern__log_test_skip(\
3516                             fctkern_ptr__,\
3517                             fctkern_ptr__->ns.curr_test_name,\
3518                             (fctkern_ptr__->ns.test_is_skip) ?\
3519                                 (fctkern_ptr__->ns.test_skip_cndtn) :\
3520                                 (fctkern_ptr__->ns.ts_skip_cndtn)\
3521                        );\
3522                        fct_ts__test_end(fctkern_ptr__->ns.ts_curr);\
3523                        continue;\
3524                  } else {\
3525                       fctkern__log_test_start(fctkern_ptr__, fctkern_ptr__->ns.curr_test);\
3526                       fct_test__start_timer(fctkern_ptr__->ns.curr_test);\
3527                       for (;;) \
3528                       {
3529 
3530 
3531 
3532 
3533 #define FCT_TEST_END() \
3534                          break;\
3535                       }\
3536                       fct_test__stop_timer(fctkern_ptr__->ns.curr_test);\
3537                  }\
3538                  fct_ts__add_test(fctkern_ptr__->ns.ts_curr, fctkern_ptr__->ns.curr_test);\
3539                  fctkern__log_test_end(fctkern_ptr__, fctkern_ptr__->ns.curr_test);\
3540                }\
3541                fct_ts__test_end(fctkern_ptr__->ns.ts_curr);\
3542                continue;\
3543             }\
3544          }\
3545 
3546 
3547 
3548 /*
3549 ---------------------------------------------------------
3550 CHECKING MACROS
3551 ----------------------------------------------------------
3552 
3553 The chk variants will continue on while the req variants will abort
3554 a test if a chk condition fails. The req variants are useful when you
3555 no longer want to keep checking conditions because a critical condition
3556 is not being met. */
3557 
3558 
3559 /* To support older compilers that do not have macro variable argument lists
3560 we have to use a function. The macro manages to store away the line/file
3561 location into a global before it runs this function, a trick I picked up from
3562 the error handling in the APR library. The unfortunate thing is that we can
3563 not carry forth the actual test through a "stringize" operation, but if you
3564 wanted to do that you should use fct_chk. */
3565 
3566 static int fct_xchk_lineno =0;
3567 static char const *fct_xchk_file = NULL;
3568 static fct_test_t *fct_xchk_test = NULL;
3569 static fctkern_t *fct_xchk_kern =NULL;
3570 
3571 
3572 static int
_fct_xchk_fn_varg(char const * condition,int is_pass,char const * format,va_list args)3573 _fct_xchk_fn_varg(
3574     char const *condition,
3575     int is_pass,
3576     char const *format,
3577     va_list args
3578 )
3579 {
3580     fctchk_t *chk =NULL;
3581     chk = fctchk_new(
3582               is_pass,
3583               condition,
3584               fct_xchk_file,
3585               fct_xchk_lineno,
3586               format,
3587               args
3588           );
3589     if ( chk == NULL )
3590     {
3591         fctkern__log_warn(fct_xchk_kern, "out of memory (aborting test)");
3592         goto finally;
3593     }
3594 
3595     fct_test__add(fct_xchk_test, chk);
3596     fctkern__log_chk(fct_xchk_kern, chk);
3597 finally:
3598     fct_xchk_lineno =0;
3599     fct_xchk_file =NULL;
3600     fct_xchk_test =NULL;
3601     fct_xchk_kern =NULL;
3602     return is_pass;
3603 }
3604 
3605 
3606 static int
fct_xchk2_fn(const char * condition,int is_pass,char const * format,...)3607 fct_xchk2_fn(const char *condition, int is_pass, char const *format, ...)
3608 {
3609     int r =0;
3610     va_list args;
3611     va_start(args, format);
3612     r = _fct_xchk_fn_varg(condition, is_pass, format, args);
3613     va_end(args);
3614     return r;
3615 }
3616 
3617 
3618 static int
fct_xchk_fn(int is_pass,char const * format,...)3619 fct_xchk_fn(int is_pass, char const *format, ...)
3620 {
3621     int r=0;
3622     va_list args;
3623     va_start(args, format);
3624     r = _fct_xchk_fn_varg("<none-from-xchk>", is_pass, format, args);
3625     va_end(args);
3626     return r;
3627 }
3628 
3629 
3630 /* Call this with the following argument list:
3631 
3632    fct_xchk(test_condition, format_str, ...)
3633 
3634 the bulk of this macro presets some globals to allow us to support
3635 variable argument lists on older compilers. The idea came from the APR
3636 libraries error checking routines. */
3637 #define fct_xchk  fct_xchk_kern = fctkern_ptr__,\
3638                   fct_xchk_test = fctkern_ptr__->ns.curr_test,\
3639                   fct_xchk_lineno =__LINE__,\
3640                   fct_xchk_file=__FILE__,\
3641                   fct_xchk_fn
3642 
3643 #define fct_xchk2  fct_xchk_kern = fctkern_ptr__,\
3644                    fct_xchk_test = fctkern_ptr__->ns.curr_test,\
3645                    fct_xchk_lineno =__LINE__,\
3646                    fct_xchk_file=__FILE__,\
3647                    fct_xchk2_fn
3648 
3649 
3650 /* This checks the condition and reports the condition as a string
3651 if it fails. */
3652 #define fct_chk(_CNDTN_)  (fct_xchk((_CNDTN_) ? 1 : 0, #_CNDTN_))
3653 
3654 #define _fct_req(_CNDTN_)  \
3655     if ( !(fct_xchk((_CNDTN_) ? 1 : 0, #_CNDTN_)) ) { break; }
3656 
3657 
3658 /* When in test mode, construct a mock test object for fct_xchk to operate
3659 with. If we fail a setup up, then we go directly to a teardown mode. */
3660 #define fct_req(_CNDTN_) 				                 \
3661     if ( fct_ts__is_test_mode(fctkern_ptr__->ns.ts_curr) ) {             \
3662        _fct_req((_CNDTN_));                                              \
3663     }                                                                    \
3664     else if ( fct_ts__is_setup_mode(fctkern_ptr__->ns.ts_curr)           \
3665               || fct_ts__is_teardown_mode(fctkern_ptr__->ns.ts_curr) ) { \
3666        fctkern_ptr__->ns.curr_test = fct_ts__make_abort_test(            \
3667             fctkern_ptr__->ns.ts_curr                                    \
3668             );                                                           \
3669        if ( !(fct_xchk((_CNDTN_) ? 1 : 0, #_CNDTN_)) ) {                 \
3670            fct_ts__setup_abort(fctkern_ptr__->ns.ts_curr);               \
3671            fct_ts__add_test(                                             \
3672                 fctkern_ptr__->ns.ts_curr, fctkern_ptr__->ns.curr_test   \
3673                 );                                                       \
3674        }                                                                 \
3675     } else {                                                             \
3676        assert("invalid condition for fct_req!");                         \
3677        _fct_req((_CNDTN_));                                              \
3678     }
3679 
3680 
3681 #define fct_chk_eq_dbl(V1, V2) \
3682     fct_xchk(\
3683         ((int)(fabs((V1)-(V2)) < DBL_EPSILON)),\
3684         "chk_eq_dbl: %f != %f",\
3685         (V1),\
3686         (V2)\
3687         )
3688 
3689 
3690 #define fct_chk_neq_dbl(V1, V2) \
3691     fct_xchk(\
3692         ((int)(fabs((V1)-(V2)) >= DBL_EPSILON)),\
3693         "chk_neq_dbl: %f == %f",\
3694         (V1),\
3695         (V2)\
3696         )
3697 
3698 
3699 #define fct_chk_eq_str(V1, V2) \
3700     fct_xchk(fctstr_eq((V1), (V2)),\
3701           "chk_eq_str: '%s' != '%s'",\
3702           (V1),\
3703           (V2)\
3704           )
3705 
3706 
3707 #define fct_chk_neq_str(V1, V2) \
3708     fct_xchk(!fctstr_eq((V1), (V2)),\
3709           "chk_neq_str: '%s' == '%s'",\
3710           (V1),\
3711           (V2)\
3712           )
3713 
3714 /* To quiet warnings with GCC, who think we are being silly and passing
3715 in NULL to strlen, we will filter the predicate through these little
3716 functions */
3717 static int
_fct_chk_empty_str(char const * s)3718 _fct_chk_empty_str(char const *s)
3719 {
3720     if ( s == NULL )
3721     {
3722         return 1;
3723     }
3724     return strlen(s) ==0;
3725 }
3726 static int
_fct_chk_full_str(char const * s)3727 _fct_chk_full_str(char const *s)
3728 {
3729     if ( s == NULL )
3730     {
3731         return 0;
3732     }
3733     return strlen(s) >0;
3734 }
3735 
3736 
3737 #define fct_chk_empty_str(V) \
3738     fct_xchk(_fct_chk_empty_str((V)),\
3739              "string not empty: '%s'",\
3740              (V)\
3741              )
3742 
3743 #define fct_chk_full_str(V) \
3744     fct_xchk(_fct_chk_full_str((V)),\
3745              "string is full: '%s'",\
3746              (V)\
3747              )
3748 
3749 
3750 #define fct_chk_eq_istr(V1, V2) \
3751     fct_xchk(fctstr_ieq((V1), (V2)),\
3752           "chk_eq_str: '%s' != '%s'",\
3753           (V1),\
3754           (V2)\
3755           )
3756 
3757 
3758 #define fct_chk_neq_istr(V1, V2) \
3759     fct_xchk(!fctstr_ieq((V1), (V2)),\
3760           "chk_neq_str: '%s' == '%s'",\
3761           (V1),\
3762           (V2)\
3763           )
3764 
3765 
3766 #define fct_chk_endswith_str(STR, CHECK)\
3767     fct_xchk(fctstr_endswith((STR),(CHECK)),\
3768             "fct_chk_endswith_str: '%s' doesn't end with '%s'",\
3769             (STR),\
3770             (CHECK)\
3771             )
3772 
3773 
3774 #define fct_chk_iendswith_str(STR, CHECK)\
3775     fct_xchk(fctstr_iendswith((STR), (CHECK)),\
3776              "fch_chk_iendswith_str: '%s' doesn't end with '%s'.",\
3777              (STR),\
3778              (CHECK)\
3779              )
3780 
3781 #define fct_chk_excl_str(STR, CHECK_EXCLUDE) \
3782     fct_xchk(!fctstr_incl((STR), (CHECK_EXCLUDE)),\
3783 	  "fct_chk_excl_str: '%s' is included in '%s'",\
3784 	  (STR),\
3785           (CHECK_EXCLUDE)\
3786 	  )
3787 
3788 #define fct_chk_excl_istr(ISTR, ICHECK_EXCLUDE) \
3789     fct_xchk(!fctstr_iincl((ISTR), (ICHECK_EXCLUDE)),\
3790 	  "fct_chk_excl_istr (case insensitive): '%s' is "\
3791           "included in'%s'",\
3792           (ISTR),\
3793           (ICHECK_EXCLUDE)\
3794           )
3795 
3796 #define fct_chk_incl_str(STR, CHECK_INCLUDE) \
3797     fct_xchk(fctstr_incl((STR), (CHECK_INCLUDE)),\
3798           "fct_chk_incl_str: '%s' does not include '%s'",\
3799 	      (STR),\
3800           (CHECK_INCLUDE)\
3801 	  )
3802 
3803 
3804 #define fct_chk_incl_istr(ISTR, ICHECK_INCLUDE) \
3805     fct_xchk(fctstr_iincl((ISTR), (ICHECK_INCLUDE)),\
3806           "fct_chk_incl_istr (case insensitive): '%s' does "\
3807           "not include '%s'",\
3808 	      (ISTR),\
3809           (ICHECK_INCLUDE)\
3810 	  )
3811 
3812 
3813 #define fct_chk_startswith_str(STR, CHECK)\
3814     fct_xchk(fctstr_startswith((STR), (CHECK)),\
3815           "'%s' does not start with '%s'",\
3816           (STR),\
3817           (CHECK)\
3818     )
3819 
3820 
3821 #define fct_chk_startswith_istr(STR, CHECK)\
3822     fct_xchk(fctstr_istartswith((STR), (CHECK)),\
3823           "case insensitive check: '%s' does not start with '%s'",\
3824           (STR),\
3825           (CHECK)\
3826     )
3827 
3828 #define fct_chk_eq_int(V1, V2) \
3829     fct_xchk(\
3830         ((V1) == (V2)),\
3831         "chq_eq_int: %d != %d",\
3832         (V1),\
3833         (V2)\
3834         )
3835 
3836 
3837 #define fct_chk_neq_int(V1, V2) \
3838     fct_xchk(\
3839         ((V1) != (V2)),\
3840         "chq_neq_int: %d == %d",\
3841         (V1),\
3842         (V2)\
3843         )
3844 
3845 #define fct_chk_ex(EXCEPTION, CODE)   \
3846    {                                  \
3847       bool pass_chk_ex = false;       \
3848       try {                           \
3849           CODE;                       \
3850           pass_chk_ex = false;        \
3851       } catch ( EXCEPTION ) {         \
3852           pass_chk_ex = true;         \
3853       } catch ( ... ) {               \
3854           pass_chk_ex = false;        \
3855       }                               \
3856       fct_xchk(                       \
3857 	pass_chk_ex,                  \
3858         "%s exception not generated", \
3859         #EXCEPTION                    \
3860       );                              \
3861    }                                  \
3862 
3863 /*
3864 ---------------------------------------------------------
3865 GUT CHECK MACROS
3866 ----------------------------------------------------------
3867 
3868 The following macros are used to help check the "guts" of
3869 the FCT, and to confirm that it all works according to spec.
3870 */
3871 
3872 /* Generates a message to STDERR and exits the application with a
3873 non-zero number. */
3874 #define _FCT_GUTCHK(_CNDTN_) \
3875    if ( !(_CNDTN_) ) {\
3876       fprintf(stderr, "gutchk fail: '"  #_CNDTN_ "' was not true.\n");\
3877       exit(1);\
3878    }\
3879    else {\
3880       fprintf(stdout, "gutchk pass:  '" #_CNDTN_ "'\n");\
3881    }
3882 
3883 /*
3884 ---------------------------------------------------------
3885 MULTI-FILE TEST SUITE MACROS
3886 ----------------------------------------------------------
3887 
3888 I struggled trying to figure this out in a way that was
3889 as simple as possible. I wanted to be able to define
3890 the test suite in one object file, then refer it within
3891 the other one within the minimum amount of typing.
3892 
3893 Unfortunately without resorting to some supermacro
3894 work, I could only find a happy comprimise.
3895 
3896 See test_multi.c for an example.
3897 */
3898 
3899 /* The following macros are used in your separate object
3900 file to define your test suite.  */
3901 
3902 
3903 #define FCTMF_FIXTURE_SUITE_BGN(NAME) \
3904 	void NAME (fctkern_t *fctkern_ptr__) {\
3905         FCT_REFERENCE_FUNCS();\
3906         FCT_FIXTURE_SUITE_BGN( NAME ) {
3907 
3908 #define FCTMF_FIXTURE_SUITE_END() \
3909 		} FCT_FIXTURE_SUITE_END();\
3910 	}
3911 
3912 #define FCTMF_SUITE_BGN(NAME) \
3913 	void NAME (fctkern_t *fctkern_ptr__) {\
3914         FCT_REFERENCE_FUNCS();\
3915         FCT_SUITE_BGN( NAME ) {
3916 #define FCTMF_SUITE_END() \
3917        } FCT_SUITE_END(); \
3918    }
3919 
3920 
3921 /* Deprecated, no longer required. */
3922 #define FCTMF_SUITE_DEF(NAME)
3923 
3924 
3925 /* Executes a test suite defined by FCTMF_SUITE* */
3926 #define FCTMF_SUITE_CALL(NAME)  {\
3927     void NAME (fctkern_t *);\
3928     NAME (fctkern_ptr__);\
3929     }
3930 
3931 
3932 /*
3933 ---------------------------------------------------------
3934 FCT QUICK TEST API
3935 ----------------------------------------------------------
3936 The goal of these little macros is to try and get you
3937 up and running with a test as quick as possible.
3938 
3939 The basic idea is that there is one test per test suite.
3940 */
3941 
3942 #define FCT_QTEST_BGN(NAME) \
3943 	FCT_SUITE_BGN(NAME) {\
3944 		FCT_TEST_BGN(NAME) {\
3945 
3946 #define FCT_QTEST_END() \
3947 		} FCT_TEST_END();\
3948 	} FCT_SUITE_END();
3949 
3950 
3951 #define FCT_QTEST_BGN_IF(_CONDITION_, _NAME_) \
3952 	FCT_SUITE_BGN(_NAME_) {\
3953 		FCT_TEST_BGN_IF(_CONDITION_, _NAME_) {\
3954 
3955 #define FCT_QTEST_END_IF() \
3956 		} FCT_TEST_END_IF();\
3957 	} FCT_SUITE_END();
3958 
3959 #endif /* !FCT_INCLUDED__IMB */
3960