]>
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> | |
fa7225c8 A |
27 | #include <dispatch/dispatch.h> |
28 | #include <pthread.h> | |
427c49bc A |
29 | #include <stdarg.h> |
30 | #include <stdlib.h> | |
31 | #include <string.h> | |
32 | #include <sys/stat.h> | |
33 | #include <sys/types.h> | |
427c49bc A |
34 | #include <unistd.h> |
35 | #include <AvailabilityMacros.h> | |
36 | ||
37 | #include "testmore.h" | |
38 | #include "testenv.h" | |
39 | ||
fa7225c8 | 40 | pthread_mutex_t test_mutex; // protects the test number variables |
427c49bc | 41 | static int test_fails = 0; |
5c19dc3a A |
42 | static int test_todo_pass = 0; |
43 | static int test_todo = 0; | |
44 | static int test_num = 0; | |
427c49bc | 45 | static int test_cases = 0; |
5c19dc3a A |
46 | static int test_plan_line = 0; |
47 | static const char *test_plan_file = NULL; | |
427c49bc A |
48 | |
49 | const char *test_directive = NULL; | |
50 | const char *test_reason = NULL; | |
51 | ||
52 | static void fprint_string(FILE *file, CFStringRef string) { | |
53 | UInt8 buf[256]; | |
54 | CFRange range = { .location = 0 }; | |
55 | range.length = CFStringGetLength(string); | |
56 | while (range.length > 0) { | |
57 | CFIndex bytesUsed = 0; | |
58 | CFIndex converted = CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, false, buf, sizeof(buf), &bytesUsed); | |
59 | fwrite(buf, 1, bytesUsed, file); | |
60 | range.length -= converted; | |
61 | range.location += converted; | |
62 | } | |
63 | } | |
64 | ||
866f8763 | 65 | static void cffprint(FILE *file, CFStringRef fmt, ...) CF_FORMAT_FUNCTION(2,3); |
427c49bc | 66 | |
866f8763 | 67 | static void cffprint_v(FILE *file, CFStringRef fmt, va_list args) CF_FORMAT_FUNCTION(2,0); |
d8f41ccd A |
68 | static void cffprint_c_v(FILE *file, const char *fmt, va_list args); |
69 | ||
70 | static void cffprint_v(FILE *file, CFStringRef fmt, va_list args) { | |
71 | CFStringRef line = CFStringCreateWithFormatAndArguments(NULL, NULL, fmt, args); | |
72 | fprint_string(file, line); | |
73 | CFRelease(line); | |
74 | } | |
75 | ||
866f8763 A |
76 | #pragma clang diagnostic push |
77 | #pragma clang diagnostic ignored "-Wformat-nonliteral" | |
78 | ||
d8f41ccd A |
79 | static void cffprint_c_v(FILE *file, const char *fmt, va_list args) { |
80 | CFStringRef cffmt = CFStringCreateWithCString(kCFAllocatorDefault, fmt, kCFStringEncodingUTF8); | |
81 | cffprint_v(file, cffmt, args); | |
82 | CFRelease(cffmt); | |
83 | } | |
84 | ||
866f8763 A |
85 | #pragma clang diagnostic pop |
86 | ||
87 | ||
427c49bc A |
88 | static void cffprint(FILE *file, CFStringRef fmt, ...) { |
89 | va_list args; | |
90 | va_start(args, fmt); | |
d8f41ccd | 91 | cffprint_v(file, fmt, args); |
427c49bc | 92 | va_end(args); |
427c49bc A |
93 | } |
94 | ||
95 | void test_skip(const char *reason, int how_many, int unless) | |
96 | { | |
97 | if (unless) | |
98 | return; | |
99 | ||
100 | int done; | |
101 | for (done = 0; done < how_many; ++done) | |
102 | test_ok(1, NULL, "skip", reason, __FILE__, __LINE__, NULL); | |
103 | } | |
104 | ||
105 | void test_bail_out(const char *reason, const char *file, unsigned line) | |
106 | { | |
d8f41ccd | 107 | fprintf(stdout, "[FAIL] BAIL OUT! (%s at line %u) %s\n", file, line, reason); |
427c49bc A |
108 | fflush(stdout); |
109 | exit(255); | |
110 | } | |
111 | ||
112 | void test_plan_skip_all(const char *reason) | |
113 | { | |
fa7225c8 A |
114 | // Not super thread-safe. Don't test_plan_skip_all from multiple threads simultaneously. |
115 | pthread_mutex_lock(&test_mutex); | |
116 | int skipN = test_cases - test_num; | |
117 | pthread_mutex_unlock(&test_mutex); | |
118 | ||
119 | if (skipN > 0) | |
427c49bc | 120 | { |
fa7225c8 | 121 | test_skip(reason, skipN, 0); |
427c49bc A |
122 | } |
123 | } | |
124 | ||
5c19dc3a A |
125 | static const char *test_plan_name(void) { |
126 | const char *plan_name = strrchr(test_plan_file, '/'); | |
127 | plan_name = plan_name ? plan_name + 1 : test_plan_file; | |
128 | return plan_name; | |
129 | } | |
130 | ||
131 | static int test_plan_pass(void) { | |
132 | if (test_verbose) { | |
133 | const char *name = test_plan_name(); | |
134 | fprintf(stdout, "[BEGIN] %s plan\n[PASS] %s plan\n", name, name); | |
135 | // Update counts for summary | |
136 | //test_num++; | |
137 | //test_cases++; | |
138 | } | |
139 | return 0; | |
140 | } | |
141 | ||
866f8763 A |
142 | static int test_plan_fail(CFStringRef reason, ...) CF_FORMAT_FUNCTION(1, 2); |
143 | ||
5c19dc3a A |
144 | static int test_plan_fail(CFStringRef reason, ...) { |
145 | const char *name = test_plan_name(); | |
146 | va_list ap; | |
147 | va_start(ap, reason); | |
148 | CFStringRef desc = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, reason, ap); | |
149 | cffprint(stdout, CFSTR("[BEGIN] %s plan\n%@[WARN] %s plan\n"), name, desc, name); | |
150 | CFRelease(desc); | |
151 | // Update counts for summary. We consider test_plan_ok itself an unscheduled testcase for counts. | |
152 | //test_num++; | |
153 | //test_fails++; | |
154 | //test_cases++; | |
155 | return 1; | |
156 | } | |
157 | ||
158 | int test_plan_ok(void) { | |
427c49bc | 159 | int status = 0; |
d8f41ccd | 160 | fflush(stderr); |
5c19dc3a | 161 | const char *name = test_plan_name(); |
427c49bc | 162 | |
fa7225c8 | 163 | pthread_mutex_lock(&test_mutex); |
427c49bc A |
164 | if (!test_num) |
165 | { | |
166 | if (test_cases) | |
167 | { | |
5c19dc3a | 168 | status = test_plan_fail(CFSTR("No tests run!\n")); |
427c49bc A |
169 | } |
170 | else | |
171 | { | |
5c19dc3a | 172 | status = test_plan_fail(CFSTR("Looks like your test died before it could output anything.\n")); |
427c49bc A |
173 | } |
174 | } | |
175 | else if (test_num < test_cases) | |
176 | { | |
5c19dc3a | 177 | status = test_plan_fail(CFSTR("Looks like you planned %d tests but only ran %d.\n"), test_cases, test_num); |
427c49bc A |
178 | } |
179 | else if (test_num > test_cases) | |
180 | { | |
5c19dc3a A |
181 | status = test_plan_fail(CFSTR("Looks like you planned %d tests but ran %d.\n"), test_cases, test_num); |
182 | } else if (!test_fails) { | |
183 | status = test_plan_pass(); | |
427c49bc | 184 | } |
d8f41ccd | 185 | if (test_fails) |
427c49bc | 186 | { |
5c19dc3a A |
187 | fprintf(stdout, "%s failed %d tests of %d.\n", name, test_fails, test_num); |
188 | status = 1; | |
427c49bc | 189 | } |
fa7225c8 | 190 | pthread_mutex_unlock(&test_mutex); |
d8f41ccd | 191 | fflush(stdout); |
427c49bc | 192 | |
5c19dc3a A |
193 | return status; |
194 | } | |
195 | ||
fa7225c8 | 196 | // You should hold the test_mutex when you call this. |
5c19dc3a | 197 | static void test_plan_reset(void) { |
427c49bc | 198 | test_fails = 0; |
5c19dc3a A |
199 | test_todo_pass = 0; |
200 | test_todo = 0; | |
201 | test_num = 0; | |
427c49bc | 202 | test_cases = 0; |
5c19dc3a A |
203 | test_plan_file = NULL; |
204 | test_plan_line = 0; | |
427c49bc A |
205 | } |
206 | ||
5c19dc3a | 207 | void test_plan_final(int *failed, int *todo_pass, int *todo, int *actual, int *planned, const char **file, int *line) { |
fa7225c8 | 208 | pthread_mutex_lock(&test_mutex); |
5c19dc3a A |
209 | if (failed) |
210 | *failed = test_fails; | |
211 | if (todo_pass) | |
212 | *todo_pass = test_todo_pass; | |
213 | if (todo) | |
214 | *todo = test_todo; | |
215 | if (actual) | |
216 | *actual = test_num; | |
217 | if (planned) | |
218 | *planned = test_cases; | |
219 | if (file) | |
220 | *file = test_plan_file; | |
221 | if (line) | |
222 | *line = test_plan_line; | |
223 | ||
224 | test_plan_reset(); | |
fa7225c8 | 225 | pthread_mutex_unlock(&test_mutex); |
5c19dc3a | 226 | } |
427c49bc | 227 | |
5c19dc3a | 228 | void test_plan_tests(int count, const char *file, unsigned line) { |
427c49bc A |
229 | if (test_cases) |
230 | { | |
d8f41ccd A |
231 | fprintf(stdout, |
232 | "[FAIL] You tried to plan twice!\n"); | |
427c49bc | 233 | |
d8f41ccd | 234 | fflush(stdout); |
427c49bc A |
235 | exit(255); |
236 | } | |
237 | else | |
238 | { | |
239 | if (!count) | |
240 | { | |
d8f41ccd A |
241 | fprintf(stdout, "[BEGIN] plan_tests\nYou said to run 0 tests! " |
242 | "You've got to run something.\n[WARN] plan_tests\n"); | |
243 | fflush(stdout); | |
427c49bc A |
244 | } |
245 | ||
246 | test_plan_file=file; | |
247 | test_plan_line=line; | |
d8f41ccd | 248 | |
427c49bc | 249 | test_cases = count; |
fa7225c8 A |
250 | |
251 | static dispatch_once_t onceToken; | |
252 | dispatch_once(&onceToken, ^{ | |
253 | if(pthread_mutex_init(&test_mutex, NULL) != 0) { | |
254 | fprintf(stdout, "Failed to initialize mutex: %d\n", errno); | |
255 | } | |
256 | }); | |
427c49bc A |
257 | } |
258 | } | |
259 | ||
260 | int | |
261 | test_diag(const char *directive, const char *reason, | |
262 | const char *file, unsigned line, const char *fmt, ...) | |
263 | { | |
264 | int is_todo = directive && !strcmp(directive, "TODO"); | |
265 | va_list args; | |
266 | ||
267 | va_start(args, fmt); | |
268 | ||
269 | if (is_todo) | |
270 | { | |
271 | fputs("# ", stdout); | |
272 | if (fmt) | |
273 | vprintf(fmt, args); | |
274 | fputs("\n", stdout); | |
275 | fflush(stdout); | |
276 | } | |
277 | else | |
278 | { | |
d8f41ccd | 279 | fputs("# ", stdout); |
427c49bc | 280 | if (fmt) |
d8f41ccd A |
281 | vfprintf(stdout, fmt, args); |
282 | fputs("\n", stdout); | |
283 | fflush(stdout); | |
427c49bc A |
284 | } |
285 | ||
286 | va_end(args); | |
287 | ||
288 | return 1; | |
289 | } | |
290 | ||
291 | int | |
6b200bc3 | 292 | test_ok(int passed, __attribute((cf_consumed)) CFStringRef CF_CONSUMED description, const char *directive, |
427c49bc A |
293 | const char *reason, const char *file, unsigned line, |
294 | const char *fmt, ...) | |
295 | { | |
5c19dc3a | 296 | int is_todo = directive && !strcmp(directive, "TODO"); |
427c49bc A |
297 | int is_setup = directive && !is_todo && !strcmp(directive, "SETUP"); |
298 | ||
299 | if (is_setup) | |
300 | { | |
301 | if (!passed) | |
302 | { | |
d8f41ccd A |
303 | fflush(stderr); |
304 | fprintf(stdout, "[BEGIN] SETUP\n"); | |
305 | if (fmt) { | |
306 | va_list args; | |
307 | va_start(args, fmt); | |
308 | cffprint_c_v(stdout, fmt, args); | |
309 | va_end(args); | |
310 | } | |
311 | cffprint(stdout, CFSTR("[WARN] SETUP%s%@%s%s\n"), | |
427c49bc A |
312 | description ? " - " : "", |
313 | description ? description : CFSTR(""), | |
314 | reason ? " - " : "", | |
315 | reason ? reason : ""); | |
d8f41ccd | 316 | fflush(stdout); |
427c49bc A |
317 | } |
318 | } | |
319 | else | |
320 | { | |
321 | if (!test_cases) | |
322 | { | |
5c19dc3a A |
323 | // Make having a plan optional? Commenting out the next 3 lines does - mb |
324 | //fprintf(stdout, "[FAIL] You tried to run a test without a plan! " | |
325 | // "Gotta have a plan. at %s line %u\n", file, line); | |
326 | //fflush(stdout); | |
427c49bc A |
327 | } |
328 | ||
fa7225c8 | 329 | pthread_mutex_lock(&test_mutex); |
427c49bc | 330 | ++test_num; |
5c19dc3a A |
331 | if (passed) { |
332 | if (is_todo) { | |
333 | test_todo_pass++; | |
334 | } | |
335 | } else if (is_todo) { | |
336 | test_todo++; | |
337 | } else { | |
338 | ++test_fails; | |
339 | } | |
427c49bc | 340 | |
5c19dc3a | 341 | /* We only print this when a test fails, unless verbose is enabled */ |
d8f41ccd A |
342 | if ((!passed && !is_todo) || test_verbose > 0) { |
343 | fflush(stderr); | |
344 | if (test_strict_bats) { | |
345 | cffprint(stdout, CFSTR("[BEGIN] %d%s%@\n"), | |
346 | test_num, | |
347 | description ? " - " : "", | |
348 | description ? description : CFSTR("")); | |
349 | } | |
350 | if (is_todo && passed) { | |
351 | fprintf(stdout, "%s:%d: warning: Unexpectedly passed (TODO) test\n", file, line); | |
352 | } else if (is_todo && !passed) { | |
353 | /* Enable this to output TODO as warning */ | |
354 | fprintf(stdout, "%s:%d: ok: Failed (TODO) test\n", file, line); | |
355 | } else if (!passed) { | |
356 | fprintf(stdout, "%s:%d: error: Failed test\n", file, line); | |
357 | } | |
358 | if (fmt) { | |
359 | va_list args; | |
360 | va_start(args, fmt); | |
361 | cffprint_c_v(stdout, fmt, args); | |
362 | va_end(args); | |
363 | } | |
5c19dc3a | 364 | cffprint(stdout, CFSTR("[%s] %d%s%@%s%s%s%s\n"), passed ? (is_todo ? "PASS" : "PASS") : (is_todo ? "PASS" : "FAIL"), |
d8f41ccd A |
365 | test_num, |
366 | description ? " - " : "", | |
367 | description ? description : CFSTR(""), | |
368 | directive ? " # " : "", | |
369 | directive ? directive : "", | |
370 | reason ? " " : "", | |
371 | reason ? reason : ""); | |
372 | fflush(stdout); | |
427c49bc | 373 | } |
fa7225c8 | 374 | pthread_mutex_unlock(&test_mutex); |
427c49bc A |
375 | } |
376 | ||
427c49bc A |
377 | if (description) |
378 | CFRelease(description); | |
379 | ||
380 | return passed; | |
381 | } | |
382 | ||
fa7225c8 | 383 | |
5c19dc3a | 384 | // TODO: Move this to testsec.h so that testmore and testenv can be shared |
fa7225c8 A |
385 | static void buf_kill(void* p) { |
386 | free(p); | |
387 | } | |
388 | ||
427c49bc A |
389 | const char * |
390 | sec_errstr(int err) | |
391 | { | |
fa7225c8 A |
392 | static pthread_key_t buffer0key; |
393 | static pthread_key_t buffer1key; | |
394 | static pthread_key_t switchkey; | |
395 | static dispatch_once_t onceToken; | |
396 | dispatch_once(&onceToken, ^{ | |
397 | pthread_key_create(&buffer0key, buf_kill); | |
398 | pthread_key_create(&buffer1key, buf_kill); | |
399 | pthread_key_create(&switchkey, buf_kill); | |
400 | }); | |
401 | ||
402 | uint32_t * switchp = (uint32_t*) pthread_getspecific(switchkey); | |
403 | if(switchp == NULL) { | |
404 | switchp = (uint32_t*) malloc(sizeof(uint32_t)); | |
405 | *switchp = 0; | |
406 | pthread_setspecific(switchkey, switchp); | |
407 | } | |
408 | ||
409 | char* buf = NULL; | |
410 | ||
411 | pthread_key_t current = (*switchp) ? buffer0key : buffer1key; | |
412 | *switchp = !(*switchp); | |
413 | ||
414 | buf = pthread_getspecific(current); | |
415 | if(buf == NULL) { | |
416 | buf = (char*) malloc(20); | |
417 | pthread_setspecific(current, buf); | |
418 | } | |
419 | ||
420 | snprintf(buf, 20, "0x%X", err); | |
421 | return buf; | |
427c49bc | 422 | } |