This source file includes following definitions.
- _testcase_run_bare
- _testcase_run_forked
- testcase_run_one
- _tinytest_set_flag
- usage
- tinytest_main
- _tinytest_get_verbosity
- _tinytest_set_test_failed
- _tinytest_set_test_skipped
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <assert.h>
30
31 #ifdef TINYTEST_LOCAL
32 #include "tinytest_local.h"
33 #endif
34
35 #ifdef WIN32
36 #include <windows.h>
37 #else
38 #include <sys/types.h>
39 #include <sys/wait.h>
40 #include <unistd.h>
41 #endif
42
43 #if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
44 #if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \
45 __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070)
46
47 #define FORK_BREAKS_GCOV
48 #include <vproc.h>
49 #endif
50 #endif
51
52 #ifndef __GNUC__
53 #define __attribute__(x)
54 #endif
55
56 #include "tinytest.h"
57 #include "tinytest_macros.h"
58
59 #define LONGEST_TEST_NAME 16384
60
61 static int in_tinytest_main = 0;
62 static int n_ok = 0;
63 static int n_bad = 0;
64 static int n_skipped = 0;
65
66 static int opt_forked = 0;
67 static int opt_nofork = 0;
68 static int opt_verbosity = 1;
69 const char *verbosity_flag = "";
70
71 enum outcome { SKIP=2, OK=1, FAIL=0 };
72 static enum outcome cur_test_outcome = 0;
73 const char *cur_test_prefix = NULL;
74
75 const char *cur_test_name = NULL;
76
77 #ifdef WIN32
78
79 static char commandname[MAX_PATH+1];
80 #endif
81
82 static void usage(struct testgroup_t *groups, int list_groups)
83 __attribute__((noreturn));
84
85 static enum outcome
86 _testcase_run_bare(const struct testcase_t *testcase)
87 {
88 void *env = NULL;
89 int outcome;
90 if (testcase->setup) {
91 env = testcase->setup->setup_fn(testcase);
92 if (!env)
93 return FAIL;
94 else if (env == (void*)TT_SKIP)
95 return SKIP;
96 }
97
98 cur_test_outcome = OK;
99 testcase->fn(env);
100 outcome = cur_test_outcome;
101
102 if (testcase->setup) {
103 if (testcase->setup->cleanup_fn(testcase, env) == 0)
104 outcome = FAIL;
105 }
106
107 return outcome;
108 }
109
110 #define MAGIC_EXITCODE 42
111
112 static enum outcome
113 _testcase_run_forked(const struct testgroup_t *group,
114 const struct testcase_t *testcase)
115 {
116 #ifdef WIN32
117
118
119
120
121
122
123
124
125 int ok;
126 char buffer[LONGEST_TEST_NAME+256];
127 STARTUPINFOA si;
128 PROCESS_INFORMATION info;
129 DWORD exitcode;
130
131 if (!in_tinytest_main) {
132 printf("\nERROR. On Windows, _testcase_run_forked must be"
133 " called from within tinytest_main.\n");
134 abort();
135 }
136 if (opt_verbosity>0)
137 printf("[forking] ");
138
139 snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s",
140 commandname, verbosity_flag, group->prefix, testcase->name);
141
142 memset(&si, 0, sizeof(si));
143 memset(&info, 0, sizeof(info));
144 si.cb = sizeof(si);
145
146 ok = CreateProcessA(commandname, buffer, NULL, NULL, 0,
147 0, NULL, NULL, &si, &info);
148 if (!ok) {
149 printf("CreateProcess failed!\n");
150 return 0;
151 }
152 WaitForSingleObject(info.hProcess, INFINITE);
153 GetExitCodeProcess(info.hProcess, &exitcode);
154 CloseHandle(info.hProcess);
155 CloseHandle(info.hThread);
156 if (exitcode == 0)
157 return OK;
158 else if (exitcode == MAGIC_EXITCODE)
159 return SKIP;
160 else
161 return FAIL;
162 #else
163 int outcome_pipe[2];
164 pid_t pid;
165 (void)group;
166
167 if (pipe(outcome_pipe))
168 perror("opening pipe");
169
170 if (opt_verbosity>0)
171 printf("[forking] ");
172 pid = fork();
173 #ifdef FORK_BREAKS_GCOV
174 vproc_transaction_begin(0);
175 #endif
176 if (!pid) {
177
178 int test_r, write_r;
179 char b[1];
180 close(outcome_pipe[0]);
181 test_r = _testcase_run_bare(testcase);
182 assert(0<=(int)test_r && (int)test_r<=2);
183 b[0] = "NYS"[test_r];
184 write_r = (int)write(outcome_pipe[1], b, 1);
185 if (write_r != 1) {
186 perror("write outcome to pipe");
187 exit(1);
188 }
189 exit(0);
190 return FAIL;
191 } else {
192
193 int status, r;
194 char b[1];
195
196
197 close(outcome_pipe[1]);
198 r = (int)read(outcome_pipe[0], b, 1);
199 if (r == 0) {
200 printf("[Lost connection!] ");
201 return 0;
202 } else if (r != 1) {
203 perror("read outcome from pipe");
204 }
205 waitpid(pid, &status, 0);
206 close(outcome_pipe[0]);
207 return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL);
208 }
209 #endif
210 }
211
212 int
213 testcase_run_one(const struct testgroup_t *group,
214 const struct testcase_t *testcase)
215 {
216 enum outcome outcome;
217
218 if (testcase->flags & TT_SKIP) {
219 if (opt_verbosity>0)
220 printf("%s%s: SKIPPED\n",
221 group->prefix, testcase->name);
222 ++n_skipped;
223 return SKIP;
224 }
225
226 if (opt_verbosity>0 && !opt_forked) {
227 printf("%s%s: ", group->prefix, testcase->name);
228 } else {
229 if (opt_verbosity==0) printf(".");
230 cur_test_prefix = group->prefix;
231 cur_test_name = testcase->name;
232 }
233
234 if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) {
235 outcome = _testcase_run_forked(group, testcase);
236 } else {
237 outcome = _testcase_run_bare(testcase);
238 }
239
240 if (outcome == OK) {
241 ++n_ok;
242 if (opt_verbosity>0 && !opt_forked)
243 puts(opt_verbosity==1?"OK":"");
244 } else if (outcome == SKIP) {
245 ++n_skipped;
246 if (opt_verbosity>0 && !opt_forked)
247 puts("SKIPPED");
248 } else {
249 ++n_bad;
250 if (!opt_forked)
251 printf("\n [%s FAILED]\n", testcase->name);
252 }
253
254 if (opt_forked) {
255 exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1));
256 return 1;
257 } else {
258 return (int)outcome;
259 }
260 }
261
262 int
263 _tinytest_set_flag(struct testgroup_t *groups, const char *arg, unsigned long flag)
264 {
265 int i, j;
266 size_t length = LONGEST_TEST_NAME;
267 char fullname[LONGEST_TEST_NAME];
268 int found=0;
269 if (strstr(arg, ".."))
270 length = strstr(arg,"..")-arg;
271 for (i=0; groups[i].prefix; ++i) {
272 for (j=0; groups[i].cases[j].name; ++j) {
273 snprintf(fullname, sizeof(fullname), "%s%s",
274 groups[i].prefix, groups[i].cases[j].name);
275 if (!flag)
276 printf(" %s\n", fullname);
277 if (!strncmp(fullname, arg, length)) {
278 groups[i].cases[j].flags |= flag;
279 ++found;
280 }
281 }
282 }
283 return found;
284 }
285
286 static void
287 usage(struct testgroup_t *groups, int list_groups)
288 {
289 puts("Options are: [--verbose|--quiet|--terse] [--no-fork]");
290 puts(" Specify tests by name, or using a prefix ending with '..'");
291 puts(" To skip a test, list give its name prefixed with a colon.");
292 puts(" Use --list-tests for a list of tests.");
293 if (list_groups) {
294 puts("Known tests are:");
295 _tinytest_set_flag(groups, "..", 0);
296 }
297 exit(0);
298 }
299
300 int
301 tinytest_main(int c, const char **v, struct testgroup_t *groups)
302 {
303 int i, j, n=0;
304
305 #ifdef WIN32
306 const char *sp = strrchr(v[0], '.');
307 const char *extension = "";
308 if (!sp || stricmp(sp, ".exe"))
309 extension = ".exe";
310 snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension);
311 commandname[MAX_PATH]='\0';
312 #endif
313 for (i=1; i<c; ++i) {
314 if (v[i][0] == '-') {
315 if (!strcmp(v[i], "--RUNNING-FORKED")) {
316 opt_forked = 1;
317 } else if (!strcmp(v[i], "--no-fork")) {
318 opt_nofork = 1;
319 } else if (!strcmp(v[i], "--quiet")) {
320 opt_verbosity = -1;
321 verbosity_flag = "--quiet";
322 } else if (!strcmp(v[i], "--verbose")) {
323 opt_verbosity = 2;
324 verbosity_flag = "--verbose";
325 } else if (!strcmp(v[i], "--terse")) {
326 opt_verbosity = 0;
327 verbosity_flag = "--terse";
328 } else if (!strcmp(v[i], "--help")) {
329 usage(groups, 0);
330 } else if (!strcmp(v[i], "--list-tests")) {
331 usage(groups, 1);
332 } else {
333 printf("Unknown option %s. Try --help\n",v[i]);
334 return -1;
335 }
336 } else {
337 const char *test = v[i];
338 int flag = _TT_ENABLED;
339 if (test[0] == ':') {
340 ++test;
341 flag = TT_SKIP;
342 } else {
343 ++n;
344 }
345 if (!_tinytest_set_flag(groups, test, flag)) {
346 printf("No such test as %s!\n", v[i]);
347 return -1;
348 }
349 }
350 }
351 if (!n)
352 _tinytest_set_flag(groups, "..", _TT_ENABLED);
353
354 setvbuf(stdout, NULL, _IONBF, 0);
355
356 ++in_tinytest_main;
357 for (i=0; groups[i].prefix; ++i)
358 for (j=0; groups[i].cases[j].name; ++j)
359 if (groups[i].cases[j].flags & _TT_ENABLED)
360 testcase_run_one(&groups[i],
361 &groups[i].cases[j]);
362
363 --in_tinytest_main;
364
365 if (opt_verbosity==0)
366 puts("");
367
368 if (n_bad)
369 printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad,
370 n_bad+n_ok,n_skipped);
371 else if (opt_verbosity >= 1)
372 printf("%d tests ok. (%d skipped)\n", n_ok, n_skipped);
373
374 return (n_bad == 0) ? 0 : 1;
375 }
376
377 int
378 _tinytest_get_verbosity(void)
379 {
380 return opt_verbosity;
381 }
382
383 void
384 _tinytest_set_test_failed(void)
385 {
386 if (opt_verbosity <= 0 && cur_test_name) {
387 if (opt_verbosity==0) puts("");
388 printf("%s%s: ", cur_test_prefix, cur_test_name);
389 cur_test_name = NULL;
390 }
391 cur_test_outcome = 0;
392 }
393
394 void
395 _tinytest_set_test_skipped(void)
396 {
397 if (cur_test_outcome==OK)
398 cur_test_outcome = SKIP;
399 }
400