minicheck.c revision 302385
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#include <stdio.h>
9#include <stdlib.h>
10#include <setjmp.h>
11#include <assert.h>
12
13#include "internal.h"  /* for UNUSED_P only */
14#include "minicheck.h"
15
16Suite *
17suite_create(const char *name)
18{
19    Suite *suite = (Suite *) calloc(1, sizeof(Suite));
20    if (suite != NULL) {
21        suite->name = name;
22    }
23    return suite;
24}
25
26TCase *
27tcase_create(const char *name)
28{
29    TCase *tc = (TCase *) calloc(1, sizeof(TCase));
30    if (tc != NULL) {
31        tc->name = name;
32    }
33    return tc;
34}
35
36void
37suite_add_tcase(Suite *suite, TCase *tc)
38{
39    assert(suite != NULL);
40    assert(tc != NULL);
41    assert(tc->next_tcase == NULL);
42
43    tc->next_tcase = suite->tests;
44    suite->tests = tc;
45}
46
47void
48tcase_add_checked_fixture(TCase *tc,
49                          tcase_setup_function setup,
50                          tcase_teardown_function teardown)
51{
52    assert(tc != NULL);
53    tc->setup = setup;
54    tc->teardown = teardown;
55}
56
57void
58tcase_add_test(TCase *tc, tcase_test_function test)
59{
60    assert(tc != NULL);
61    if (tc->allocated == tc->ntests) {
62        int nalloc = tc->allocated + 100;
63        size_t new_size = sizeof(tcase_test_function) * nalloc;
64        tcase_test_function *new_tests = realloc(tc->tests, new_size);
65        assert(new_tests != NULL);
66        if (new_tests != tc->tests) {
67            free(tc->tests);
68            tc->tests = new_tests;
69        }
70        tc->allocated = nalloc;
71    }
72    tc->tests[tc->ntests] = test;
73    tc->ntests++;
74}
75
76SRunner *
77srunner_create(Suite *suite)
78{
79    SRunner *runner = calloc(1, sizeof(SRunner));
80    if (runner != NULL) {
81        runner->suite = suite;
82    }
83    return runner;
84}
85
86static jmp_buf env;
87
88static char const *_check_current_function = NULL;
89static int _check_current_lineno = -1;
90static char const *_check_current_filename = NULL;
91
92void
93_check_set_test_info(char const *function, char const *filename, int lineno)
94{
95    _check_current_function = function;
96    _check_current_lineno = lineno;
97    _check_current_filename = filename;
98}
99
100
101static void
102add_failure(SRunner *runner, int verbosity)
103{
104    runner->nfailures++;
105    if (verbosity >= CK_VERBOSE) {
106        printf("%s:%d: %s\n", _check_current_filename,
107               _check_current_lineno, _check_current_function);
108    }
109}
110
111void
112srunner_run_all(SRunner *runner, int verbosity)
113{
114    Suite *suite;
115    TCase *tc;
116    assert(runner != NULL);
117    suite = runner->suite;
118    tc = suite->tests;
119    while (tc != NULL) {
120        int i;
121        for (i = 0; i < tc->ntests; ++i) {
122            runner->nchecks++;
123
124            if (tc->setup != NULL) {
125                /* setup */
126                if (setjmp(env)) {
127                    add_failure(runner, verbosity);
128                    continue;
129                }
130                tc->setup();
131            }
132            /* test */
133            if (setjmp(env)) {
134                add_failure(runner, verbosity);
135                continue;
136            }
137            (tc->tests[i])();
138
139            /* teardown */
140            if (tc->teardown != NULL) {
141                if (setjmp(env)) {
142                    add_failure(runner, verbosity);
143                    continue;
144                }
145                tc->teardown();
146            }
147        }
148        tc = tc->next_tcase;
149    }
150    if (verbosity) {
151        int passed = runner->nchecks - runner->nfailures;
152        double percentage = ((double) passed) / runner->nchecks;
153        int display = (int) (percentage * 100);
154        printf("%d%%: Checks: %d, Failed: %d\n",
155               display, runner->nchecks, runner->nfailures);
156    }
157}
158
159void
160_fail_unless(int UNUSED_P(condition), const char *UNUSED_P(file), int UNUSED_P(line), const char *msg)
161{
162    /* Always print the error message so it isn't lost.  In this case,
163       we have a failure, so there's no reason to be quiet about what
164       it is.
165    */
166    if (msg != NULL)
167        printf("%s", msg);
168    longjmp(env, 1);
169}
170
171int
172srunner_ntests_failed(SRunner *runner)
173{
174    assert(runner != NULL);
175    return runner->nfailures;
176}
177
178void
179srunner_free(SRunner *runner)
180{
181    free(runner->suite);
182    free(runner);
183}
184