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