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
handle_success(int verbosity)144 handle_success(int verbosity) {
145   if (verbosity >= CK_VERBOSE) {
146     printf("PASS: %s\n", _check_current_function);
147   }
148 }
149 
150 static void
handle_failure(SRunner * runner,int verbosity,const char * phase_info)151 handle_failure(SRunner *runner, int verbosity, const char *phase_info) {
152   runner->nfailures++;
153   if (verbosity != CK_SILENT) {
154     printf("FAIL: %s (%s at %s:%d)\n", _check_current_function, phase_info,
155            _check_current_filename, _check_current_lineno);
156   }
157 }
158 
159 void
srunner_run_all(SRunner * runner,int verbosity)160 srunner_run_all(SRunner *runner, int verbosity) {
161   Suite *suite;
162   TCase *volatile tc;
163   assert(runner != NULL);
164   suite = runner->suite;
165   tc = suite->tests;
166   while (tc != NULL) {
167     volatile int i;
168     for (i = 0; i < tc->ntests; ++i) {
169       runner->nchecks++;
170 
171       if (tc->setup != NULL) {
172         /* setup */
173         if (setjmp(env)) {
174           handle_failure(runner, verbosity, "during setup");
175           continue;
176         }
177         tc->setup();
178       }
179       /* test */
180       if (setjmp(env)) {
181         handle_failure(runner, verbosity, "during actual test");
182         continue;
183       }
184       (tc->tests[i])();
185 
186       /* teardown */
187       if (tc->teardown != NULL) {
188         if (setjmp(env)) {
189           handle_failure(runner, verbosity, "during teardown");
190           continue;
191         }
192         tc->teardown();
193       }
194 
195       handle_success(verbosity);
196     }
197     tc = tc->next_tcase;
198   }
199   if (verbosity != CK_SILENT) {
200     int passed = runner->nchecks - runner->nfailures;
201     double percentage = ((double)passed) / runner->nchecks;
202     int display = (int)(percentage * 100);
203     printf("%d%%: Checks: %d, Failed: %d\n", display, runner->nchecks,
204            runner->nfailures);
205   }
206 }
207 
208 void
_fail_unless(int condition,const char * file,int line,const char * msg)209 _fail_unless(int condition, const char *file, int line, const char *msg) {
210   /* Always print the error message so it isn't lost.  In this case,
211      we have a failure, so there's no reason to be quiet about what
212      it is.
213   */
214   UNUSED_P(condition);
215   _check_current_filename = file;
216   _check_current_lineno = line;
217   if (msg != NULL) {
218     const int has_newline = (msg[strlen(msg) - 1] == '\n');
219     fprintf(stderr, "ERROR: %s%s", msg, has_newline ? "" : "\n");
220   }
221   longjmp(env, 1);
222 }
223 
224 int
srunner_ntests_failed(SRunner * runner)225 srunner_ntests_failed(SRunner *runner) {
226   assert(runner != NULL);
227   return runner->nfailures;
228 }
229 
230 void
srunner_free(SRunner * runner)231 srunner_free(SRunner *runner) {
232   if (! runner) {
233     return;
234   }
235 
236   suite_free(runner->suite);
237   free(runner);
238 }
239