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 / _ \\ /| '_ \ / _` | __|
9 | __// \| |_) | (_| | |_
10 \___/_/\_\ .__/ \__,_|\__|
11 |_| XML parser
12
13 Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
14 Copyright (c) 2000-2017 Expat development team
15 Licensed under the MIT license:
16
17 Permission is hereby granted, free of charge, to any person obtaining
18 a copy of this software and associated documentation files (the
19 "Software"), to deal in the Software without restriction, including
20 without limitation the rights to use, copy, modify, merge, publish,
21 distribute, sublicense, and/or sell copies of the Software, and to permit
22 persons to whom the Software is furnished to do so, subject to the
23 following conditions:
24
25 The above copyright notice and this permission notice shall be included
26 in all copies or substantial portions of the Software.
27
28 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
31 NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
32 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
33 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
34 USE OR OTHER DEALINGS IN THE SOFTWARE.
35 */
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <setjmp.h>
40 #include <assert.h>
41 #include <string.h>
42
43 #include "internal.h" /* for UNUSED_P only */
44 #include "minicheck.h"
45
46 Suite *
suite_create(const char * name)47 suite_create(const char *name) {
48 Suite *suite = (Suite *)calloc(1, sizeof(Suite));
49 if (suite != NULL) {
50 suite->name = name;
51 }
52 return suite;
53 }
54
55 TCase *
tcase_create(const char * name)56 tcase_create(const char *name) {
57 TCase *tc = (TCase *)calloc(1, sizeof(TCase));
58 if (tc != NULL) {
59 tc->name = name;
60 }
61 return tc;
62 }
63
64 void
suite_add_tcase(Suite * suite,TCase * tc)65 suite_add_tcase(Suite *suite, TCase *tc) {
66 assert(suite != NULL);
67 assert(tc != NULL);
68 assert(tc->next_tcase == NULL);
69
70 tc->next_tcase = suite->tests;
71 suite->tests = tc;
72 }
73
74 void
tcase_add_checked_fixture(TCase * tc,tcase_setup_function setup,tcase_teardown_function teardown)75 tcase_add_checked_fixture(TCase *tc, tcase_setup_function setup,
76 tcase_teardown_function teardown) {
77 assert(tc != NULL);
78 tc->setup = setup;
79 tc->teardown = teardown;
80 }
81
82 void
tcase_add_test(TCase * tc,tcase_test_function test)83 tcase_add_test(TCase *tc, tcase_test_function test) {
84 assert(tc != NULL);
85 if (tc->allocated == tc->ntests) {
86 int nalloc = tc->allocated + 100;
87 size_t new_size = sizeof(tcase_test_function) * nalloc;
88 tcase_test_function *new_tests = realloc(tc->tests, new_size);
89 assert(new_tests != NULL);
90 tc->tests = new_tests;
91 tc->allocated = nalloc;
92 }
93 tc->tests[tc->ntests] = test;
94 tc->ntests++;
95 }
96
97 static void
tcase_free(TCase * tc)98 tcase_free(TCase *tc) {
99 if (! tc) {
100 return;
101 }
102
103 free(tc->tests);
104 free(tc);
105 }
106
107 static void
suite_free(Suite * suite)108 suite_free(Suite *suite) {
109 if (! suite) {
110 return;
111 }
112
113 while (suite->tests != NULL) {
114 TCase *next = suite->tests->next_tcase;
115 tcase_free(suite->tests);
116 suite->tests = next;
117 }
118 free(suite);
119 }
120
121 SRunner *
srunner_create(Suite * suite)122 srunner_create(Suite *suite) {
123 SRunner *runner = calloc(1, sizeof(SRunner));
124 if (runner != NULL) {
125 runner->suite = suite;
126 }
127 return runner;
128 }
129
130 static jmp_buf env;
131
132 static char const *_check_current_function = NULL;
133 static int _check_current_lineno = -1;
134 static char const *_check_current_filename = NULL;
135
136 void
_check_set_test_info(char const * function,char const * filename,int lineno)137 _check_set_test_info(char const *function, char const *filename, int lineno) {
138 _check_current_function = function;
139 _check_current_lineno = lineno;
140 _check_current_filename = filename;
141 }
142
143 static void
handle_success(int verbosity)144 handle_success(int verbosity) {
145 if (verbosity >= CK_VERBOSE) {
146 printf("PASS: %s\n", _check_current_function);
147 }
148 }
149
150 static void
handle_failure(SRunner * runner,int verbosity,const char * phase_info)151 handle_failure(SRunner *runner, int verbosity, const char *phase_info) {
152 runner->nfailures++;
153 if (verbosity != CK_SILENT) {
154 printf("FAIL: %s (%s at %s:%d)\n", _check_current_function, phase_info,
155 _check_current_filename, _check_current_lineno);
156 }
157 }
158
159 void
srunner_run_all(SRunner * runner,int verbosity)160 srunner_run_all(SRunner *runner, int verbosity) {
161 Suite *suite;
162 TCase *volatile tc;
163 assert(runner != NULL);
164 suite = runner->suite;
165 tc = suite->tests;
166 while (tc != NULL) {
167 volatile int i;
168 for (i = 0; i < tc->ntests; ++i) {
169 runner->nchecks++;
170
171 if (tc->setup != NULL) {
172 /* setup */
173 if (setjmp(env)) {
174 handle_failure(runner, verbosity, "during setup");
175 continue;
176 }
177 tc->setup();
178 }
179 /* test */
180 if (setjmp(env)) {
181 handle_failure(runner, verbosity, "during actual test");
182 continue;
183 }
184 (tc->tests[i])();
185
186 /* teardown */
187 if (tc->teardown != NULL) {
188 if (setjmp(env)) {
189 handle_failure(runner, verbosity, "during teardown");
190 continue;
191 }
192 tc->teardown();
193 }
194
195 handle_success(verbosity);
196 }
197 tc = tc->next_tcase;
198 }
199 if (verbosity != CK_SILENT) {
200 int passed = runner->nchecks - runner->nfailures;
201 double percentage = ((double)passed) / runner->nchecks;
202 int display = (int)(percentage * 100);
203 printf("%d%%: Checks: %d, Failed: %d\n", display, runner->nchecks,
204 runner->nfailures);
205 }
206 }
207
208 void
_fail_unless(int condition,const char * file,int line,const char * msg)209 _fail_unless(int condition, const char *file, int line, const char *msg) {
210 /* Always print the error message so it isn't lost. In this case,
211 we have a failure, so there's no reason to be quiet about what
212 it is.
213 */
214 UNUSED_P(condition);
215 _check_current_filename = file;
216 _check_current_lineno = line;
217 if (msg != NULL) {
218 const int has_newline = (msg[strlen(msg) - 1] == '\n');
219 fprintf(stderr, "ERROR: %s%s", msg, has_newline ? "" : "\n");
220 }
221 longjmp(env, 1);
222 }
223
224 int
srunner_ntests_failed(SRunner * runner)225 srunner_ntests_failed(SRunner *runner) {
226 assert(runner != NULL);
227 return runner->nfailures;
228 }
229
230 void
srunner_free(SRunner * runner)231 srunner_free(SRunner *runner) {
232 if (! runner) {
233 return;
234 }
235
236 suite_free(runner->suite);
237 free(runner);
238 }
239