#include <fcntl.h>
#include <dispatch/dispatch.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <AvailabilityMacros.h>
#include "testmore.h"
#include "testenv.h"
pthread_mutex_t test_mutex; static int test_fails = 0;
static int test_todo_pass = 0;
static int test_todo = 0;
static int test_num = 0;
static int test_cases = 0;
static int test_plan_line = 0;
static const char *test_plan_file = NULL;
const char *test_directive = NULL;
const char *test_reason = NULL;
static void fprint_string(FILE *file, CFStringRef string) {
UInt8 buf[256];
CFRange range = { .location = 0 };
range.length = CFStringGetLength(string);
while (range.length > 0) {
CFIndex bytesUsed = 0;
CFIndex converted = CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, false, buf, sizeof(buf), &bytesUsed);
fwrite(buf, 1, bytesUsed, file);
range.length -= converted;
range.location += converted;
}
}
static void cffprint(FILE *file, CFStringRef fmt, ...) CF_FORMAT_FUNCTION(2,3);
static void cffprint_v(FILE *file, CFStringRef fmt, va_list args) CF_FORMAT_FUNCTION(2,0);
static void cffprint_c_v(FILE *file, const char *fmt, va_list args);
static void cffprint_v(FILE *file, CFStringRef fmt, va_list args) {
CFStringRef line = CFStringCreateWithFormatAndArguments(NULL, NULL, fmt, args);
fprint_string(file, line);
CFRelease(line);
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
static void cffprint_c_v(FILE *file, const char *fmt, va_list args) {
CFStringRef cffmt = CFStringCreateWithCString(kCFAllocatorDefault, fmt, kCFStringEncodingUTF8);
cffprint_v(file, cffmt, args);
CFRelease(cffmt);
}
#pragma clang diagnostic pop
static void cffprint(FILE *file, CFStringRef fmt, ...) {
va_list args;
va_start(args, fmt);
cffprint_v(file, fmt, args);
va_end(args);
}
void test_skip(const char *reason, int how_many, int unless)
{
if (unless)
return;
int done;
for (done = 0; done < how_many; ++done)
test_ok(1, NULL, "skip", reason, __FILE__, __LINE__, NULL);
}
void test_bail_out(const char *reason, const char *file, unsigned line)
{
fprintf(stdout, "[FAIL] BAIL OUT! (%s at line %u) %s\n", file, line, reason);
fflush(stdout);
exit(255);
}
void test_plan_skip_all(const char *reason)
{
pthread_mutex_lock(&test_mutex);
int skipN = test_cases - test_num;
pthread_mutex_unlock(&test_mutex);
if (skipN > 0)
{
test_skip(reason, skipN, 0);
}
}
static const char *test_plan_name(void) {
const char *plan_name = strrchr(test_plan_file, '/');
plan_name = plan_name ? plan_name + 1 : test_plan_file;
return plan_name;
}
static int test_plan_pass(void) {
if (test_verbose) {
const char *name = test_plan_name();
fprintf(stdout, "[BEGIN] %s plan\n[PASS] %s plan\n", name, name);
}
return 0;
}
static int test_plan_fail(CFStringRef reason, ...) CF_FORMAT_FUNCTION(1, 2);
static int test_plan_fail(CFStringRef reason, ...) {
const char *name = test_plan_name();
va_list ap;
va_start(ap, reason);
CFStringRef desc = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, reason, ap);
cffprint(stdout, CFSTR("[BEGIN] %s plan\n%@[WARN] %s plan\n"), name, desc, name);
CFRelease(desc);
return 1;
}
int test_plan_ok(void) {
int status = 0;
fflush(stderr);
const char *name = test_plan_name();
pthread_mutex_lock(&test_mutex);
if (!test_num)
{
if (test_cases)
{
status = test_plan_fail(CFSTR("No tests run!\n"));
}
else
{
status = test_plan_fail(CFSTR("Looks like your test died before it could output anything.\n"));
}
}
else if (test_num < test_cases)
{
status = test_plan_fail(CFSTR("Looks like you planned %d tests but only ran %d.\n"), test_cases, test_num);
}
else if (test_num > test_cases)
{
status = test_plan_fail(CFSTR("Looks like you planned %d tests but ran %d.\n"), test_cases, test_num);
} else if (!test_fails) {
status = test_plan_pass();
}
if (test_fails)
{
fprintf(stdout, "%s failed %d tests of %d.\n", name, test_fails, test_num);
status = 1;
}
pthread_mutex_unlock(&test_mutex);
fflush(stdout);
return status;
}
static void test_plan_reset(void) {
test_fails = 0;
test_todo_pass = 0;
test_todo = 0;
test_num = 0;
test_cases = 0;
test_plan_file = NULL;
test_plan_line = 0;
}
void test_plan_final(int *failed, int *todo_pass, int *todo, int *actual, int *planned, const char **file, int *line) {
pthread_mutex_lock(&test_mutex);
if (failed)
*failed = test_fails;
if (todo_pass)
*todo_pass = test_todo_pass;
if (todo)
*todo = test_todo;
if (actual)
*actual = test_num;
if (planned)
*planned = test_cases;
if (file)
*file = test_plan_file;
if (line)
*line = test_plan_line;
test_plan_reset();
pthread_mutex_unlock(&test_mutex);
}
void test_plan_tests(int count, const char *file, unsigned line) {
if (test_cases)
{
fprintf(stdout,
"[FAIL] You tried to plan twice!\n");
fflush(stdout);
exit(255);
}
else
{
if (!count)
{
fprintf(stdout, "[BEGIN] plan_tests\nYou said to run 0 tests! "
"You've got to run something.\n[WARN] plan_tests\n");
fflush(stdout);
}
test_plan_file=file;
test_plan_line=line;
test_cases = count;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if(pthread_mutex_init(&test_mutex, NULL) != 0) {
fprintf(stdout, "Failed to initialize mutex: %d\n", errno);
}
});
}
}
int
test_diag(const char *directive, const char *reason,
const char *file, unsigned line, const char *fmt, ...)
{
int is_todo = directive && !strcmp(directive, "TODO");
va_list args;
va_start(args, fmt);
if (is_todo)
{
fputs("# ", stdout);
if (fmt)
vprintf(fmt, args);
fputs("\n", stdout);
fflush(stdout);
}
else
{
fputs("# ", stdout);
if (fmt)
vfprintf(stdout, fmt, args);
fputs("\n", stdout);
fflush(stdout);
}
va_end(args);
return 1;
}
int
test_ok(int passed, __attribute((cf_consumed)) CFStringRef CF_CONSUMED description, const char *directive,
const char *reason, const char *file, unsigned line,
const char *fmt, ...)
{
int is_todo = directive && !strcmp(directive, "TODO");
int is_setup = directive && !is_todo && !strcmp(directive, "SETUP");
if (is_setup)
{
if (!passed)
{
fflush(stderr);
fprintf(stdout, "[BEGIN] SETUP\n");
if (fmt) {
va_list args;
va_start(args, fmt);
cffprint_c_v(stdout, fmt, args);
va_end(args);
}
cffprint(stdout, CFSTR("[WARN] SETUP%s%@%s%s\n"),
description ? " - " : "",
description ? description : CFSTR(""),
reason ? " - " : "",
reason ? reason : "");
fflush(stdout);
}
}
else
{
if (!test_cases)
{
}
pthread_mutex_lock(&test_mutex);
++test_num;
if (passed) {
if (is_todo) {
test_todo_pass++;
}
} else if (is_todo) {
test_todo++;
} else {
++test_fails;
}
if ((!passed && !is_todo) || test_verbose > 0) {
fflush(stderr);
if (test_strict_bats) {
cffprint(stdout, CFSTR("[BEGIN] %d%s%@\n"),
test_num,
description ? " - " : "",
description ? description : CFSTR(""));
}
if (is_todo && passed) {
fprintf(stdout, "%s:%d: warning: Unexpectedly passed (TODO) test\n", file, line);
} else if (is_todo && !passed) {
fprintf(stdout, "%s:%d: ok: Failed (TODO) test\n", file, line);
} else if (!passed) {
fprintf(stdout, "%s:%d: error: Failed test\n", file, line);
}
if (fmt) {
va_list args;
va_start(args, fmt);
cffprint_c_v(stdout, fmt, args);
va_end(args);
}
cffprint(stdout, CFSTR("[%s] %d%s%@%s%s%s%s\n"), passed ? (is_todo ? "PASS" : "PASS") : (is_todo ? "PASS" : "FAIL"),
test_num,
description ? " - " : "",
description ? description : CFSTR(""),
directive ? " # " : "",
directive ? directive : "",
reason ? " " : "",
reason ? reason : "");
fflush(stdout);
}
pthread_mutex_unlock(&test_mutex);
}
if (description)
CFRelease(description);
return passed;
}
static void buf_kill(void* p) {
free(p);
}
const char *
sec_errstr(int err)
{
static pthread_key_t buffer0key;
static pthread_key_t buffer1key;
static pthread_key_t switchkey;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
pthread_key_create(&buffer0key, buf_kill);
pthread_key_create(&buffer1key, buf_kill);
pthread_key_create(&switchkey, buf_kill);
});
uint32_t * switchp = (uint32_t*) pthread_getspecific(switchkey);
if(switchp == NULL) {
switchp = (uint32_t*) malloc(sizeof(uint32_t));
*switchp = 0;
pthread_setspecific(switchkey, switchp);
}
char* buf = NULL;
pthread_key_t current = (*switchp) ? buffer0key : buffer1key;
*switchp = !(*switchp);
buf = pthread_getspecific(current);
if(buf == NULL) {
buf = (char*) malloc(20);
pthread_setspecific(current, buf);
}
snprintf(buf, 20, "0x%X", err);
return buf;
}