xref: /freebsd/contrib/expat/tests/minicheck.c (revision 4543ef516683042d46f3bd3bb8a4f3f746e00499)
1220ed979SColeman Kane /* Miniature re-implementation of the "check" library.
20a48773fSEric van Gyzen 
30a48773fSEric van Gyzen    This is intended to support just enough of check to run the Expat
40a48773fSEric van Gyzen    tests.  This interface is based entirely on the portion of the
50a48773fSEric van Gyzen    check library being used.
60a48773fSEric van Gyzen                             __  __            _
70a48773fSEric van Gyzen                          ___\ \/ /_ __   __ _| |_
80a48773fSEric van Gyzen                         / _ \\  /| '_ \ / _` | __|
90a48773fSEric van Gyzen                        |  __//  \| |_) | (_| | |_
100a48773fSEric van Gyzen                         \___/_/\_\ .__/ \__,_|\__|
110a48773fSEric van Gyzen                                  |_| XML parser
120a48773fSEric van Gyzen 
13cc68614dSXin LI    Copyright (c) 2004-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
14*4543ef51SXin LI    Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
15cc68614dSXin LI    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
16cc68614dSXin LI    Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
17cc68614dSXin LI    Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
18*4543ef51SXin LI    Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
190a48773fSEric van Gyzen    Licensed under the MIT license:
200a48773fSEric van Gyzen 
210a48773fSEric van Gyzen    Permission is  hereby granted,  free of charge,  to any  person obtaining
220a48773fSEric van Gyzen    a  copy  of  this  software   and  associated  documentation  files  (the
230a48773fSEric van Gyzen    "Software"),  to  deal in  the  Software  without restriction,  including
240a48773fSEric van Gyzen    without  limitation the  rights  to use,  copy,  modify, merge,  publish,
250a48773fSEric van Gyzen    distribute, sublicense, and/or sell copies of the Software, and to permit
260a48773fSEric van Gyzen    persons  to whom  the Software  is  furnished to  do so,  subject to  the
270a48773fSEric van Gyzen    following conditions:
280a48773fSEric van Gyzen 
290a48773fSEric van Gyzen    The above copyright  notice and this permission notice  shall be included
300a48773fSEric van Gyzen    in all copies or substantial portions of the Software.
310a48773fSEric van Gyzen 
320a48773fSEric van Gyzen    THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
330a48773fSEric van Gyzen    EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
340a48773fSEric van Gyzen    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
350a48773fSEric van Gyzen    NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
360a48773fSEric van Gyzen    DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
370a48773fSEric van Gyzen    OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
380a48773fSEric van Gyzen    USE OR OTHER DEALINGS IN THE SOFTWARE.
39220ed979SColeman Kane */
40220ed979SColeman Kane 
41*4543ef51SXin LI #if defined(NDEBUG)
42*4543ef51SXin LI #  undef NDEBUG /* because test suite relies on assert(...) at the moment */
43*4543ef51SXin LI #endif
44*4543ef51SXin LI 
45*4543ef51SXin LI #include <stdarg.h>
46220ed979SColeman Kane #include <stdio.h>
47220ed979SColeman Kane #include <stdlib.h>
48220ed979SColeman Kane #include <setjmp.h>
49220ed979SColeman Kane #include <assert.h>
500a48773fSEric van Gyzen #include <string.h>
51220ed979SColeman Kane 
52be8aff81SXin LI #include "internal.h" /* for UNUSED_P only */
53220ed979SColeman Kane #include "minicheck.h"
54220ed979SColeman Kane 
55220ed979SColeman Kane Suite *
suite_create(const char * name)566b2c1e49SXin LI suite_create(const char *name) {
57220ed979SColeman Kane   Suite *suite = (Suite *)calloc(1, sizeof(Suite));
58220ed979SColeman Kane   if (suite != NULL) {
59220ed979SColeman Kane     suite->name = name;
60220ed979SColeman Kane   }
61220ed979SColeman Kane   return suite;
62220ed979SColeman Kane }
63220ed979SColeman Kane 
64220ed979SColeman Kane TCase *
tcase_create(const char * name)656b2c1e49SXin LI tcase_create(const char *name) {
66220ed979SColeman Kane   TCase *tc = (TCase *)calloc(1, sizeof(TCase));
67220ed979SColeman Kane   if (tc != NULL) {
68220ed979SColeman Kane     tc->name = name;
69220ed979SColeman Kane   }
70220ed979SColeman Kane   return tc;
71220ed979SColeman Kane }
72220ed979SColeman Kane 
73220ed979SColeman Kane void
suite_add_tcase(Suite * suite,TCase * tc)746b2c1e49SXin LI suite_add_tcase(Suite *suite, TCase *tc) {
75220ed979SColeman Kane   assert(suite != NULL);
76220ed979SColeman Kane   assert(tc != NULL);
77220ed979SColeman Kane   assert(tc->next_tcase == NULL);
78220ed979SColeman Kane 
79220ed979SColeman Kane   tc->next_tcase = suite->tests;
80220ed979SColeman Kane   suite->tests = tc;
81220ed979SColeman Kane }
82220ed979SColeman Kane 
83220ed979SColeman Kane void
tcase_add_checked_fixture(TCase * tc,tcase_setup_function setup,tcase_teardown_function teardown)846b2c1e49SXin LI tcase_add_checked_fixture(TCase *tc, tcase_setup_function setup,
856b2c1e49SXin LI                           tcase_teardown_function teardown) {
86220ed979SColeman Kane   assert(tc != NULL);
87220ed979SColeman Kane   tc->setup = setup;
88220ed979SColeman Kane   tc->teardown = teardown;
89220ed979SColeman Kane }
90220ed979SColeman Kane 
91220ed979SColeman Kane void
tcase_add_test(TCase * tc,tcase_test_function test)926b2c1e49SXin LI tcase_add_test(TCase *tc, tcase_test_function test) {
93220ed979SColeman Kane   assert(tc != NULL);
94220ed979SColeman Kane   if (tc->allocated == tc->ntests) {
95220ed979SColeman Kane     int nalloc = tc->allocated + 100;
96220ed979SColeman Kane     size_t new_size = sizeof(tcase_test_function) * nalloc;
97*4543ef51SXin LI     tcase_test_function *const new_tests
98*4543ef51SXin LI         = (tcase_test_function *)realloc(tc->tests, new_size);
99220ed979SColeman Kane     assert(new_tests != NULL);
100220ed979SColeman Kane     tc->tests = new_tests;
101220ed979SColeman Kane     tc->allocated = nalloc;
102220ed979SColeman Kane   }
103220ed979SColeman Kane   tc->tests[tc->ntests] = test;
104220ed979SColeman Kane   tc->ntests++;
105220ed979SColeman Kane }
106220ed979SColeman Kane 
1070a48773fSEric van Gyzen static void
tcase_free(TCase * tc)1086b2c1e49SXin LI tcase_free(TCase *tc) {
1090a48773fSEric van Gyzen   if (! tc) {
1100a48773fSEric van Gyzen     return;
1110a48773fSEric van Gyzen   }
1120a48773fSEric van Gyzen 
1130a48773fSEric van Gyzen   free(tc->tests);
1140a48773fSEric van Gyzen   free(tc);
1150a48773fSEric van Gyzen }
1160a48773fSEric van Gyzen 
1170a48773fSEric van Gyzen static void
suite_free(Suite * suite)1186b2c1e49SXin LI suite_free(Suite *suite) {
1190a48773fSEric van Gyzen   if (! suite) {
1200a48773fSEric van Gyzen     return;
1210a48773fSEric van Gyzen   }
1220a48773fSEric van Gyzen 
1230a48773fSEric van Gyzen   while (suite->tests != NULL) {
1240a48773fSEric van Gyzen     TCase *next = suite->tests->next_tcase;
1250a48773fSEric van Gyzen     tcase_free(suite->tests);
1260a48773fSEric van Gyzen     suite->tests = next;
1270a48773fSEric van Gyzen   }
1280a48773fSEric van Gyzen   free(suite);
1290a48773fSEric van Gyzen }
1300a48773fSEric van Gyzen 
131220ed979SColeman Kane SRunner *
srunner_create(Suite * suite)1326b2c1e49SXin LI srunner_create(Suite *suite) {
133*4543ef51SXin LI   SRunner *const runner = (SRunner *)calloc(1, sizeof(SRunner));
134220ed979SColeman Kane   if (runner != NULL) {
135220ed979SColeman Kane     runner->suite = suite;
136220ed979SColeman Kane   }
137220ed979SColeman Kane   return runner;
138220ed979SColeman Kane }
139220ed979SColeman Kane 
140220ed979SColeman Kane static jmp_buf env;
141220ed979SColeman Kane 
142*4543ef51SXin LI #define SUBTEST_LEN (50) // informative, but not too long
143220ed979SColeman Kane static char const *_check_current_function = NULL;
144*4543ef51SXin LI static char _check_current_subtest[SUBTEST_LEN];
145220ed979SColeman Kane static int _check_current_lineno = -1;
146220ed979SColeman Kane static char const *_check_current_filename = NULL;
147220ed979SColeman Kane 
148220ed979SColeman Kane void
_check_set_test_info(char const * function,char const * filename,int lineno)1496b2c1e49SXin LI _check_set_test_info(char const *function, char const *filename, int lineno) {
150220ed979SColeman Kane   _check_current_function = function;
151*4543ef51SXin LI   set_subtest("%s", "");
152220ed979SColeman Kane   _check_current_lineno = lineno;
153220ed979SColeman Kane   _check_current_filename = filename;
154220ed979SColeman Kane }
155220ed979SColeman Kane 
156*4543ef51SXin LI void
set_subtest(char const * fmt,...)157*4543ef51SXin LI set_subtest(char const *fmt, ...) {
158*4543ef51SXin LI   va_list ap;
159*4543ef51SXin LI   va_start(ap, fmt);
160*4543ef51SXin LI   vsnprintf(_check_current_subtest, SUBTEST_LEN, fmt, ap);
161*4543ef51SXin LI   va_end(ap);
162*4543ef51SXin LI   // replace line feeds with spaces, for nicer error logs
163*4543ef51SXin LI   for (size_t i = 0; i < SUBTEST_LEN; ++i) {
164*4543ef51SXin LI     if (_check_current_subtest[i] == '\n') {
165*4543ef51SXin LI       _check_current_subtest[i] = ' ';
166*4543ef51SXin LI     }
167*4543ef51SXin LI   }
168*4543ef51SXin LI   _check_current_subtest[SUBTEST_LEN - 1] = '\0'; // ensure termination
169*4543ef51SXin LI }
170*4543ef51SXin LI 
171220ed979SColeman Kane static void
handle_success(int verbosity)172cc68614dSXin LI handle_success(int verbosity) {
173220ed979SColeman Kane   if (verbosity >= CK_VERBOSE) {
174cc68614dSXin LI     printf("PASS: %s\n", _check_current_function);
175cc68614dSXin LI   }
176cc68614dSXin LI }
177cc68614dSXin LI 
178cc68614dSXin LI static void
handle_failure(SRunner * runner,int verbosity,const char * context,const char * phase_info)179*4543ef51SXin LI handle_failure(SRunner *runner, int verbosity, const char *context,
180*4543ef51SXin LI                const char *phase_info) {
181cc68614dSXin LI   runner->nfailures++;
182cc68614dSXin LI   if (verbosity != CK_SILENT) {
183*4543ef51SXin LI     if (strlen(_check_current_subtest) != 0) {
184*4543ef51SXin LI       phase_info = _check_current_subtest;
185*4543ef51SXin LI     }
186*4543ef51SXin LI     printf("FAIL [%s]: %s (%s at %s:%d)\n", context, _check_current_function,
187*4543ef51SXin LI            phase_info, _check_current_filename, _check_current_lineno);
188220ed979SColeman Kane   }
189220ed979SColeman Kane }
190220ed979SColeman Kane 
191220ed979SColeman Kane void
srunner_run_all(SRunner * runner,const char * context,int verbosity)192*4543ef51SXin LI srunner_run_all(SRunner *runner, const char *context, int verbosity) {
193220ed979SColeman Kane   Suite *suite;
1946b2c1e49SXin LI   TCase *volatile tc;
195220ed979SColeman Kane   assert(runner != NULL);
196220ed979SColeman Kane   suite = runner->suite;
197220ed979SColeman Kane   tc = suite->tests;
198220ed979SColeman Kane   while (tc != NULL) {
1996b2c1e49SXin LI     volatile int i;
200220ed979SColeman Kane     for (i = 0; i < tc->ntests; ++i) {
201220ed979SColeman Kane       runner->nchecks++;
202*4543ef51SXin LI       set_subtest("%s", "");
203220ed979SColeman Kane 
204220ed979SColeman Kane       if (tc->setup != NULL) {
205220ed979SColeman Kane         /* setup */
206220ed979SColeman Kane         if (setjmp(env)) {
207*4543ef51SXin LI           handle_failure(runner, verbosity, context, "during setup");
208220ed979SColeman Kane           continue;
209220ed979SColeman Kane         }
210220ed979SColeman Kane         tc->setup();
211220ed979SColeman Kane       }
212220ed979SColeman Kane       /* test */
213220ed979SColeman Kane       if (setjmp(env)) {
214*4543ef51SXin LI         handle_failure(runner, verbosity, context, "during actual test");
215220ed979SColeman Kane         continue;
216220ed979SColeman Kane       }
217220ed979SColeman Kane       (tc->tests[i])();
218*4543ef51SXin LI       set_subtest("%s", "");
219220ed979SColeman Kane 
220220ed979SColeman Kane       /* teardown */
221220ed979SColeman Kane       if (tc->teardown != NULL) {
222220ed979SColeman Kane         if (setjmp(env)) {
223*4543ef51SXin LI           handle_failure(runner, verbosity, context, "during teardown");
224220ed979SColeman Kane           continue;
225220ed979SColeman Kane         }
226220ed979SColeman Kane         tc->teardown();
227220ed979SColeman Kane       }
228cc68614dSXin LI 
229cc68614dSXin LI       handle_success(verbosity);
230220ed979SColeman Kane     }
231220ed979SColeman Kane     tc = tc->next_tcase;
232220ed979SColeman Kane   }
233*4543ef51SXin LI }
234*4543ef51SXin LI 
235*4543ef51SXin LI void
srunner_summarize(SRunner * runner,int verbosity)236*4543ef51SXin LI srunner_summarize(SRunner *runner, int verbosity) {
237cc68614dSXin LI   if (verbosity != CK_SILENT) {
238220ed979SColeman Kane     int passed = runner->nchecks - runner->nfailures;
239220ed979SColeman Kane     double percentage = ((double)passed) / runner->nchecks;
240220ed979SColeman Kane     int display = (int)(percentage * 100);
2416b2c1e49SXin LI     printf("%d%%: Checks: %d, Failed: %d\n", display, runner->nchecks,
2426b2c1e49SXin LI            runner->nfailures);
243220ed979SColeman Kane   }
244220ed979SColeman Kane }
245220ed979SColeman Kane 
246220ed979SColeman Kane void
_fail(const char * file,int line,const char * msg)247*4543ef51SXin LI _fail(const char *file, int line, const char *msg) {
248220ed979SColeman Kane   /* Always print the error message so it isn't lost.  In this case,
249220ed979SColeman Kane      we have a failure, so there's no reason to be quiet about what
250220ed979SColeman Kane      it is.
251220ed979SColeman Kane   */
252cc68614dSXin LI   _check_current_filename = file;
253cc68614dSXin LI   _check_current_lineno = line;
2540a48773fSEric van Gyzen   if (msg != NULL) {
2550a48773fSEric van Gyzen     const int has_newline = (msg[strlen(msg) - 1] == '\n');
2560a48773fSEric van Gyzen     fprintf(stderr, "ERROR: %s%s", msg, has_newline ? "" : "\n");
2570a48773fSEric van Gyzen   }
258220ed979SColeman Kane   longjmp(env, 1);
259220ed979SColeman Kane }
260220ed979SColeman Kane 
261220ed979SColeman Kane int
srunner_ntests_failed(SRunner * runner)2626b2c1e49SXin LI srunner_ntests_failed(SRunner *runner) {
263220ed979SColeman Kane   assert(runner != NULL);
264220ed979SColeman Kane   return runner->nfailures;
265220ed979SColeman Kane }
266220ed979SColeman Kane 
267220ed979SColeman Kane void
srunner_free(SRunner * runner)2686b2c1e49SXin LI srunner_free(SRunner *runner) {
2690a48773fSEric van Gyzen   if (! runner) {
2700a48773fSEric van Gyzen     return;
2710a48773fSEric van Gyzen   }
2720a48773fSEric van Gyzen 
2730a48773fSEric van Gyzen   suite_free(runner->suite);
274220ed979SColeman Kane   free(runner);
275220ed979SColeman Kane }
276