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