1 /* Miniature re-implementation of the "check" library.
2 
3    This is intended to support just enough of check to run the Expat
4    tests.  This interface is based entirely on the portion of the
5    check library being used.
6                             __  __            _
7                          ___\ \/ /_ __   __ _| |_
8                         / _ \\  /| '_ \ / _` | __|
9                        |  __//  \| |_) | (_| | |_
10                         \___/_/\_\ .__/ \__,_|\__|
11                                  |_| XML parser
12 
13    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
14    Copyright (c) 2000-2017 Expat development team
15    Licensed under the MIT license:
16 
17    Permission is  hereby granted,  free of charge,  to any  person obtaining
18    a  copy  of  this  software   and  associated  documentation  files  (the
19    "Software"),  to  deal in  the  Software  without restriction,  including
20    without  limitation the  rights  to use,  copy,  modify, merge,  publish,
21    distribute, sublicense, and/or sell copies of the Software, and to permit
22    persons  to whom  the Software  is  furnished to  do so,  subject to  the
23    following conditions:
24 
25    The above copyright  notice and this permission notice  shall be included
26    in all copies or substantial portions of the Software.
27 
28    THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
29    EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
30    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
31    NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
32    DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
33    OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
34    USE OR OTHER DEALINGS IN THE SOFTWARE.
35 */
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <setjmp.h>
40 #include <assert.h>
41 #include <string.h>
42 
43 #include "internal.h" /* for UNUSED_P only */
44 #include "minicheck.h"
45 
46 Suite *
suite_create(const char * name)47 suite_create(const char *name) {
48   Suite *suite = (Suite *)calloc(1, sizeof(Suite));
49   if (suite != NULL) {
50     suite->name = name;
51   }
52   return suite;
53 }
54 
55 TCase *
tcase_create(const char * name)56 tcase_create(const char *name) {
57   TCase *tc = (TCase *)calloc(1, sizeof(TCase));
58   if (tc != NULL) {
59     tc->name = name;
60   }
61   return tc;
62 }
63 
64 void
suite_add_tcase(Suite * suite,TCase * tc)65 suite_add_tcase(Suite *suite, TCase *tc) {
66   assert(suite != NULL);
67   assert(tc != NULL);
68   assert(tc->next_tcase == NULL);
69 
70   tc->next_tcase = suite->tests;
71   suite->tests = tc;
72 }
73 
74 void
tcase_add_checked_fixture(TCase * tc,tcase_setup_function setup,tcase_teardown_function teardown)75 tcase_add_checked_fixture(TCase *tc, tcase_setup_function setup,
76                           tcase_teardown_function teardown) {
77   assert(tc != NULL);
78   tc->setup = setup;
79   tc->teardown = teardown;
80 }
81 
82 void
tcase_add_test(TCase * tc,tcase_test_function test)83 tcase_add_test(TCase *tc, tcase_test_function test) {
84   assert(tc != NULL);
85   if (tc->allocated == tc->ntests) {
86     int nalloc = tc->allocated + 100;
87     size_t new_size = sizeof(tcase_test_function) * nalloc;
88     tcase_test_function *new_tests = realloc(tc->tests, new_size);
89     assert(new_tests != NULL);
90     tc->tests = new_tests;
91     tc->allocated = nalloc;
92   }
93   tc->tests[tc->ntests] = test;
94   tc->ntests++;
95 }
96 
97 static void
tcase_free(TCase * tc)98 tcase_free(TCase *tc) {
99   if (! tc) {
100     return;
101   }
102 
103   free(tc->tests);
104   free(tc);
105 }
106 
107 static void
suite_free(Suite * suite)108 suite_free(Suite *suite) {
109   if (! suite) {
110     return;
111   }
112 
113   while (suite->tests != NULL) {
114     TCase *next = suite->tests->next_tcase;
115     tcase_free(suite->tests);
116     suite->tests = next;
117   }
118   free(suite);
119 }
120 
121 SRunner *
srunner_create(Suite * suite)122 srunner_create(Suite *suite) {
123   SRunner *runner = calloc(1, sizeof(SRunner));
124   if (runner != NULL) {
125     runner->suite = suite;
126   }
127   return runner;
128 }
129 
130 static jmp_buf env;
131 
132 static char const *_check_current_function = NULL;
133 static int _check_current_lineno = -1;
134 static char const *_check_current_filename = NULL;
135 
136 void
_check_set_test_info(char const * function,char const * filename,int lineno)137 _check_set_test_info(char const *function, char const *filename, int lineno) {
138   _check_current_function = function;
139   _check_current_lineno = lineno;
140   _check_current_filename = filename;
141 }
142 
143 static void
add_failure(SRunner * runner,int verbosity)144 add_failure(SRunner *runner, int verbosity) {
145   runner->nfailures++;
146   if (verbosity >= CK_VERBOSE) {
147     printf("%s:%d: %s\n", _check_current_filename, _check_current_lineno,
148            _check_current_function);
149   }
150 }
151 
152 void
srunner_run_all(SRunner * runner,int verbosity)153 srunner_run_all(SRunner *runner, int verbosity) {
154   Suite *suite;
155   TCase *volatile tc;
156   assert(runner != NULL);
157   suite = runner->suite;
158   tc = suite->tests;
159   while (tc != NULL) {
160     volatile int i;
161     for (i = 0; i < tc->ntests; ++i) {
162       runner->nchecks++;
163 
164       if (tc->setup != NULL) {
165         /* setup */
166         if (setjmp(env)) {
167           add_failure(runner, verbosity);
168           continue;
169         }
170         tc->setup();
171       }
172       /* test */
173       if (setjmp(env)) {
174         add_failure(runner, verbosity);
175         continue;
176       }
177       (tc->tests[i])();
178 
179       /* teardown */
180       if (tc->teardown != NULL) {
181         if (setjmp(env)) {
182           add_failure(runner, verbosity);
183           continue;
184         }
185         tc->teardown();
186       }
187     }
188     tc = tc->next_tcase;
189   }
190   if (verbosity) {
191     int passed = runner->nchecks - runner->nfailures;
192     double percentage = ((double)passed) / runner->nchecks;
193     int display = (int)(percentage * 100);
194     printf("%d%%: Checks: %d, Failed: %d\n", display, runner->nchecks,
195            runner->nfailures);
196   }
197 }
198 
199 void
_fail_unless(int condition,const char * file,int line,const char * msg)200 _fail_unless(int condition, const char *file, int line, const char *msg) {
201   /* Always print the error message so it isn't lost.  In this case,
202      we have a failure, so there's no reason to be quiet about what
203      it is.
204   */
205   UNUSED_P(condition);
206   UNUSED_P(file);
207   UNUSED_P(line);
208   if (msg != NULL) {
209     const int has_newline = (msg[strlen(msg) - 1] == '\n');
210     fprintf(stderr, "ERROR: %s%s", msg, has_newline ? "" : "\n");
211   }
212   longjmp(env, 1);
213 }
214 
215 int
srunner_ntests_failed(SRunner * runner)216 srunner_ntests_failed(SRunner *runner) {
217   assert(runner != NULL);
218   return runner->nfailures;
219 }
220 
221 void
srunner_free(SRunner * runner)222 srunner_free(SRunner *runner) {
223   if (! runner) {
224     return;
225   }
226 
227   suite_free(runner->suite);
228   free(runner);
229 }
230