]>
Commit | Line | Data |
---|---|---|
427c49bc | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2005-2007,2012-2014 Apple Inc. All Rights Reserved. |
427c49bc A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | * | |
23 | * testmore.c | |
24 | */ | |
25 | ||
26 | #include <fcntl.h> | |
27 | #include <stdarg.h> | |
28 | #include <stdlib.h> | |
29 | #include <string.h> | |
30 | #include <sys/stat.h> | |
31 | #include <sys/types.h> | |
427c49bc A |
32 | #include <unistd.h> |
33 | #include <AvailabilityMacros.h> | |
34 | ||
35 | #include "testmore.h" | |
36 | #include "testenv.h" | |
37 | ||
427c49bc | 38 | static int test_fails = 0; |
5c19dc3a A |
39 | static int test_todo_pass = 0; |
40 | static int test_todo = 0; | |
41 | static int test_num = 0; | |
427c49bc | 42 | static int test_cases = 0; |
5c19dc3a A |
43 | static int test_plan_line = 0; |
44 | static const char *test_plan_file = NULL; | |
427c49bc A |
45 | |
46 | const char *test_directive = NULL; | |
47 | const char *test_reason = NULL; | |
48 | ||
49 | static void fprint_string(FILE *file, CFStringRef string) { | |
50 | UInt8 buf[256]; | |
51 | CFRange range = { .location = 0 }; | |
52 | range.length = CFStringGetLength(string); | |
53 | while (range.length > 0) { | |
54 | CFIndex bytesUsed = 0; | |
55 | CFIndex converted = CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, false, buf, sizeof(buf), &bytesUsed); | |
56 | fwrite(buf, 1, bytesUsed, file); | |
57 | range.length -= converted; | |
58 | range.location += converted; | |
59 | } | |
60 | } | |
61 | ||
62 | static void cffprint(FILE *file, CFStringRef fmt, ...) CF_FORMAT_FUNCTION(2,0); | |
63 | ||
d8f41ccd A |
64 | static void cffprint_v(FILE *file, CFStringRef fmt, va_list args); |
65 | static void cffprint_c_v(FILE *file, const char *fmt, va_list args); | |
66 | ||
67 | static void cffprint_v(FILE *file, CFStringRef fmt, va_list args) { | |
68 | CFStringRef line = CFStringCreateWithFormatAndArguments(NULL, NULL, fmt, args); | |
69 | fprint_string(file, line); | |
70 | CFRelease(line); | |
71 | } | |
72 | ||
73 | static void cffprint_c_v(FILE *file, const char *fmt, va_list args) { | |
74 | CFStringRef cffmt = CFStringCreateWithCString(kCFAllocatorDefault, fmt, kCFStringEncodingUTF8); | |
75 | cffprint_v(file, cffmt, args); | |
76 | CFRelease(cffmt); | |
77 | } | |
78 | ||
427c49bc A |
79 | static void cffprint(FILE *file, CFStringRef fmt, ...) { |
80 | va_list args; | |
81 | va_start(args, fmt); | |
d8f41ccd | 82 | cffprint_v(file, fmt, args); |
427c49bc | 83 | va_end(args); |
427c49bc A |
84 | } |
85 | ||
86 | void test_skip(const char *reason, int how_many, int unless) | |
87 | { | |
88 | if (unless) | |
89 | return; | |
90 | ||
91 | int done; | |
92 | for (done = 0; done < how_many; ++done) | |
93 | test_ok(1, NULL, "skip", reason, __FILE__, __LINE__, NULL); | |
94 | } | |
95 | ||
96 | void test_bail_out(const char *reason, const char *file, unsigned line) | |
97 | { | |
d8f41ccd | 98 | fprintf(stdout, "[FAIL] BAIL OUT! (%s at line %u) %s\n", file, line, reason); |
427c49bc A |
99 | fflush(stdout); |
100 | exit(255); | |
101 | } | |
102 | ||
103 | void test_plan_skip_all(const char *reason) | |
104 | { | |
105 | if (test_num < test_cases) | |
106 | { | |
107 | test_skip(reason, test_cases - test_num, 0); | |
108 | } | |
109 | } | |
110 | ||
5c19dc3a A |
111 | static const char *test_plan_name(void) { |
112 | const char *plan_name = strrchr(test_plan_file, '/'); | |
113 | plan_name = plan_name ? plan_name + 1 : test_plan_file; | |
114 | return plan_name; | |
115 | } | |
116 | ||
117 | static int test_plan_pass(void) { | |
118 | if (test_verbose) { | |
119 | const char *name = test_plan_name(); | |
120 | fprintf(stdout, "[BEGIN] %s plan\n[PASS] %s plan\n", name, name); | |
121 | // Update counts for summary | |
122 | //test_num++; | |
123 | //test_cases++; | |
124 | } | |
125 | return 0; | |
126 | } | |
127 | ||
128 | static int test_plan_fail(CFStringRef reason, ...) { | |
129 | const char *name = test_plan_name(); | |
130 | va_list ap; | |
131 | va_start(ap, reason); | |
132 | CFStringRef desc = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, reason, ap); | |
133 | cffprint(stdout, CFSTR("[BEGIN] %s plan\n%@[WARN] %s plan\n"), name, desc, name); | |
134 | CFRelease(desc); | |
135 | // Update counts for summary. We consider test_plan_ok itself an unscheduled testcase for counts. | |
136 | //test_num++; | |
137 | //test_fails++; | |
138 | //test_cases++; | |
139 | return 1; | |
140 | } | |
141 | ||
142 | int test_plan_ok(void) { | |
427c49bc | 143 | int status = 0; |
d8f41ccd | 144 | fflush(stderr); |
5c19dc3a | 145 | const char *name = test_plan_name(); |
427c49bc A |
146 | |
147 | if (!test_num) | |
148 | { | |
149 | if (test_cases) | |
150 | { | |
5c19dc3a | 151 | status = test_plan_fail(CFSTR("No tests run!\n")); |
427c49bc A |
152 | } |
153 | else | |
154 | { | |
5c19dc3a | 155 | status = test_plan_fail(CFSTR("Looks like your test died before it could output anything.\n")); |
427c49bc A |
156 | } |
157 | } | |
158 | else if (test_num < test_cases) | |
159 | { | |
5c19dc3a | 160 | status = test_plan_fail(CFSTR("Looks like you planned %d tests but only ran %d.\n"), test_cases, test_num); |
427c49bc A |
161 | } |
162 | else if (test_num > test_cases) | |
163 | { | |
5c19dc3a A |
164 | status = test_plan_fail(CFSTR("Looks like you planned %d tests but ran %d.\n"), test_cases, test_num); |
165 | } else if (!test_fails) { | |
166 | status = test_plan_pass(); | |
427c49bc | 167 | } |
d8f41ccd | 168 | if (test_fails) |
427c49bc | 169 | { |
5c19dc3a A |
170 | fprintf(stdout, "%s failed %d tests of %d.\n", name, test_fails, test_num); |
171 | status = 1; | |
427c49bc | 172 | } |
d8f41ccd | 173 | fflush(stdout); |
427c49bc | 174 | |
5c19dc3a A |
175 | return status; |
176 | } | |
177 | ||
178 | static void test_plan_reset(void) { | |
427c49bc | 179 | test_fails = 0; |
5c19dc3a A |
180 | test_todo_pass = 0; |
181 | test_todo = 0; | |
182 | test_num = 0; | |
427c49bc | 183 | test_cases = 0; |
5c19dc3a A |
184 | test_plan_file = NULL; |
185 | test_plan_line = 0; | |
427c49bc A |
186 | } |
187 | ||
5c19dc3a A |
188 | void test_plan_final(int *failed, int *todo_pass, int *todo, int *actual, int *planned, const char **file, int *line) { |
189 | if (failed) | |
190 | *failed = test_fails; | |
191 | if (todo_pass) | |
192 | *todo_pass = test_todo_pass; | |
193 | if (todo) | |
194 | *todo = test_todo; | |
195 | if (actual) | |
196 | *actual = test_num; | |
197 | if (planned) | |
198 | *planned = test_cases; | |
199 | if (file) | |
200 | *file = test_plan_file; | |
201 | if (line) | |
202 | *line = test_plan_line; | |
203 | ||
204 | test_plan_reset(); | |
205 | } | |
427c49bc | 206 | |
5c19dc3a | 207 | void test_plan_tests(int count, const char *file, unsigned line) { |
427c49bc A |
208 | if (test_cases) |
209 | { | |
d8f41ccd A |
210 | fprintf(stdout, |
211 | "[FAIL] You tried to plan twice!\n"); | |
427c49bc | 212 | |
d8f41ccd | 213 | fflush(stdout); |
427c49bc A |
214 | exit(255); |
215 | } | |
216 | else | |
217 | { | |
218 | if (!count) | |
219 | { | |
d8f41ccd A |
220 | fprintf(stdout, "[BEGIN] plan_tests\nYou said to run 0 tests! " |
221 | "You've got to run something.\n[WARN] plan_tests\n"); | |
222 | fflush(stdout); | |
427c49bc A |
223 | } |
224 | ||
225 | test_plan_file=file; | |
226 | test_plan_line=line; | |
d8f41ccd | 227 | |
427c49bc | 228 | test_cases = count; |
427c49bc A |
229 | } |
230 | } | |
231 | ||
232 | int | |
233 | test_diag(const char *directive, const char *reason, | |
234 | const char *file, unsigned line, const char *fmt, ...) | |
235 | { | |
236 | int is_todo = directive && !strcmp(directive, "TODO"); | |
237 | va_list args; | |
238 | ||
239 | va_start(args, fmt); | |
240 | ||
241 | if (is_todo) | |
242 | { | |
243 | fputs("# ", stdout); | |
244 | if (fmt) | |
245 | vprintf(fmt, args); | |
246 | fputs("\n", stdout); | |
247 | fflush(stdout); | |
248 | } | |
249 | else | |
250 | { | |
d8f41ccd | 251 | fputs("# ", stdout); |
427c49bc | 252 | if (fmt) |
d8f41ccd A |
253 | vfprintf(stdout, fmt, args); |
254 | fputs("\n", stdout); | |
255 | fflush(stdout); | |
427c49bc A |
256 | } |
257 | ||
258 | va_end(args); | |
259 | ||
260 | return 1; | |
261 | } | |
262 | ||
263 | int | |
264 | test_ok(int passed, __attribute((cf_consumed)) CFStringRef description, const char *directive, | |
265 | const char *reason, const char *file, unsigned line, | |
266 | const char *fmt, ...) | |
267 | { | |
5c19dc3a | 268 | int is_todo = directive && !strcmp(directive, "TODO"); |
427c49bc A |
269 | int is_setup = directive && !is_todo && !strcmp(directive, "SETUP"); |
270 | ||
271 | if (is_setup) | |
272 | { | |
273 | if (!passed) | |
274 | { | |
d8f41ccd A |
275 | fflush(stderr); |
276 | fprintf(stdout, "[BEGIN] SETUP\n"); | |
277 | if (fmt) { | |
278 | va_list args; | |
279 | va_start(args, fmt); | |
280 | cffprint_c_v(stdout, fmt, args); | |
281 | va_end(args); | |
282 | } | |
283 | cffprint(stdout, CFSTR("[WARN] SETUP%s%@%s%s\n"), | |
427c49bc A |
284 | description ? " - " : "", |
285 | description ? description : CFSTR(""), | |
286 | reason ? " - " : "", | |
287 | reason ? reason : ""); | |
d8f41ccd | 288 | fflush(stdout); |
427c49bc A |
289 | } |
290 | } | |
291 | else | |
292 | { | |
293 | if (!test_cases) | |
294 | { | |
5c19dc3a A |
295 | // Make having a plan optional? Commenting out the next 3 lines does - mb |
296 | //fprintf(stdout, "[FAIL] You tried to run a test without a plan! " | |
297 | // "Gotta have a plan. at %s line %u\n", file, line); | |
298 | //fflush(stdout); | |
427c49bc A |
299 | } |
300 | ||
301 | ++test_num; | |
5c19dc3a A |
302 | if (passed) { |
303 | if (is_todo) { | |
304 | test_todo_pass++; | |
305 | } | |
306 | } else if (is_todo) { | |
307 | test_todo++; | |
308 | } else { | |
309 | ++test_fails; | |
310 | } | |
427c49bc | 311 | |
5c19dc3a | 312 | /* We only print this when a test fails, unless verbose is enabled */ |
d8f41ccd A |
313 | if ((!passed && !is_todo) || test_verbose > 0) { |
314 | fflush(stderr); | |
315 | if (test_strict_bats) { | |
316 | cffprint(stdout, CFSTR("[BEGIN] %d%s%@\n"), | |
317 | test_num, | |
318 | description ? " - " : "", | |
319 | description ? description : CFSTR("")); | |
320 | } | |
321 | if (is_todo && passed) { | |
322 | fprintf(stdout, "%s:%d: warning: Unexpectedly passed (TODO) test\n", file, line); | |
323 | } else if (is_todo && !passed) { | |
324 | /* Enable this to output TODO as warning */ | |
325 | fprintf(stdout, "%s:%d: ok: Failed (TODO) test\n", file, line); | |
326 | } else if (!passed) { | |
327 | fprintf(stdout, "%s:%d: error: Failed test\n", file, line); | |
328 | } | |
329 | if (fmt) { | |
330 | va_list args; | |
331 | va_start(args, fmt); | |
332 | cffprint_c_v(stdout, fmt, args); | |
333 | va_end(args); | |
334 | } | |
5c19dc3a | 335 | cffprint(stdout, CFSTR("[%s] %d%s%@%s%s%s%s\n"), passed ? (is_todo ? "PASS" : "PASS") : (is_todo ? "PASS" : "FAIL"), |
d8f41ccd A |
336 | test_num, |
337 | description ? " - " : "", | |
338 | description ? description : CFSTR(""), | |
339 | directive ? " # " : "", | |
340 | directive ? directive : "", | |
341 | reason ? " " : "", | |
342 | reason ? reason : ""); | |
343 | fflush(stdout); | |
427c49bc A |
344 | } |
345 | } | |
346 | ||
427c49bc A |
347 | if (description) |
348 | CFRelease(description); | |
349 | ||
350 | return passed; | |
351 | } | |
352 | ||
5c19dc3a | 353 | // TODO: Move this to testsec.h so that testmore and testenv can be shared |
427c49bc A |
354 | const char * |
355 | sec_errstr(int err) | |
356 | { | |
357 | #if 1 | |
358 | static int bufnum = 0; | |
359 | static char buf[2][20]; | |
360 | bufnum = bufnum ? 0 : 1; | |
361 | sprintf(buf[bufnum], "0x%X", err); | |
362 | return buf[bufnum]; | |
363 | #else /* !1 */ | |
364 | if (err >= errSecErrnoBase && err <= errSecErrnoLimit) | |
365 | return strerror(err - 100000); | |
366 | ||
367 | #ifdef MAC_OS_X_VERSION_10_4 | |
368 | /* AvailabilityMacros.h would only define this if we are on a | |
369 | Tiger or later machine. */ | |
370 | extern const char *cssmErrorString(long); | |
371 | return cssmErrorString(err); | |
372 | #else /* !defined(MAC_OS_X_VERSION_10_4) */ | |
373 | extern const char *_ZN8Security15cssmErrorStringEl(long); | |
374 | return _ZN8Security15cssmErrorStringEl(err); | |
375 | #endif /* MAC_OS_X_VERSION_10_4 */ | |
376 | #endif /* !1 */ | |
377 | } |