xref: /openbsd-src/lib/libexpat/tests/minicheck.c (revision bd8f1dc3b0e01803a74947836eef57849c13acb0)
133ab7b2bSbluhm /* Miniature re-implementation of the "check" library.
22e724bc9Sbluhm 
32e724bc9Sbluhm    This is intended to support just enough of check to run the Expat
42e724bc9Sbluhm    tests.  This interface is based entirely on the portion of the
52e724bc9Sbluhm    check library being used.
62e724bc9Sbluhm                             __  __            _
72e724bc9Sbluhm                          ___\ \/ /_ __   __ _| |_
82e724bc9Sbluhm                         / _ \\  /| '_ \ / _` | __|
92e724bc9Sbluhm                        |  __//  \| |_) | (_| | |_
102e724bc9Sbluhm                         \___/_/\_\ .__/ \__,_|\__|
112e724bc9Sbluhm                                  |_| XML parser
122e724bc9Sbluhm 
1308819b41Sbluhm    Copyright (c) 2004-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
14*bd8f1dc3Sbluhm    Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
1508819b41Sbluhm    Copyright (c) 2017      Rhodri James <rhodri@wildebeest.org.uk>
1608819b41Sbluhm    Copyright (c) 2018      Marco Maggi <marco.maggi-ipsu@poste.it>
1708819b41Sbluhm    Copyright (c) 2019      David Loffredo <loffredo@steptools.com>
18*bd8f1dc3Sbluhm    Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
192e724bc9Sbluhm    Licensed under the MIT license:
202e724bc9Sbluhm 
212e724bc9Sbluhm    Permission is  hereby granted,  free of charge,  to any  person obtaining
222e724bc9Sbluhm    a  copy  of  this  software   and  associated  documentation  files  (the
232e724bc9Sbluhm    "Software"),  to  deal in  the  Software  without restriction,  including
242e724bc9Sbluhm    without  limitation the  rights  to use,  copy,  modify, merge,  publish,
252e724bc9Sbluhm    distribute, sublicense, and/or sell copies of the Software, and to permit
262e724bc9Sbluhm    persons  to whom  the Software  is  furnished to  do so,  subject to  the
272e724bc9Sbluhm    following conditions:
282e724bc9Sbluhm 
292e724bc9Sbluhm    The above copyright  notice and this permission notice  shall be included
302e724bc9Sbluhm    in all copies or substantial portions of the Software.
312e724bc9Sbluhm 
322e724bc9Sbluhm    THE  SOFTWARE  IS  PROVIDED  "AS  IS",  WITHOUT  WARRANTY  OF  ANY  KIND,
332e724bc9Sbluhm    EXPRESS  OR IMPLIED,  INCLUDING  BUT  NOT LIMITED  TO  THE WARRANTIES  OF
342e724bc9Sbluhm    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
352e724bc9Sbluhm    NO EVENT SHALL THE AUTHORS OR  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
362e724bc9Sbluhm    DAMAGES OR  OTHER LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT,  TORT OR
372e724bc9Sbluhm    OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
382e724bc9Sbluhm    USE OR OTHER DEALINGS IN THE SOFTWARE.
3933ab7b2bSbluhm */
4033ab7b2bSbluhm 
41*bd8f1dc3Sbluhm #if defined(NDEBUG)
42*bd8f1dc3Sbluhm #  undef NDEBUG /* because test suite relies on assert(...) at the moment */
43*bd8f1dc3Sbluhm #endif
44*bd8f1dc3Sbluhm 
45*bd8f1dc3Sbluhm #include <stdarg.h>
4633ab7b2bSbluhm #include <stdio.h>
4733ab7b2bSbluhm #include <stdlib.h>
4833ab7b2bSbluhm #include <setjmp.h>
4933ab7b2bSbluhm #include <assert.h>
502e724bc9Sbluhm #include <string.h>
5133ab7b2bSbluhm 
5233ab7b2bSbluhm #include "internal.h" /* for UNUSED_P only */
5333ab7b2bSbluhm #include "minicheck.h"
5433ab7b2bSbluhm 
5533ab7b2bSbluhm Suite *
suite_create(const char * name)5628ce3119Sbluhm suite_create(const char *name) {
5733ab7b2bSbluhm   Suite *suite = (Suite *)calloc(1, sizeof(Suite));
5833ab7b2bSbluhm   if (suite != NULL) {
5933ab7b2bSbluhm     suite->name = name;
6033ab7b2bSbluhm   }
6133ab7b2bSbluhm   return suite;
6233ab7b2bSbluhm }
6333ab7b2bSbluhm 
6433ab7b2bSbluhm TCase *
tcase_create(const char * name)6528ce3119Sbluhm tcase_create(const char *name) {
6633ab7b2bSbluhm   TCase *tc = (TCase *)calloc(1, sizeof(TCase));
6733ab7b2bSbluhm   if (tc != NULL) {
6833ab7b2bSbluhm     tc->name = name;
6933ab7b2bSbluhm   }
7033ab7b2bSbluhm   return tc;
7133ab7b2bSbluhm }
7233ab7b2bSbluhm 
7333ab7b2bSbluhm void
suite_add_tcase(Suite * suite,TCase * tc)7428ce3119Sbluhm suite_add_tcase(Suite *suite, TCase *tc) {
7533ab7b2bSbluhm   assert(suite != NULL);
7633ab7b2bSbluhm   assert(tc != NULL);
7733ab7b2bSbluhm   assert(tc->next_tcase == NULL);
7833ab7b2bSbluhm 
7933ab7b2bSbluhm   tc->next_tcase = suite->tests;
8033ab7b2bSbluhm   suite->tests = tc;
8133ab7b2bSbluhm }
8233ab7b2bSbluhm 
8333ab7b2bSbluhm void
tcase_add_checked_fixture(TCase * tc,tcase_setup_function setup,tcase_teardown_function teardown)8428ce3119Sbluhm tcase_add_checked_fixture(TCase *tc, tcase_setup_function setup,
8528ce3119Sbluhm                           tcase_teardown_function teardown) {
8633ab7b2bSbluhm   assert(tc != NULL);
8733ab7b2bSbluhm   tc->setup = setup;
8833ab7b2bSbluhm   tc->teardown = teardown;
8933ab7b2bSbluhm }
9033ab7b2bSbluhm 
9133ab7b2bSbluhm void
tcase_add_test(TCase * tc,tcase_test_function test)9228ce3119Sbluhm tcase_add_test(TCase *tc, tcase_test_function test) {
9333ab7b2bSbluhm   assert(tc != NULL);
9433ab7b2bSbluhm   if (tc->allocated == tc->ntests) {
9533ab7b2bSbluhm     int nalloc = tc->allocated + 100;
9633ab7b2bSbluhm     size_t new_size = sizeof(tcase_test_function) * nalloc;
97*bd8f1dc3Sbluhm     tcase_test_function *const new_tests
98*bd8f1dc3Sbluhm         = (tcase_test_function *)realloc(tc->tests, new_size);
9933ab7b2bSbluhm     assert(new_tests != NULL);
10033ab7b2bSbluhm     tc->tests = new_tests;
10133ab7b2bSbluhm     tc->allocated = nalloc;
10233ab7b2bSbluhm   }
10333ab7b2bSbluhm   tc->tests[tc->ntests] = test;
10433ab7b2bSbluhm   tc->ntests++;
10533ab7b2bSbluhm }
10633ab7b2bSbluhm 
1075837d4fcSbluhm static void
tcase_free(TCase * tc)10828ce3119Sbluhm tcase_free(TCase *tc) {
1095837d4fcSbluhm   if (! tc) {
1105837d4fcSbluhm     return;
1115837d4fcSbluhm   }
1125837d4fcSbluhm 
1135837d4fcSbluhm   free(tc->tests);
1145837d4fcSbluhm   free(tc);
1155837d4fcSbluhm }
1165837d4fcSbluhm 
1175837d4fcSbluhm static void
suite_free(Suite * suite)11828ce3119Sbluhm suite_free(Suite *suite) {
1195837d4fcSbluhm   if (! suite) {
1205837d4fcSbluhm     return;
1215837d4fcSbluhm   }
1225837d4fcSbluhm 
1235837d4fcSbluhm   while (suite->tests != NULL) {
1245837d4fcSbluhm     TCase *next = suite->tests->next_tcase;
1255837d4fcSbluhm     tcase_free(suite->tests);
1265837d4fcSbluhm     suite->tests = next;
1275837d4fcSbluhm   }
1285837d4fcSbluhm   free(suite);
1295837d4fcSbluhm }
1305837d4fcSbluhm 
13133ab7b2bSbluhm SRunner *
srunner_create(Suite * suite)13228ce3119Sbluhm srunner_create(Suite *suite) {
133*bd8f1dc3Sbluhm   SRunner *const runner = (SRunner *)calloc(1, sizeof(SRunner));
13433ab7b2bSbluhm   if (runner != NULL) {
13533ab7b2bSbluhm     runner->suite = suite;
13633ab7b2bSbluhm   }
13733ab7b2bSbluhm   return runner;
13833ab7b2bSbluhm }
13933ab7b2bSbluhm 
14033ab7b2bSbluhm static jmp_buf env;
14133ab7b2bSbluhm 
142*bd8f1dc3Sbluhm #define SUBTEST_LEN (50) // informative, but not too long
14333ab7b2bSbluhm static char const *_check_current_function = NULL;
144*bd8f1dc3Sbluhm static char _check_current_subtest[SUBTEST_LEN];
14533ab7b2bSbluhm static int _check_current_lineno = -1;
14633ab7b2bSbluhm static char const *_check_current_filename = NULL;
14733ab7b2bSbluhm 
14833ab7b2bSbluhm void
_check_set_test_info(char const * function,char const * filename,int lineno)14928ce3119Sbluhm _check_set_test_info(char const *function, char const *filename, int lineno) {
15033ab7b2bSbluhm   _check_current_function = function;
151*bd8f1dc3Sbluhm   set_subtest("%s", "");
15233ab7b2bSbluhm   _check_current_lineno = lineno;
15333ab7b2bSbluhm   _check_current_filename = filename;
15433ab7b2bSbluhm }
15533ab7b2bSbluhm 
156*bd8f1dc3Sbluhm void
set_subtest(char const * fmt,...)157*bd8f1dc3Sbluhm set_subtest(char const *fmt, ...) {
158*bd8f1dc3Sbluhm   va_list ap;
159*bd8f1dc3Sbluhm   va_start(ap, fmt);
160*bd8f1dc3Sbluhm   vsnprintf(_check_current_subtest, SUBTEST_LEN, fmt, ap);
161*bd8f1dc3Sbluhm   va_end(ap);
162*bd8f1dc3Sbluhm   // replace line feeds with spaces, for nicer error logs
163*bd8f1dc3Sbluhm   for (size_t i = 0; i < SUBTEST_LEN; ++i) {
164*bd8f1dc3Sbluhm     if (_check_current_subtest[i] == '\n') {
165*bd8f1dc3Sbluhm       _check_current_subtest[i] = ' ';
166*bd8f1dc3Sbluhm     }
167*bd8f1dc3Sbluhm   }
168*bd8f1dc3Sbluhm   _check_current_subtest[SUBTEST_LEN - 1] = '\0'; // ensure termination
169*bd8f1dc3Sbluhm }
170*bd8f1dc3Sbluhm 
17133ab7b2bSbluhm static void
handle_success(int verbosity)172326b8ed6Sbluhm handle_success(int verbosity) {
17333ab7b2bSbluhm   if (verbosity >= CK_VERBOSE) {
174326b8ed6Sbluhm     printf("PASS: %s\n", _check_current_function);
175326b8ed6Sbluhm   }
176326b8ed6Sbluhm }
177326b8ed6Sbluhm 
178326b8ed6Sbluhm static void
handle_failure(SRunner * runner,int verbosity,const char * context,const char * phase_info)179*bd8f1dc3Sbluhm handle_failure(SRunner *runner, int verbosity, const char *context,
180*bd8f1dc3Sbluhm                const char *phase_info) {
181326b8ed6Sbluhm   runner->nfailures++;
182326b8ed6Sbluhm   if (verbosity != CK_SILENT) {
183*bd8f1dc3Sbluhm     if (strlen(_check_current_subtest) != 0) {
184*bd8f1dc3Sbluhm       phase_info = _check_current_subtest;
185*bd8f1dc3Sbluhm     }
186*bd8f1dc3Sbluhm     printf("FAIL [%s]: %s (%s at %s:%d)\n", context, _check_current_function,
187*bd8f1dc3Sbluhm            phase_info, _check_current_filename, _check_current_lineno);
18833ab7b2bSbluhm   }
18933ab7b2bSbluhm }
19033ab7b2bSbluhm 
19133ab7b2bSbluhm void
srunner_run_all(SRunner * runner,const char * context,int verbosity)192*bd8f1dc3Sbluhm srunner_run_all(SRunner *runner, const char *context, int verbosity) {
19333ab7b2bSbluhm   Suite *suite;
19428ce3119Sbluhm   TCase *volatile tc;
19533ab7b2bSbluhm   assert(runner != NULL);
19633ab7b2bSbluhm   suite = runner->suite;
19733ab7b2bSbluhm   tc = suite->tests;
19833ab7b2bSbluhm   while (tc != NULL) {
19928ce3119Sbluhm     volatile int i;
20033ab7b2bSbluhm     for (i = 0; i < tc->ntests; ++i) {
20133ab7b2bSbluhm       runner->nchecks++;
202*bd8f1dc3Sbluhm       set_subtest("%s", "");
20333ab7b2bSbluhm 
20433ab7b2bSbluhm       if (tc->setup != NULL) {
20533ab7b2bSbluhm         /* setup */
20633ab7b2bSbluhm         if (setjmp(env)) {
207*bd8f1dc3Sbluhm           handle_failure(runner, verbosity, context, "during setup");
20833ab7b2bSbluhm           continue;
20933ab7b2bSbluhm         }
21033ab7b2bSbluhm         tc->setup();
21133ab7b2bSbluhm       }
21233ab7b2bSbluhm       /* test */
21333ab7b2bSbluhm       if (setjmp(env)) {
214*bd8f1dc3Sbluhm         handle_failure(runner, verbosity, context, "during actual test");
21533ab7b2bSbluhm         continue;
21633ab7b2bSbluhm       }
21733ab7b2bSbluhm       (tc->tests[i])();
218*bd8f1dc3Sbluhm       set_subtest("%s", "");
21933ab7b2bSbluhm 
22033ab7b2bSbluhm       /* teardown */
22133ab7b2bSbluhm       if (tc->teardown != NULL) {
22233ab7b2bSbluhm         if (setjmp(env)) {
223*bd8f1dc3Sbluhm           handle_failure(runner, verbosity, context, "during teardown");
22433ab7b2bSbluhm           continue;
22533ab7b2bSbluhm         }
22633ab7b2bSbluhm         tc->teardown();
22733ab7b2bSbluhm       }
228326b8ed6Sbluhm 
229326b8ed6Sbluhm       handle_success(verbosity);
23033ab7b2bSbluhm     }
23133ab7b2bSbluhm     tc = tc->next_tcase;
23233ab7b2bSbluhm   }
233*bd8f1dc3Sbluhm }
234*bd8f1dc3Sbluhm 
235*bd8f1dc3Sbluhm void
srunner_summarize(SRunner * runner,int verbosity)236*bd8f1dc3Sbluhm srunner_summarize(SRunner *runner, int verbosity) {
237326b8ed6Sbluhm   if (verbosity != CK_SILENT) {
23833ab7b2bSbluhm     int passed = runner->nchecks - runner->nfailures;
23933ab7b2bSbluhm     double percentage = ((double)passed) / runner->nchecks;
24033ab7b2bSbluhm     int display = (int)(percentage * 100);
24128ce3119Sbluhm     printf("%d%%: Checks: %d, Failed: %d\n", display, runner->nchecks,
24228ce3119Sbluhm            runner->nfailures);
24333ab7b2bSbluhm   }
24433ab7b2bSbluhm }
24533ab7b2bSbluhm 
24633ab7b2bSbluhm void
_fail(const char * file,int line,const char * msg)247*bd8f1dc3Sbluhm _fail(const char *file, int line, const char *msg) {
24833ab7b2bSbluhm   /* Always print the error message so it isn't lost.  In this case,
24933ab7b2bSbluhm      we have a failure, so there's no reason to be quiet about what
25033ab7b2bSbluhm      it is.
25133ab7b2bSbluhm   */
252326b8ed6Sbluhm   _check_current_filename = file;
253326b8ed6Sbluhm   _check_current_lineno = line;
2542e724bc9Sbluhm   if (msg != NULL) {
2552e724bc9Sbluhm     const int has_newline = (msg[strlen(msg) - 1] == '\n');
2562e724bc9Sbluhm     fprintf(stderr, "ERROR: %s%s", msg, has_newline ? "" : "\n");
2572e724bc9Sbluhm   }
25833ab7b2bSbluhm   longjmp(env, 1);
25933ab7b2bSbluhm }
26033ab7b2bSbluhm 
26133ab7b2bSbluhm int
srunner_ntests_failed(SRunner * runner)26228ce3119Sbluhm srunner_ntests_failed(SRunner *runner) {
26333ab7b2bSbluhm   assert(runner != NULL);
26433ab7b2bSbluhm   return runner->nfailures;
26533ab7b2bSbluhm }
26633ab7b2bSbluhm 
26733ab7b2bSbluhm void
srunner_free(SRunner * runner)26828ce3119Sbluhm srunner_free(SRunner *runner) {
2695837d4fcSbluhm   if (! runner) {
2705837d4fcSbluhm     return;
2715837d4fcSbluhm   }
2725837d4fcSbluhm 
2735837d4fcSbluhm   suite_free(runner->suite);
27433ab7b2bSbluhm   free(runner);
27533ab7b2bSbluhm }
276