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