1 /* ----------------------------------------------------------------------------
2    tinytest - A tiny C unit-testing library
3    Copyright (C) 2010-2018  Mark A Lindner
4 
5    This file is part of tinytest.
6 
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public License
9    as published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11 
12    This library is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16 
17    You should have received a copy of the GNU Library General Public
18    License along with this library; if not, see
19    <http://www.gnu.org/licenses/>.
20    ----------------------------------------------------------------------------
21 */
22 
23 #include "tinytest.h"
24 
25 #include <stdarg.h>
26 #include <stdio.h>
27 
28 #ifdef WIN32
29 #include <io.h>
30 #define F_OK 4  // Windows doesn't have F_OK
31 #else
32 #include <unistd.h>
33 #endif
34 
35 /*
36  */
37 
38 static tt_testsuite_t *__tt_current_suite = NULL;
39 
40 static const char *__tt_op_strings[] = { "==", "!=", "<", "<=", ">", ">=",
41                                          "==", "!=", "<", "<=", ">", ">=",
42                                          "==", "!=", "<", "<=", ">", ">=",
43                                          "==", "!=", "<", "<=", ">", ">=",
44                                          "==", "!=", "<", "<=", ">", ">=",
45                                          "==", "!=", "<", "<=", ">", ">=",
46                                          "==", "!=", "", "!",
47                                          "==file", "!=file",
48                                          "==txtfile", "!=txtfile" };
49 
50 /*
51  */
52 
__tt_chop(char * s)53 static tt_bool_t __tt_chop(char *s)
54 {
55   size_t len = strlen(s);
56   char *p;
57   tt_bool_t eol = TT_FALSE;
58 
59   for(p = s + len - 1; p > s; --p) {
60     if ((*p == '\r') || (*p == '\n')) {
61       eol = TT_TRUE;
62       *p = 0;
63     }
64   }
65 
66   return(eol);
67 }
68 
__tt_compare_files_text(const char * file1,const char * file2,tt_bool_t verbose)69 static tt_bool_t __tt_compare_files_text(const char *file1, const char *file2,
70                                          tt_bool_t verbose)
71 {
72   FILE *fp1, *fp2;
73   char buf1[4096], buf2[4096];
74   int line = 1;
75   int character = 0;
76   tt_bool_t matched = TT_TRUE, eof1, eof2;
77 
78   if(!(fp1 = fopen(file1, "rt")))
79   {
80     printf("cannot open file: %s\n", file1);
81     return(TT_FALSE);
82   }
83 
84   if(!(fp2 = fopen(file2, "rt")))
85   {
86     fclose(fp1);
87     printf("cannot open file: %s\n", file2);
88     return(TT_FALSE);
89   }
90 
91   for(;;)
92   {
93     char *r1, *r2, *p, *q;
94 
95     r1 = fgets(buf1, sizeof(buf1), fp1);
96     r2 = fgets(buf2, sizeof(buf2), fp2);
97 
98     if((r1 == NULL) || (r2 == NULL))
99     {
100       matched = (r1 == r2) ? TT_TRUE : TT_FALSE;
101       break;
102     }
103 
104     eof1 = __tt_chop(r1);
105     eof2 = __tt_chop(r2);
106 
107     p = r1;
108     q = r2;
109 
110     while(*p && *q)
111     {
112       if(*p != *q)
113       {
114         matched = TT_FALSE;
115         break;
116       }
117 
118       ++character;
119       ++p;
120       ++q;
121     }
122 
123     if(eof1 != eof2)
124       matched = TT_FALSE;
125 
126     if(matched == TT_FALSE)
127       break;
128 
129     if(eof1 && eof2)
130     {
131       ++line;
132       character = 0;
133     }
134   }
135 
136   fclose(fp1);
137   fclose(fp2);
138 
139   if(!matched && verbose)
140     printf("files \"%s\" and \"%s\" differ starting at line %d, char %d\n",
141            file1, file2, line, character);
142 
143   return(matched);
144 }
145 
__tt_compare_files(const char * file1,const char * file2,tt_bool_t verbose)146 static tt_bool_t __tt_compare_files(const char *file1, const char *file2,
147                                     tt_bool_t verbose)
148 {
149   FILE *fp1, *fp2;
150   char buf1[4096], buf2[4096];
151   int line = 1;
152   int character = 0;
153   size_t r1, r2;
154   tt_bool_t done = TT_FALSE, matched = TT_TRUE;
155 
156   if(!(fp1 = fopen(file1, "rb")))
157   {
158     printf("cannot open file: %s\n", file1);
159     return(TT_FALSE);
160   }
161 
162   if(!(fp2 = fopen(file2, "rb")))
163   {
164     fclose(fp1);
165     printf("cannot open file: %s\n", file2);
166     return(TT_FALSE);
167   }
168 
169   while(!done)
170   {
171     char *p, *q, *pe, *qe;
172 
173     r1 = fread(buf1, 1, sizeof(buf1), fp1);
174     r2 = fread(buf2, 1, sizeof(buf2), fp2);
175 
176     p = buf1;
177     q = buf2;
178     pe = buf1 + r1;
179     qe = buf2 + r2;
180 
181     while(p < pe && q < qe)
182     {
183       if(*p != *q)
184       {
185         matched = TT_FALSE;
186         done = TT_TRUE;
187         break;
188       }
189 
190       if(*p == '\n')
191       {
192         ++line;
193         character = 0;
194       }
195       else
196         ++character;
197 
198       ++p;
199       ++q;
200     }
201 
202     if(p < pe || q < qe)
203     {
204       matched = TT_FALSE;
205       break;
206     }
207 
208     if(feof(fp1) || feof(fp2))
209       break;
210   }
211 
212   fclose(fp1);
213   fclose(fp2);
214 
215   if(!matched && verbose)
216     printf("files \"%s\" and \"%s\" differ starting at line %d, char %d\n",
217            file1, file2, line, character);
218 
219   return(matched);
220 }
221 
222 /*
223  */
224 
tt_suite_create(const char * name)225 tt_testsuite_t *tt_suite_create(const char *name)
226 {
227   tt_testsuite_t *suite = calloc(1, sizeof(tt_testsuite_t));
228   suite->name = strdup(name);
229   return(suite);
230 }
231 
232 /*
233  */
234 
tt_suite_destroy(tt_testsuite_t * suite)235 void tt_suite_destroy(tt_testsuite_t *suite)
236 {
237   tt_test_t *test = suite->first_test;
238 
239   while(test)
240   {
241     tt_test_t *tmp = test->next;
242     free((void *)test->name);
243     free(test);
244     test = tmp;
245   }
246 
247   free((void *)suite->name);
248   free(suite);
249 }
250 
251 /*
252  */
253 
tt_suite_add_test(tt_testsuite_t * suite,const char * name,void (* function)(void))254 void tt_suite_add_test(tt_testsuite_t *suite, const char *name,
255                        void (*function)(void))
256 {
257   tt_test_t *test = calloc(1, sizeof(tt_test_t));
258   test->name = strdup(name);
259   test->function = function;
260 
261   if(suite->last_test != NULL)
262     suite->last_test->next = test;
263 
264   suite->last_test = test;
265 
266   if(suite->first_test == NULL)
267     suite->first_test = test;
268 
269   ++suite->num_tests;
270 }
271 
272 /*
273  */
274 
tt_suite_run(tt_testsuite_t * suite)275 void tt_suite_run(tt_testsuite_t *suite)
276 {
277   __tt_current_suite = suite;
278 
279   suite->num_failures = 0;
280 
281   for(suite->current_test = suite->first_test;
282       suite->current_test;
283       suite->current_test = suite->current_test->next)
284   {
285     printf("[TEST] %s\n", suite->current_test->name);
286 
287     if(setjmp(suite->jump_buf) == 0)
288     {
289       suite->current_test->function();
290     }
291 
292     if(suite->current_test->failed)
293     {
294       printf("[FAIL] %s\n", suite->current_test->name);
295       ++suite->num_failures;
296     }
297     else
298     {
299       printf("[ OK ] %s\n", suite->current_test->name);
300     }
301   }
302 
303   if(suite->num_failures > 0)
304     puts("*** FAILURES! ***");
305 
306   printf("%d tests; %d passed, %d failed\n",
307          suite->num_tests, suite->num_tests - suite->num_failures,
308          suite->num_failures);
309 
310   suite->current_test = NULL;
311   __tt_current_suite = NULL;
312 }
313 
314 /*
315  */
316 
tt_output_val(FILE * stream,const tt_val_t * val)317 void tt_output_val(FILE *stream, const tt_val_t *val)
318 {
319   switch(val->type)
320   {
321     case TT_VAL_INT:
322       fprintf(stream, "%d", val->value.int_val);
323       break;
324 
325     case TT_VAL_UINT:
326       fprintf(stream, "%u", val->value.uint_val);
327       break;
328 
329     case TT_VAL_INT64:
330       fprintf(stream, "%lld", val->value.int64_val);
331       break;
332 
333     case TT_VAL_UINT64:
334       fprintf(stream, "%llu", val->value.uint64_val);
335       break;
336 
337     case TT_VAL_DOUBLE:
338       fprintf(stream, "%f", val->value.double_val);
339       break;
340 
341     case TT_VAL_STR:
342     {
343       const char *p;
344 
345       fputc('\"', stream);
346       for(p = val->value.str_val; *p; ++p)
347       {
348         if(*p == '\n')
349           fputs("\\n", stream);
350         else if(*p == '\r')
351           fputs("\\r", stream);
352         else if(*p == '\t')
353           fputs("\\t", stream);
354         else if(*p == '\f')
355           fputs("\\f", stream);
356         else if(*p < ' ')
357           fprintf(stream, "\\0x%02X", *p);
358         else
359           fputc(*p, stream);
360       }
361       fputc('\"', stream);
362       break;
363     }
364 
365     case TT_VAL_PTR:
366       fprintf(stream, "%p", val->value.ptr_val);
367       break;
368 
369     default:
370       fputs("???", stream);
371       break;
372   }
373 }
374 
375 /*
376  */
377 
tt_expect(const char * file,int line,const char * aexpr,tt_op_t op,const char * bexpr,const tt_val_t a,const tt_val_t b,tt_bool_t fatal)378 void tt_expect(const char *file, int line, const char *aexpr,
379                tt_op_t op, const char *bexpr, const tt_val_t a,
380                const tt_val_t b, tt_bool_t fatal)
381 {
382   tt_bool_t result = TT_FALSE;
383 
384   switch(op)
385   {
386     case TT_OP_INT_EQ:
387       result = (a.value.int_val == b.value.int_val);
388       break;
389 
390     case TT_OP_INT_NE:
391       result = (a.value.int_val != b.value.int_val);
392       break;
393 
394     case TT_OP_INT_LT:
395       result = (a.value.int_val < b.value.int_val);
396       break;
397 
398     case TT_OP_INT_LE:
399       result = (a.value.int_val <= b.value.int_val);
400       break;
401 
402     case TT_OP_INT_GT:
403       result = (a.value.int_val > b.value.int_val);
404       break;
405 
406     case TT_OP_INT_GE:
407       result = (a.value.int_val >= b.value.int_val);
408       break;
409 
410     case TT_OP_UINT_EQ:
411       result = (a.value.uint_val == b.value.uint_val);
412       break;
413 
414     case TT_OP_UINT_NE:
415       result = (a.value.uint_val != b.value.uint_val);
416       break;
417 
418     case TT_OP_UINT_LT:
419       result = (a.value.uint_val < b.value.uint_val);
420       break;
421 
422     case TT_OP_UINT_LE:
423       result = (a.value.uint_val <= b.value.uint_val);
424       break;
425 
426     case TT_OP_UINT_GT:
427       result = (a.value.uint_val > b.value.uint_val);
428       break;
429 
430     case TT_OP_UINT_GE:
431       result = (a.value.uint_val >= b.value.uint_val);
432       break;
433 
434     case TT_OP_INT64_EQ:
435       result = (a.value.int64_val == b.value.int64_val);
436       break;
437 
438     case TT_OP_INT64_NE:
439       result = (a.value.int64_val != b.value.int64_val);
440       break;
441 
442     case TT_OP_INT64_LT:
443       result = (a.value.int64_val < b.value.int64_val);
444       break;
445 
446     case TT_OP_INT64_LE:
447       result = (a.value.int64_val <= b.value.int64_val);
448       break;
449 
450     case TT_OP_INT64_GT:
451       result = (a.value.int64_val > b.value.int64_val);
452       break;
453 
454     case TT_OP_INT64_GE:
455       result = (a.value.int64_val >= b.value.int64_val);
456       break;
457 
458     case TT_OP_UINT64_EQ:
459       result = (a.value.uint64_val == b.value.uint64_val);
460       break;
461 
462     case TT_OP_UINT64_NE:
463       result = (a.value.uint64_val != b.value.uint64_val);
464       break;
465 
466     case TT_OP_UINT64_LT:
467       result = (a.value.uint64_val < b.value.uint64_val);
468       break;
469 
470     case TT_OP_UINT64_LE:
471       result = (a.value.uint64_val <= b.value.uint64_val);
472       break;
473 
474     case TT_OP_UINT64_GT:
475       result = (a.value.uint64_val > b.value.uint64_val);
476       break;
477 
478     case TT_OP_UINT64_GE:
479       result = (a.value.uint64_val >= b.value.uint64_val);
480       break;
481 
482     case TT_OP_DOUBLE_EQ:
483       result = (a.value.double_val == b.value.double_val);
484       break;
485 
486     case TT_OP_DOUBLE_NE:
487     {
488       double diff = (a.value.double_val - b.value.double_val);
489       result = ((diff < -.0001) || (diff > .0001));
490       break;
491     }
492 
493     case TT_OP_DOUBLE_LT:
494       result = (a.value.double_val < b.value.double_val);
495       break;
496 
497     case TT_OP_DOUBLE_LE:
498       result = (a.value.double_val <= b.value.double_val);
499       break;
500 
501     case TT_OP_DOUBLE_GT:
502       result = (a.value.double_val > b.value.double_val);
503       break;
504 
505     case TT_OP_DOUBLE_GE:
506       result = (a.value.double_val >= b.value.double_val);
507       break;
508 
509     case TT_OP_STR_EQ:
510       result = !strcmp(a.value.str_val, b.value.str_val);
511       break;
512 
513     case TT_OP_STR_NE:
514       result = strcmp(a.value.str_val, b.value.str_val);
515       break;
516 
517     case TT_OP_STR_LT:
518       result = (strcmp(a.value.str_val, b.value.str_val) < 0);
519       break;
520 
521     case TT_OP_STR_LE:
522       result = (strcmp(a.value.str_val, b.value.str_val) <= 0);
523       break;
524 
525     case TT_OP_STR_GT:
526       result = (strcmp(a.value.str_val, b.value.str_val) > 0);
527       break;
528 
529     case TT_OP_STR_GE:
530       result = (strcmp(a.value.str_val, b.value.str_val) >= 0);
531       break;
532 
533     case TT_OP_PTR_EQ:
534       result = (a.value.ptr_val == b.value.ptr_val);
535       break;
536 
537     case TT_OP_PTR_NE:
538       result = (a.value.ptr_val != b.value.ptr_val);
539       break;
540 
541     case TT_OP_FILE_EQ:
542       result = __tt_compare_files(a.value.str_val, b.value.str_val, TT_TRUE);
543       break;
544 
545     case TT_OP_FILE_NE:
546       result = !__tt_compare_files(a.value.str_val, b.value.str_val, TT_TRUE);
547       break;
548 
549     case TT_OP_TXTFILE_EQ:
550       result = __tt_compare_files_text(a.value.str_val, b.value.str_val,
551                                        TT_TRUE);
552       break;
553 
554     case TT_OP_TXTFILE_NE:
555       result = !__tt_compare_files_text(a.value.str_val, b.value.str_val,
556                                         TT_TRUE);
557       break;
558 
559     default:
560       break;
561   }
562 
563   if(!result)
564   {
565     __tt_current_suite->current_test->failed = TT_TRUE;
566 
567     printf("%s:%d: failed %s: %s [", file, line, (fatal ? "assert" : "expect"),
568            aexpr);
569     tt_output_val(stdout, &a);
570     printf("] %s %s [", __tt_op_strings[op], bexpr);
571     tt_output_val(stdout, &b);
572     puts("]");
573 
574     if(fatal)
575       longjmp(__tt_current_suite->jump_buf, 0);
576   }
577 }
578 
579 /*
580  */
581 
tt_expect_bool(const char * file,int line,const char * expr,tt_op_t op,int val,tt_bool_t fatal)582 void tt_expect_bool(const char *file, int line, const char *expr, tt_op_t op,
583                     int val, tt_bool_t fatal)
584 {
585   tt_bool_t result = TT_FALSE;
586 
587   switch(op)
588   {
589     case TT_OP_TRUE:
590       result = (val != 0);
591       break;
592 
593     case TT_OP_FALSE:
594       result = (val == 0);
595       break;
596 
597     default:
598       break;
599   }
600 
601   if(!result)
602   {
603     __tt_current_suite->current_test->failed = TT_TRUE;
604 
605     printf("%s:%d: failed %s: %s(%s)\n", file, line,
606            (fatal ? "assert" : "expect"), __tt_op_strings[op], expr);
607 
608     if(fatal)
609       longjmp(__tt_current_suite->jump_buf, 0);
610   }
611 }
612 
613 /*
614  */
615 
tt_fail(const char * file,int line,const char * message,...)616 void tt_fail(const char *file, int line, const char *message, ...)
617 {
618   va_list vp;
619   va_start(vp, message);
620   printf("%s:%d: failed: ", file, line);
621   vprintf(message, vp);
622   va_end(vp);
623   putchar('\n');
624 }
625 
626 /*
627  */
628 
tt_file_exists(const char * file)629 tt_bool_t tt_file_exists(const char *file)
630 {
631   return(access(file, F_OK) == 0);
632 }
633 
634 #ifdef _MSC_VER
635 
636 // All of this extra code is because MSVC doesn't support the C99 standard.
637 // Sigh.
638 
639 /*
640  */
641 
tt_test_int(const char * file,int line,const char * aexpr,tt_op_t op,const char * bexpr,int a,int b,tt_bool_t fatal)642 void tt_test_int(const char *file, int line, const char *aexpr, tt_op_t op,
643                  const char *bexpr, int a, int b, tt_bool_t fatal)
644 {
645   tt_val_t aval = { TT_VAL_INT }, bval = { TT_VAL_INT };
646   aval.value.int_val = a;
647   bval.value.int_val = b;
648   tt_expect(file, line, aexpr, op, bexpr, aval, bval, fatal);
649 }
650 
651 /*
652  */
653 
tt_test_uint(const char * file,int line,const char * aexpr,tt_op_t op,const char * bexpr,unsigned int a,unsigned int b,tt_bool_t fatal)654 void tt_test_uint(const char *file, int line, const char *aexpr, tt_op_t op,
655                   const char *bexpr, unsigned int a, unsigned int b,
656                   tt_bool_t fatal)
657 {
658   tt_val_t aval = { TT_VAL_UINT }, bval = { TT_VAL_UINT };
659   aval.value.uint_val = a;
660   bval.value.uint_val = b;
661   tt_expect(file, line, aexpr, op, bexpr, aval, bval, fatal);
662 }
663 
664 /*
665  */
666 
tt_test_int64(const char * file,int line,const char * aexpr,tt_op_t op,const char * bexpr,long long a,long long b,tt_bool_t fatal)667 void tt_test_int64(const char *file, int line, const char *aexpr, tt_op_t op,
668                    const char *bexpr, long long a, long long b,
669                    tt_bool_t fatal)
670 {
671   tt_val_t aval = { TT_VAL_INT64 }, bval = { TT_VAL_INT64 };
672   aval.value.int64_val = a;
673   bval.value.int64_val = b;
674   tt_expect(file, line, aexpr, op, bexpr, aval, bval, fatal);
675 }
676 
677 /*
678  */
679 
tt_test_uint64(const char * file,int line,const char * aexpr,tt_op_t op,const char * bexpr,unsigned long long a,unsigned long long b,tt_bool_t fatal)680 void tt_test_uint64(const char *file, int line, const char *aexpr,
681                     tt_op_t op, const char *bexpr, unsigned long long a,
682                     unsigned long long b, tt_bool_t fatal)
683 {
684   tt_val_t aval = { TT_VAL_UINT64 }, bval = { TT_VAL_UINT64 };
685   aval.value.uint64_val = a;
686   bval.value.uint64_val = b;
687   tt_expect(file, line, aexpr, op, bexpr, aval, bval, fatal);
688 }
689 
690 /*
691  */
692 
tt_test_double(const char * file,int line,const char * aexpr,tt_op_t op,const char * bexpr,double a,double b,tt_bool_t fatal)693 void tt_test_double(const char *file, int line, const char *aexpr, tt_op_t op,
694                     const char *bexpr, double a, double b, tt_bool_t fatal)
695 {
696   tt_val_t aval = { TT_VAL_DOUBLE }, bval = { TT_VAL_DOUBLE };
697   aval.value.double_val = a;
698   bval.value.double_val = b;
699   tt_expect(file, line, aexpr, op, bexpr, aval, bval, fatal);
700 }
701 
702 /*
703  */
704 
tt_test_str(const char * file,int line,const char * aexpr,tt_op_t op,const char * bexpr,const char * a,const char * b,tt_bool_t fatal)705 void tt_test_str(const char *file, int line, const char *aexpr, tt_op_t op,
706                  const char *bexpr, const char *a, const char *b,
707                  tt_bool_t fatal)
708 {
709   tt_val_t aval = { TT_VAL_STR }, bval = { TT_VAL_STR };
710   aval.value.str_val = a;
711   bval.value.str_val = b;
712   tt_expect(file, line, aexpr, op, bexpr, aval, bval, fatal);
713 }
714 
715 /*
716  */
717 
tt_test_ptr(const char * file,int line,const char * aexpr,tt_op_t op,const char * bexpr,const void * a,const void * b,tt_bool_t fatal)718 void tt_test_ptr(const char *file, int line, const char *aexpr, tt_op_t op,
719                  const char *bexpr, const void *a, const void *b,
720                  tt_bool_t fatal)
721 {
722   tt_val_t aval = { TT_VAL_PTR }, bval = { TT_VAL_PTR };
723   aval.value.ptr_val = a;
724   bval.value.ptr_val = b;
725   tt_expect(file, line, aexpr, op, bexpr, aval, bval, fatal);
726 }
727 
728 #endif // WIN32
729 
730 /* end of source file */
731