1251212Spfg/* Miniature re-implementation of the "check" library.
2251212Spfg *
3251212Spfg * This is intended to support just enough of check to run the Expat
4251212Spfg * tests.  This interface is based entirely on the portion of the
5251212Spfg * check library being used.
6251212Spfg */
7251212Spfg
8251212Spfg#include <stdio.h>
9251212Spfg#include <stdlib.h>
10251212Spfg#include <setjmp.h>
11251212Spfg#include <assert.h>
12251212Spfg
13251212Spfg#include "minicheck.h"
14251212Spfg
15251212SpfgSuite *
16251212Spfgsuite_create(char *name)
17251212Spfg{
18251212Spfg    Suite *suite = (Suite *) calloc(1, sizeof(Suite));
19251212Spfg    if (suite != NULL) {
20251212Spfg        suite->name = name;
21251212Spfg    }
22251212Spfg    return suite;
23251212Spfg}
24251212Spfg
25251212SpfgTCase *
26251212Spfgtcase_create(char *name)
27251212Spfg{
28251212Spfg    TCase *tc = (TCase *) calloc(1, sizeof(TCase));
29251212Spfg    if (tc != NULL) {
30251212Spfg        tc->name = name;
31251212Spfg    }
32251212Spfg    return tc;
33251212Spfg}
34251212Spfg
35251212Spfgvoid
36251212Spfgsuite_add_tcase(Suite *suite, TCase *tc)
37251212Spfg{
38251212Spfg    assert(suite != NULL);
39251212Spfg    assert(tc != NULL);
40251212Spfg    assert(tc->next_tcase == NULL);
41251212Spfg
42251212Spfg    tc->next_tcase = suite->tests;
43251212Spfg    suite->tests = tc;
44251212Spfg}
45251212Spfg
46251212Spfgvoid
47251212Spfgtcase_add_checked_fixture(TCase *tc,
48251212Spfg                          tcase_setup_function setup,
49251212Spfg                          tcase_teardown_function teardown)
50251212Spfg{
51251212Spfg    assert(tc != NULL);
52251212Spfg    tc->setup = setup;
53251212Spfg    tc->teardown = teardown;
54251212Spfg}
55251212Spfg
56251212Spfgvoid
57251212Spfgtcase_add_test(TCase *tc, tcase_test_function test)
58251212Spfg{
59251212Spfg    assert(tc != NULL);
60251212Spfg    if (tc->allocated == tc->ntests) {
61251212Spfg        int nalloc = tc->allocated + 100;
62251212Spfg        size_t new_size = sizeof(tcase_test_function) * nalloc;
63251212Spfg        tcase_test_function *new_tests = realloc(tc->tests, new_size);
64251212Spfg        assert(new_tests != NULL);
65251212Spfg        if (new_tests != tc->tests) {
66251212Spfg            free(tc->tests);
67251212Spfg            tc->tests = new_tests;
68251212Spfg        }
69251212Spfg        tc->allocated = nalloc;
70251212Spfg    }
71251212Spfg    tc->tests[tc->ntests] = test;
72251212Spfg    tc->ntests++;
73251212Spfg}
74
75SRunner *
76srunner_create(Suite *suite)
77{
78    SRunner *runner = calloc(1, sizeof(SRunner));
79    if (runner != NULL) {
80        runner->suite = suite;
81    }
82    return runner;
83}
84
85static jmp_buf env;
86
87static char const *_check_current_function = NULL;
88static int _check_current_lineno = -1;
89static char const *_check_current_filename = NULL;
90
91void
92_check_set_test_info(char const *function, char const *filename, int lineno)
93{
94    _check_current_function = function;
95    _check_current_lineno = lineno;
96    _check_current_filename = filename;
97}
98
99
100static void
101add_failure(SRunner *runner, int verbosity)
102{
103    runner->nfailures++;
104    if (verbosity >= CK_VERBOSE) {
105        printf("%s:%d: %s\n", _check_current_filename,
106               _check_current_lineno, _check_current_function);
107    }
108}
109
110void
111srunner_run_all(SRunner *runner, int verbosity)
112{
113    Suite *suite;
114    TCase *tc;
115    assert(runner != NULL);
116    suite = runner->suite;
117    tc = suite->tests;
118    while (tc != NULL) {
119        int i;
120        for (i = 0; i < tc->ntests; ++i) {
121            runner->nchecks++;
122
123            if (tc->setup != NULL) {
124                /* setup */
125                if (setjmp(env)) {
126                    add_failure(runner, verbosity);
127                    continue;
128                }
129                tc->setup();
130            }
131            /* test */
132            if (setjmp(env)) {
133                add_failure(runner, verbosity);
134                continue;
135            }
136            (tc->tests[i])();
137
138            /* teardown */
139            if (tc->teardown != NULL) {
140                if (setjmp(env)) {
141                    add_failure(runner, verbosity);
142                    continue;
143                }
144                tc->teardown();
145            }
146        }
147        tc = tc->next_tcase;
148    }
149    if (verbosity) {
150        int passed = runner->nchecks - runner->nfailures;
151        double percentage = ((double) passed) / runner->nchecks;
152        int display = (int) (percentage * 100);
153        printf("%d%%: Checks: %d, Failed: %d\n",
154               display, runner->nchecks, runner->nfailures);
155    }
156}
157
158void
159_fail_unless(int condition, const char *file, int line, char *msg)
160{
161    /* Always print the error message so it isn't lost.  In this case,
162       we have a failure, so there's no reason to be quiet about what
163       it is.
164    */
165    if (msg != NULL)
166        printf("%s", msg);
167    longjmp(env, 1);
168}
169
170int
171srunner_ntests_failed(SRunner *runner)
172{
173    assert(runner != NULL);
174    return runner->nfailures;
175}
176
177void
178srunner_free(SRunner *runner)
179{
180    free(runner->suite);
181    free(runner);
182}
183