1/*
2 * Copyright (c) 2005-2007 Apple Inc. All Rights Reserved.
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>
32#include <sys/time.h>
33#include <unistd.h>
34#include <AvailabilityMacros.h>
35
36#include "testmore.h"
37#include "testenv.h"
38
39static int test_num = 0;
40static int test_fails = 0;
41static int test_cases = 0;
42static const char *test_plan_file;
43static int test_plan_line=0;
44
45const char *test_directive = NULL;
46const char *test_reason = NULL;
47
48static void fprint_string(FILE *file, CFStringRef string) {
49    UInt8 buf[256];
50    CFRange range = { .location = 0 };
51    range.length = CFStringGetLength(string);
52    while (range.length > 0) {
53        CFIndex bytesUsed = 0;
54        CFIndex converted = CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, false, buf, sizeof(buf), &bytesUsed);
55        fwrite(buf, 1, bytesUsed, file);
56        range.length -= converted;
57        range.location += converted;
58    }
59}
60
61static void cffprint(FILE *file, CFStringRef fmt, ...) CF_FORMAT_FUNCTION(2,0);
62
63static void cffprint(FILE *file, CFStringRef fmt, ...) {
64    va_list args;
65    va_start(args, fmt);
66    CFStringRef line = CFStringCreateWithFormatAndArguments(NULL, NULL, fmt, args);
67    va_end(args);
68    fprint_string(file, line);
69    CFRelease(line);
70}
71
72void test_skip(const char *reason, int how_many, int unless)
73{
74    if (unless)
75        return;
76
77    int done;
78    for (done = 0; done < how_many; ++done)
79        test_ok(1, NULL, "skip", reason, __FILE__, __LINE__, NULL);
80}
81
82void test_bail_out(const char *reason, const char *file, unsigned line)
83{
84    printf("BAIL OUT! (%s at line %u) %s\n", file, line, reason);
85    fflush(stdout);
86    exit(255);
87}
88
89void test_plan_skip_all(const char *reason)
90{
91    if (test_num < test_cases)
92    {
93        test_skip(reason, test_cases - test_num, 0);
94    }
95}
96
97static int test_plan_exit(void)
98{
99    int status = 0;
100    fflush(stdout);
101
102    if (!test_num)
103    {
104        if (test_cases)
105        {
106            fprintf(stderr, "%s:%u: warning: No tests run!\n", test_plan_file, test_plan_line);
107            status = 255;
108        }
109        else
110        {
111            fprintf(stderr, "%s:%u: error: Looks like your test died before it could "
112                    "output anything.\n", test_plan_file, test_plan_line);
113            status = 255;
114        }
115    }
116    else if (test_num < test_cases)
117    {
118        fprintf(stderr, "%s:%u: warning: Looks like you planned %d tests but only ran %d.\n",
119               test_plan_file, test_plan_line, test_cases, test_num);
120        status = test_fails + test_cases - test_num;
121    }
122    else if (test_num > test_cases)
123    {
124        fprintf(stderr, "%s:%u: warning: Looks like you planned %d tests but ran %d extra.\n",
125               test_plan_file, test_plan_line, test_cases, test_num - test_cases);
126        status = test_fails;
127    }
128    else if (test_fails)
129    {
130        fprintf(stderr, "%s:%u: error: Looks like you failed %d tests of %d.\n",
131               test_plan_file, test_plan_line, test_fails, test_cases);
132        status = test_fails;
133    }
134
135    fflush(stderr);
136
137    /* reset the test plan */
138    test_num = 0;
139    test_fails = 0;
140    test_cases = 0;
141
142    return status;
143}
144
145void test_plan_tests(int count, const char *file, unsigned line)
146{
147#if 0
148    if (atexit(test_plan_exit) < 0)
149    {
150        fprintf(stderr, "failed to setup atexit handler: %s\n",
151                strerror(errno));
152        fflush(stderr);
153        exit(255);
154    }
155#endif
156
157	if (test_cases)
158    {
159        fprintf(stderr,
160                "%s:%u: error: You tried to plan twice!\n",
161                file, line);
162
163        fflush(stderr);
164        exit(255);
165    }
166    else
167	{
168        if (!count)
169        {
170            fprintf(stderr, "%s:%u: warning: You said to run 0 tests!  You've got to run "
171                    "something.\n", file, line);
172            fflush(stderr);
173            exit(255);
174        }
175
176        test_plan_file=file;
177        test_plan_line=line;
178
179        test_cases = count;
180		fprintf(stderr, "%s:%u: note: 1..%d\n", file, line, test_cases);
181		fflush(stdout);
182	}
183}
184
185int
186test_diag(const char *directive, const char *reason,
187	const char *file, unsigned line, const char *fmt, ...)
188{
189	int is_todo = directive && !strcmp(directive, "TODO");
190	va_list args;
191
192	va_start(args, fmt);
193
194	if (is_todo)
195	{
196		fputs("# ", stdout);
197		if (fmt)
198			vprintf(fmt, args);
199		fputs("\n", stdout);
200		fflush(stdout);
201	}
202	else
203	{
204		fflush(stdout);
205		fputs("# ", stderr);
206		if (fmt)
207			vfprintf(stderr, fmt, args);
208		fputs("\n", stderr);
209		fflush(stderr);
210	}
211
212	va_end(args);
213
214	return 1;
215}
216
217int
218test_ok(int passed, __attribute((cf_consumed)) CFStringRef description, const char *directive,
219	const char *reason, const char *file, unsigned line,
220	const char *fmt, ...)
221{
222	int is_todo = !passed && directive && !strcmp(directive, "TODO");
223	int is_setup = directive && !is_todo && !strcmp(directive, "SETUP");
224
225	if (is_setup)
226	{
227		if (!passed)
228		{
229			fflush(stdout);
230            cffprint(stderr, CFSTR("# SETUP not ok%s%@%s%s\n"),
231				   description ? " - " : "",
232				   description ? description : CFSTR(""),
233				   reason ? " - " : "",
234				   reason ? reason : "");
235		}
236	}
237	else
238	{
239		if (!test_cases)
240		{
241			atexit((void(*)(void))test_plan_exit);
242			fprintf(stderr, "You tried to run a test without a plan!  "
243					"Gotta have a plan. at %s line %u\n", file, line);
244			fflush(stderr);
245			exit(255);
246		}
247
248		++test_num;
249		if (test_num > test_cases || (!passed && !is_todo))
250			++test_fails;
251
252        /* We only print this when a test fail, unless verbose is enabled */
253        if ((!passed) || (test_verbose > 0)) {
254            cffprint(stderr, CFSTR("%s:%u: note: %sok %d%s%@%s%s%s%s\n"),
255                      file, line, passed ? "" : "not ", test_num,
256                      description ? " - " : "",
257                      description ? description : CFSTR(""),
258                      directive ? " # " : "",
259                      directive ? directive : "",
260                      reason ? " " : "",
261                      reason ? reason : "");
262        }
263    }
264
265    if (passed)
266		fflush(stdout);
267	else
268    {
269		va_list args;
270
271		va_start(args, fmt);
272
273		if (is_todo)
274		{
275/* Enable this to output TODO as warning */
276#if 0
277			printf("%s:%d: warning: Failed (TODO) test\n", file, line);
278			if (fmt)
279				vprintf(fmt, args);
280#endif
281			fflush(stdout);
282		}
283        else
284		{
285			fflush(stdout);
286			fprintf(stderr, "%s:%d: error: Failed test\n", file, line);
287			if (fmt)
288				vfprintf(stderr, fmt, args);
289			fflush(stderr);
290		}
291
292		va_end(args);
293    }
294
295    if (description)
296        CFRelease(description);
297
298    return passed;
299}
300
301
302const char *
303sec_errstr(int err)
304{
305#if 1
306	static int bufnum = 0;
307    static char buf[2][20];
308	bufnum = bufnum ? 0 : 1;
309    sprintf(buf[bufnum], "0x%X", err);
310    return buf[bufnum];
311#else /* !1 */
312    if (err >= errSecErrnoBase && err <= errSecErrnoLimit)
313        return strerror(err - 100000);
314
315#ifdef MAC_OS_X_VERSION_10_4
316    /* AvailabilityMacros.h would only define this if we are on a
317       Tiger or later machine. */
318    extern const char *cssmErrorString(long);
319    return cssmErrorString(err);
320#else /* !defined(MAC_OS_X_VERSION_10_4) */
321    extern const char *_ZN8Security15cssmErrorStringEl(long);
322    return _ZN8Security15cssmErrorStringEl(err);
323#endif /* MAC_OS_X_VERSION_10_4 */
324#endif /* !1 */
325}
326
327/* run one test, described by test, return info in test struct */
328int run_one_test(struct one_test_s *test, int argc, char * const *argv)
329{
330    struct timeval start, stop;
331
332    if(test->entry==NULL) {
333        printf("%s:%d: error: wtf?\n", __FILE__, __LINE__);
334        return -1;
335    }
336
337    gettimeofday(&start, NULL);
338    test->entry(argc, argv);
339    gettimeofday(&stop, NULL);
340
341
342    /* this may overflow... */
343    test->duration=(stop.tv_sec-start.tv_sec)*1000+(stop.tv_usec/1000)-(start.tv_usec/1000);
344    test->failed_tests=test_fails;
345
346    return test_plan_exit();
347 };
348