1 /* $NetBSD: tinytest.c,v 1.1.1.1 2013/04/11 16:43:32 christos Exp $ */
2 /* tinytest.c -- Copyright 2009-2012 Nick Mathewson
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #include "tinytest_local.h"
39 #include <sys/types.h>
44 #if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
45 #if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \
46 __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070)
47 /* Workaround for a stupid bug in OSX 10.6 */
48 #define FORK_BREAKS_GCOV
54 #define __attribute__(x)
58 #include "tinytest_macros.h"
60 #define LONGEST_TEST_NAME 16384
62 static int in_tinytest_main
= 0; /**< true if we're in tinytest_main().*/
63 static int n_ok
= 0; /**< Number of tests that have passed */
64 static int n_bad
= 0; /**< Number of tests that have failed. */
65 static int n_skipped
= 0; /**< Number of tests that have been skipped. */
67 static int opt_forked
= 0; /**< True iff we're called from inside a win32 fork*/
68 static int opt_nofork
= 0; /**< Suppress calls to fork() for debugging. */
69 static int opt_verbosity
= 1; /**< -==quiet,0==terse,1==normal,2==verbose */
70 const char *verbosity_flag
= "";
72 enum outcome
{ SKIP
=2, OK
=1, FAIL
=0 };
73 static enum outcome cur_test_outcome
= 0;
74 const char *cur_test_prefix
= NULL
; /**< prefix of the current test group */
75 /** Name of the current test, if we haven't logged is yet. Used for --quiet */
76 const char *cur_test_name
= NULL
;
79 /* Copy of argv[0] for win32. */
80 static char commandname
[MAX_PATH
+1];
83 static void usage(struct testgroup_t
*groups
, int list_groups
)
84 __attribute__((noreturn
));
87 _testcase_run_bare(const struct testcase_t
*testcase
)
91 if (testcase
->setup
) {
92 env
= testcase
->setup
->setup_fn(testcase
);
95 else if (env
== (void*)TT_SKIP
)
99 cur_test_outcome
= OK
;
101 outcome
= cur_test_outcome
;
103 if (testcase
->setup
) {
104 if (testcase
->setup
->cleanup_fn(testcase
, env
) == 0)
111 #define MAGIC_EXITCODE 42
114 _testcase_run_forked(const struct testgroup_t
*group
,
115 const struct testcase_t
*testcase
)
118 /* Fork? On Win32? How primitive! We'll do what the smart kids do:
119 we'll invoke our own exe (whose name we recall from the command
120 line) with a command line that tells it to run just the test we
121 want, and this time without forking.
123 (No, threads aren't an option. The whole point of forking is to
124 share no state between tests.)
127 char buffer
[LONGEST_TEST_NAME
+256];
129 PROCESS_INFORMATION info
;
132 if (!in_tinytest_main
) {
133 printf("\nERROR. On Windows, _testcase_run_forked must be"
134 " called from within tinytest_main.\n");
138 printf("[forking] ");
140 snprintf(buffer
, sizeof(buffer
), "%s --RUNNING-FORKED %s %s%s",
141 commandname
, verbosity_flag
, group
->prefix
, testcase
->name
);
143 memset(&si
, 0, sizeof(si
));
144 memset(&info
, 0, sizeof(info
));
147 ok
= CreateProcessA(commandname
, buffer
, NULL
, NULL
, 0,
148 0, NULL
, NULL
, &si
, &info
);
150 printf("CreateProcess failed!\n");
153 WaitForSingleObject(info
.hProcess
, INFINITE
);
154 GetExitCodeProcess(info
.hProcess
, &exitcode
);
155 CloseHandle(info
.hProcess
);
156 CloseHandle(info
.hThread
);
159 else if (exitcode
== MAGIC_EXITCODE
)
168 if (pipe(outcome_pipe
))
169 perror("opening pipe");
172 printf("[forking] ");
174 #ifdef FORK_BREAKS_GCOV
175 vproc_transaction_begin(0);
181 close(outcome_pipe
[0]);
182 test_r
= _testcase_run_bare(testcase
);
183 assert(0<=(int)test_r
&& (int)test_r
<=2);
184 b
[0] = "NYS"[test_r
];
185 write_r
= (int)write(outcome_pipe
[1], b
, 1);
187 perror("write outcome to pipe");
191 return FAIL
; /* unreachable */
196 /* Close this now, so that if the other side closes it,
198 close(outcome_pipe
[1]);
199 r
= (int)read(outcome_pipe
[0], b
, 1);
201 printf("[Lost connection!] ");
204 perror("read outcome from pipe");
206 waitpid(pid
, &status
, 0);
207 close(outcome_pipe
[0]);
208 return b
[0]=='Y' ? OK
: (b
[0]=='S' ? SKIP
: FAIL
);
214 testcase_run_one(const struct testgroup_t
*group
,
215 const struct testcase_t
*testcase
)
217 enum outcome outcome
;
219 if (testcase
->flags
& TT_SKIP
) {
221 printf("%s%s: SKIPPED\n",
222 group
->prefix
, testcase
->name
);
227 if (opt_verbosity
>0 && !opt_forked
) {
228 printf("%s%s: ", group
->prefix
, testcase
->name
);
230 if (opt_verbosity
==0) printf(".");
231 cur_test_prefix
= group
->prefix
;
232 cur_test_name
= testcase
->name
;
235 if ((testcase
->flags
& TT_FORK
) && !(opt_forked
||opt_nofork
)) {
236 outcome
= _testcase_run_forked(group
, testcase
);
238 outcome
= _testcase_run_bare(testcase
);
243 if (opt_verbosity
>0 && !opt_forked
)
244 puts(opt_verbosity
==1?"OK":"");
245 } else if (outcome
== SKIP
) {
247 if (opt_verbosity
>0 && !opt_forked
)
252 printf("\n [%s FAILED]\n", testcase
->name
);
256 exit(outcome
==OK
? 0 : (outcome
==SKIP
?MAGIC_EXITCODE
: 1));
257 return 1; /* unreachable */
264 _tinytest_set_flag(struct testgroup_t
*groups
, const char *arg
, unsigned long flag
)
267 size_t length
= LONGEST_TEST_NAME
;
268 char fullname
[LONGEST_TEST_NAME
];
270 if (strstr(arg
, ".."))
271 length
= strstr(arg
,"..")-arg
;
272 for (i
=0; groups
[i
].prefix
; ++i
) {
273 for (j
=0; groups
[i
].cases
[j
].name
; ++j
) {
274 snprintf(fullname
, sizeof(fullname
), "%s%s",
275 groups
[i
].prefix
, groups
[i
].cases
[j
].name
);
276 if (!flag
) /* Hack! */
277 printf(" %s\n", fullname
);
278 if (!strncmp(fullname
, arg
, length
)) {
279 groups
[i
].cases
[j
].flags
|= flag
;
288 usage(struct testgroup_t
*groups
, int list_groups
)
290 puts("Options are: [--verbose|--quiet|--terse] [--no-fork]");
291 puts(" Specify tests by name, or using a prefix ending with '..'");
292 puts(" To skip a test, list give its name prefixed with a colon.");
293 puts(" Use --list-tests for a list of tests.");
295 puts("Known tests are:");
296 _tinytest_set_flag(groups
, "..", 0);
302 tinytest_main(int c
, const char **v
, struct testgroup_t
*groups
)
307 const char *sp
= strrchr(v
[0], '.');
308 const char *extension
= "";
309 if (!sp
|| stricmp(sp
, ".exe"))
310 extension
= ".exe"; /* Add an exe so CreateProcess will work */
311 snprintf(commandname
, sizeof(commandname
), "%s%s", v
[0], extension
);
312 commandname
[MAX_PATH
]='\0';
314 for (i
=1; i
<c
; ++i
) {
315 if (v
[i
][0] == '-') {
316 if (!strcmp(v
[i
], "--RUNNING-FORKED")) {
318 } else if (!strcmp(v
[i
], "--no-fork")) {
320 } else if (!strcmp(v
[i
], "--quiet")) {
322 verbosity_flag
= "--quiet";
323 } else if (!strcmp(v
[i
], "--verbose")) {
325 verbosity_flag
= "--verbose";
326 } else if (!strcmp(v
[i
], "--terse")) {
328 verbosity_flag
= "--terse";
329 } else if (!strcmp(v
[i
], "--help")) {
331 } else if (!strcmp(v
[i
], "--list-tests")) {
334 printf("Unknown option %s. Try --help\n",v
[i
]);
338 const char *test
= v
[i
];
339 int flag
= _TT_ENABLED
;
340 if (test
[0] == ':') {
346 if (!_tinytest_set_flag(groups
, test
, flag
)) {
347 printf("No such test as %s!\n", v
[i
]);
353 _tinytest_set_flag(groups
, "..", _TT_ENABLED
);
355 setvbuf(stdout
, NULL
, _IONBF
, 0);
358 for (i
=0; groups
[i
].prefix
; ++i
)
359 for (j
=0; groups
[i
].cases
[j
].name
; ++j
)
360 if (groups
[i
].cases
[j
].flags
& _TT_ENABLED
)
361 testcase_run_one(&groups
[i
],
362 &groups
[i
].cases
[j
]);
366 if (opt_verbosity
==0)
370 printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad
,
371 n_bad
+n_ok
,n_skipped
);
372 else if (opt_verbosity
>= 1)
373 printf("%d tests ok. (%d skipped)\n", n_ok
, n_skipped
);
375 return (n_bad
== 0) ? 0 : 1;
379 _tinytest_get_verbosity(void)
381 return opt_verbosity
;
385 _tinytest_set_test_failed(void)
387 if (opt_verbosity
<= 0 && cur_test_name
) {
388 if (opt_verbosity
==0) puts("");
389 printf("%s%s: ", cur_test_prefix
, cur_test_name
);
390 cur_test_name
= NULL
;
392 cur_test_outcome
= 0;
396 _tinytest_set_test_skipped(void)
398 if (cur_test_outcome
==OK
)
399 cur_test_outcome
= SKIP
;