2 * Copyright (c) 2005-2007,2012-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
27 #include <dispatch/dispatch.h>
33 #include <sys/types.h>
35 #include <AvailabilityMacros.h>
40 pthread_mutex_t test_mutex
; // protects the test number variables
41 static int test_fails
= 0;
42 static int test_todo_pass
= 0;
43 static int test_todo
= 0;
44 static int test_num
= 0;
45 static int test_cases
= 0;
46 static int test_plan_line
= 0;
47 static const char *test_plan_file
= NULL
;
49 const char *test_directive
= NULL
;
50 const char *test_reason
= NULL
;
52 static void fprint_string(FILE *file
, CFStringRef string
) {
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
;
65 static void cffprint(FILE *file
, CFStringRef fmt
, ...) CF_FORMAT_FUNCTION(2,3);
67 static void cffprint_v(FILE *file
, CFStringRef fmt
, va_list args
) CF_FORMAT_FUNCTION(2,0);
68 static void cffprint_c_v(FILE *file
, const char *fmt
, va_list args
);
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
);
76 #pragma clang diagnostic push
77 #pragma clang diagnostic ignored "-Wformat-nonliteral"
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
);
85 #pragma clang diagnostic pop
88 static void cffprint(FILE *file
, CFStringRef fmt
, ...) {
91 cffprint_v(file
, fmt
, args
);
95 void test_skip(const char *reason
, int how_many
, int unless
)
101 for (done
= 0; done
< how_many
; ++done
)
102 test_ok(1, NULL
, "skip", reason
, __FILE__
, __LINE__
, NULL
);
105 void test_bail_out(const char *reason
, const char *file
, unsigned line
)
107 fprintf(stdout
, "[FAIL] BAIL OUT! (%s at line %u) %s\n", file
, line
, reason
);
112 void test_plan_skip_all(const char *reason
)
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
);
121 test_skip(reason
, skipN
, 0);
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
;
131 static int test_plan_pass(void) {
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
142 static int test_plan_fail(CFStringRef reason
, ...) CF_FORMAT_FUNCTION(1, 2);
144 static int test_plan_fail(CFStringRef reason
, ...) {
145 const char *name
= test_plan_name();
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
);
151 // Update counts for summary. We consider test_plan_ok itself an unscheduled testcase for counts.
158 int test_plan_ok(void) {
161 const char *name
= test_plan_name();
163 pthread_mutex_lock(&test_mutex
);
168 status
= test_plan_fail(CFSTR("No tests run!\n"));
172 status
= test_plan_fail(CFSTR("Looks like your test died before it could output anything.\n"));
175 else if (test_num
< test_cases
)
177 status
= test_plan_fail(CFSTR("Looks like you planned %d tests but only ran %d.\n"), test_cases
, test_num
);
179 else if (test_num
> test_cases
)
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();
187 fprintf(stdout
, "%s failed %d tests of %d.\n", name
, test_fails
, test_num
);
190 pthread_mutex_unlock(&test_mutex
);
196 // You should hold the test_mutex when you call this.
197 static void test_plan_reset(void) {
203 test_plan_file
= NULL
;
207 void test_plan_final(int *failed
, int *todo_pass
, int *todo
, int *actual
, int *planned
, const char **file
, int *line
) {
208 pthread_mutex_lock(&test_mutex
);
210 *failed
= test_fails
;
212 *todo_pass
= test_todo_pass
;
218 *planned
= test_cases
;
220 *file
= test_plan_file
;
222 *line
= test_plan_line
;
225 pthread_mutex_unlock(&test_mutex
);
228 void test_plan_tests(int count
, const char *file
, unsigned line
) {
232 "[FAIL] You tried to plan twice!\n");
241 fprintf(stdout
, "[BEGIN] plan_tests\nYou said to run 0 tests! "
242 "You've got to run something.\n[WARN] plan_tests\n");
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
);
261 test_diag(const char *directive
, const char *reason
,
262 const char *file
, unsigned line
, const char *fmt
, ...)
264 int is_todo
= directive
&& !strcmp(directive
, "TODO");
281 vfprintf(stdout
, fmt
, args
);
292 test_ok(int passed
, __attribute((cf_consumed
)) CFStringRef CF_CONSUMED description
, const char *directive
,
293 const char *reason
, const char *file
, unsigned line
,
294 const char *fmt
, ...)
296 int is_todo
= directive
&& !strcmp(directive
, "TODO");
297 int is_setup
= directive
&& !is_todo
&& !strcmp(directive
, "SETUP");
304 fprintf(stdout
, "[BEGIN] SETUP\n");
308 cffprint_c_v(stdout
, fmt
, args
);
311 cffprint(stdout
, CFSTR("[WARN] SETUP%s%@%s%s\n"),
312 description
? " - " : "",
313 description
? description
: CFSTR(""),
315 reason
? reason
: "");
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);
329 pthread_mutex_lock(&test_mutex
);
335 } else if (is_todo
) {
341 /* We only print this when a test fails, unless verbose is enabled */
342 if ((!passed
&& !is_todo
) || test_verbose
> 0) {
344 if (test_strict_bats
) {
345 cffprint(stdout
, CFSTR("[BEGIN] %d%s%@\n"),
347 description
? " - " : "",
348 description
? description
: CFSTR(""));
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
);
361 cffprint_c_v(stdout
, fmt
, args
);
364 cffprint(stdout
, CFSTR("[%s] %d%s%@%s%s%s%s\n"), passed
? (is_todo
? "PASS" : "PASS") : (is_todo
? "PASS" : "FAIL"),
366 description
? " - " : "",
367 description
? description
: CFSTR(""),
368 directive
? " # " : "",
369 directive
? directive
: "",
371 reason
? reason
: "");
374 pthread_mutex_unlock(&test_mutex
);
378 CFRelease(description
);
384 // TODO: Move this to testsec.h so that testmore and testenv can be shared
385 static void buf_kill(void* p
) {
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
);
402 uint32_t * switchp
= (uint32_t*) pthread_getspecific(switchkey
);
403 if(switchp
== NULL
) {
404 switchp
= (uint32_t*) malloc(sizeof(uint32_t));
406 pthread_setspecific(switchkey
, switchp
);
411 pthread_key_t current
= (*switchp
) ? buffer0key
: buffer1key
;
412 *switchp
= !(*switchp
);
414 buf
= pthread_getspecific(current
);
416 buf
= (char*) malloc(20);
417 pthread_setspecific(current
, buf
);
420 snprintf(buf
, 20, "0x%X", err
);