1 /* tinytest.c -- Copyright 2009-2010 Nick Mathewson
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions
6 * 1. Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * 2. Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 * 3. The name of the author may not be used to endorse or promote products
12 * derived from this software without specific prior written permission.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "tinytest_local.h"
38 #include <sys/types.h>
44 #define __attribute__(x)
48 #include "tinytest_macros.h"
50 #define LONGEST_TEST_NAME 16384
52 static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/
53 static int n_ok = 0; /**< Number of tests that have passed */
54 static int n_bad = 0; /**< Number of tests that have failed. */
55 static int n_skipped = 0; /**< Number of tests that have been skipped. */
57 static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/
58 static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */
59 static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */
60 const char *verbosity_flag = "";
62 enum outcome { SKIP=2, OK=1, FAIL=0 };
63 static enum outcome cur_test_outcome = 0;
64 const char *cur_test_prefix = NULL; /**< prefix of the current test group */
65 /** Name of the current test, if we haven't logged is yet. Used for --quiet */
66 const char *cur_test_name = NULL;
69 /** Pointer to argv[0] for win32. */
70 static const char *commandname = NULL;
73 static void usage(struct testgroup_t *groups, int list_groups)
74 __attribute__((noreturn));
77 _testcase_run_bare(const struct testcase_t *testcase)
81 if (testcase->setup) {
82 env = testcase->setup->setup_fn(testcase);
85 else if (env == (void*)TT_SKIP)
89 cur_test_outcome = OK;
91 outcome = cur_test_outcome;
93 if (testcase->setup) {
94 if (testcase->setup->cleanup_fn(testcase, env) == 0)
101 #define MAGIC_EXITCODE 42
104 _testcase_run_forked(const struct testgroup_t *group,
105 const struct testcase_t *testcase)
108 /* Fork? On Win32? How primitive! We'll do what the smart kids do:
109 we'll invoke our own exe (whose name we recall from the command
110 line) with a command line that tells it to run just the test we
111 want, and this time without forking.
113 (No, threads aren't an option. The whole point of forking is to
114 share no state between tests.)
117 char buffer[LONGEST_TEST_NAME+256];
119 PROCESS_INFORMATION info;
122 if (!in_tinytest_main) {
123 printf("\nERROR. On Windows, _testcase_run_forked must be"
124 " called from within tinytest_main.\n");
128 printf("[forking] ");
130 snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s",
131 commandname, verbosity_flag, group->prefix, testcase->name);
133 memset(&si, 0, sizeof(si));
134 memset(&info, 0, sizeof(info));
137 ok = CreateProcessA(commandname, buffer, NULL, NULL, 0,
138 0, NULL, NULL, &si, &info);
140 printf("CreateProcess failed!\n");
143 WaitForSingleObject(info.hProcess, INFINITE);
144 GetExitCodeProcess(info.hProcess, &exitcode);
145 CloseHandle(info.hProcess);
146 CloseHandle(info.hThread);
149 else if (exitcode == MAGIC_EXITCODE)
158 if (pipe(outcome_pipe))
159 perror("opening pipe");
162 printf("[forking] ");
168 close(outcome_pipe[0]);
169 test_r = _testcase_run_bare(testcase);
170 assert(0<=(int)test_r && (int)test_r<=2);
171 b[0] = "NYS"[test_r];
172 write_r = (int)write(outcome_pipe[1], b, 1);
174 perror("write outcome to pipe");
178 return FAIL; /* unreachable */
183 /* Close this now, so that if the other side closes it,
185 close(outcome_pipe[1]);
186 r = (int)read(outcome_pipe[0], b, 1);
188 printf("[Lost connection!] ");
191 perror("read outcome from pipe");
193 waitpid(pid, &status, 0);
194 close(outcome_pipe[0]);
195 return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL);
201 testcase_run_one(const struct testgroup_t *group,
202 const struct testcase_t *testcase)
204 enum outcome outcome;
206 if (testcase->flags & TT_SKIP) {
208 printf("%s%s: SKIPPED\n",
209 group->prefix, testcase->name);
214 if (opt_verbosity>0 && !opt_forked) {
215 printf("%s%s: ", group->prefix, testcase->name);
217 if (opt_verbosity==0) printf(".");
218 cur_test_prefix = group->prefix;
219 cur_test_name = testcase->name;
222 if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) {
223 outcome = _testcase_run_forked(group, testcase);
225 outcome = _testcase_run_bare(testcase);
230 if (opt_verbosity>0 && !opt_forked)
231 puts(opt_verbosity==1?"OK":"");
232 } else if (outcome == SKIP) {
234 if (opt_verbosity>0 && !opt_forked)
239 printf("\n [%s FAILED]\n", testcase->name);
243 exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1));
244 return 1; /* unreachable */
251 _tinytest_set_flag(struct testgroup_t *groups, const char *arg, unsigned long flag)
254 size_t length = LONGEST_TEST_NAME;
255 char fullname[LONGEST_TEST_NAME];
257 if (strstr(arg, ".."))
258 length = strstr(arg,"..")-arg;
259 for (i=0; groups[i].prefix; ++i) {
260 for (j=0; groups[i].cases[j].name; ++j) {
261 snprintf(fullname, sizeof(fullname), "%s%s",
262 groups[i].prefix, groups[i].cases[j].name);
263 if (!flag) /* Hack! */
264 printf(" %s\n", fullname);
265 if (!strncmp(fullname, arg, length)) {
266 groups[i].cases[j].flags |= flag;
275 usage(struct testgroup_t *groups, int list_groups)
277 puts("Options are: [--verbose|--quiet|--terse] [--no-fork]");
278 puts(" Specify tests by name, or using a prefix ending with '..'");
279 puts(" To skip a test, list give its name prefixed with a colon.");
280 puts(" Use --list-tests for a list of tests.");
282 puts("Known tests are:");
283 _tinytest_set_flag(groups, "..", 0);
289 tinytest_main(int c, const char **v, struct testgroup_t *groups)
296 for (i=1; i<c; ++i) {
297 if (v[i][0] == '-') {
298 if (!strcmp(v[i], "--RUNNING-FORKED")) {
300 } else if (!strcmp(v[i], "--no-fork")) {
302 } else if (!strcmp(v[i], "--quiet")) {
304 verbosity_flag = "--quiet";
305 } else if (!strcmp(v[i], "--verbose")) {
307 verbosity_flag = "--verbose";
308 } else if (!strcmp(v[i], "--terse")) {
310 verbosity_flag = "--terse";
311 } else if (!strcmp(v[i], "--help")) {
313 } else if (!strcmp(v[i], "--list-tests")) {
316 printf("Unknown option %s. Try --help\n",v[i]);
320 const char *test = v[i];
321 int flag = _TT_ENABLED;
322 if (test[0] == ':') {
328 if (!_tinytest_set_flag(groups, test, flag)) {
329 printf("No such test as %s!\n", v[i]);
335 _tinytest_set_flag(groups, "..", _TT_ENABLED);
337 setvbuf(stdout, NULL, _IONBF, 0);
340 for (i=0; groups[i].prefix; ++i)
341 for (j=0; groups[i].cases[j].name; ++j)
342 if (groups[i].cases[j].flags & _TT_ENABLED)
343 testcase_run_one(&groups[i],
344 &groups[i].cases[j]);
348 if (opt_verbosity==0)
352 printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad,
353 n_bad+n_ok,n_skipped);
354 else if (opt_verbosity >= 1)
355 printf("%d tests ok. (%d skipped)\n", n_ok, n_skipped);
357 return (n_bad == 0) ? 0 : 1;
361 _tinytest_get_verbosity(void)
363 return opt_verbosity;
367 _tinytest_set_test_failed(void)
369 if (opt_verbosity <= 0 && cur_test_name) {
370 if (opt_verbosity==0) puts("");
371 printf("%s%s: ", cur_test_prefix, cur_test_name);
372 cur_test_name = NULL;
374 cur_test_outcome = 0;
378 _tinytest_set_test_skipped(void)
380 if (cur_test_outcome==OK)
381 cur_test_outcome = SKIP;