/*
* Copyright (c) 2015 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
#include <tests/ktest_internal.h>
#include <kern/misc_protos.h>
#include <kern/debug.h>
#define EMIT(buf, size) do {\
console_write(buf, size); \
} while(0)
/* TODO: intelligently truncate messages if possible */
#define BOUNDS_CHECK_AND_UPDATE(ret, size) do {\
if(ret < 0 || ret >= size) {\
panic("Internal ktest error in %s", __func__);\
}\
size -= ret;\
msg += ret;\
} while(0)
int vsnprintf(char *, size_t, const char *, va_list);
void
ktest_emit_start(void)
{
char str[] = "\n[KTEST]\tSTART\t" KTEST_VERSION_STR "\n";
EMIT((char *)&str[0], sizeof(str) - 1);
}
void
ktest_emit_finish(void)
{
char str[] = "\n[KTEST]\tFINISH\n";
EMIT((char *)&str[0], sizeof(str) - 1);
}
void
ktest_emit_testbegin(const char * test_name)
{
char * msg = ktest_output_buf;
int size = sizeof(ktest_output_buf);
int ret;
/* left trim the file path for readability */
const char *fname = strnstr((char *)(uintptr_t)ktest_current_file, "xnu", 100);
ret = snprintf(msg,
size,
"\n[KTEST]\t" /* header */
"TESTBEGIN\t" /* type */
"%lld\t" /* time */
"%d\t" /* index */
"%s\t" /* file */
"%d\t" /* line */
"%s\n", /* name */
ktest_current_time,
ktest_test_index,
fname,
ktest_current_line,
test_name);
BOUNDS_CHECK_AND_UPDATE(ret, size);
EMIT(ktest_output_buf, (int)(msg - ktest_output_buf));
}
void
ktest_emit_testskip(const char * skip_msg, va_list args)
{
char * msg = ktest_output_buf;
int size = sizeof(ktest_output_buf);
int ret;
const char *fname = strnstr((char *)(uintptr_t)ktest_current_file, "xnu", 100);
ret = snprintf(msg,
size,
"\n[KTEST]\t" /* header */
"TESTSKIP\t" /* type */
"%lld\t" /* time */
"%s\t" /* file */
"%d\t", /* line */
ktest_current_time,
fname,
ktest_current_line);
BOUNDS_CHECK_AND_UPDATE(ret, size);
ret = vsnprintf(msg, size, skip_msg, args);
BOUNDS_CHECK_AND_UPDATE(ret, size);
ret = snprintf(msg, size, "\n");
BOUNDS_CHECK_AND_UPDATE(ret, size);
EMIT(ktest_output_buf, (int)(msg - ktest_output_buf));
}
void
ktest_emit_testend()
{
char * msg = ktest_output_buf;
int size = sizeof(ktest_output_buf);
int ret;
const char *fname = strnstr((char *)(uintptr_t)ktest_current_file, "xnu", 100);
ret = snprintf(msg,
size,
"\n[KTEST]\t" /* header */
"TESTEND\t" /* type */
"%lld\t" /* time */
"%d\t" /* index */
"%s\t" /* file */
"%d\t" /* line */
"%s\n", /* name */
ktest_current_time,
ktest_test_index,
fname,
ktest_current_line,
ktest_test_name);
BOUNDS_CHECK_AND_UPDATE(ret, size);
EMIT(ktest_output_buf, (int)(msg - ktest_output_buf));
}
void
ktest_emit_log(const char * log_msg, va_list args)
{
char * msg = ktest_output_buf;
int size = sizeof(ktest_output_buf);
int ret;
const char *fname = strnstr((char *)(uintptr_t)ktest_current_file, "xnu", 100);
ret = snprintf(msg,
size,
"\n[KTEST]\t" /* header */
"LOG\t" /* type */
"%lld\t" /* time */
"%s\t" /* file */
"%d\t", /* line */
ktest_current_time,
fname,
ktest_current_line);
BOUNDS_CHECK_AND_UPDATE(ret, size);
ret = vsnprintf(msg, size, log_msg, args);
BOUNDS_CHECK_AND_UPDATE(ret, size);
ret = snprintf(msg, size, "\n");
BOUNDS_CHECK_AND_UPDATE(ret, size);
EMIT(ktest_output_buf, (int)(msg - ktest_output_buf));
}
void
ktest_emit_perfdata(const char * metric, const char * unit, double value, const char * desc)
{
static const char * perfstr = "%s\t%lld\t%s\t\"%s\"";
char * msg = ktest_output_buf;
int64_t print_value = (int64_t)value;
int size = sizeof(ktest_output_buf);
int ret;
const char *fname = strnstr((char *)(uintptr_t)ktest_current_file, "xnu", 100);
ret = snprintf(msg, size,
"\n[KTEST]\t" /* header */
"PERF\t" /* type */
"%lld\t" /* time */
"%s\t" /* file */
"%d\t", /* line */
ktest_current_time,
fname,
ktest_current_line);
BOUNDS_CHECK_AND_UPDATE(ret, size);
ret = snprintf(msg, size, perfstr, metric, print_value, unit, desc);
BOUNDS_CHECK_AND_UPDATE(ret, size);
ret = snprintf(msg, size, "\n");
BOUNDS_CHECK_AND_UPDATE(ret, size);
EMIT(ktest_output_buf, (int)(msg - ktest_output_buf));
}
void
ktest_emit_testcase(void)
{
char * msg = ktest_output_buf;
int size = sizeof(ktest_output_buf);
int ret;
const char *fname = strnstr((char *)(uintptr_t)ktest_current_file, "xnu", 100);
ret = snprintf(msg,
size,
"\n[KTEST]\t" /* header */
"%s\t" /* type */
"%lld\t" /* time */
"%d\t" /* index */
"%s\t" /* file */
"%d\t" /* line */
"%s\t" /* message */
"%s", /* current_expr */
ktest_testcase_result_tokens[ktest_testcase_mode]
[ktest_testcase_result],
ktest_current_time,
ktest_expression_index,
fname,
ktest_current_line,
ktest_current_msg,
ktest_current_expr);
BOUNDS_CHECK_AND_UPDATE(ret, size);
for (int i = 0; ktest_current_var_names[i][0]; i++) {
ret = snprintf(msg,
size,
"\t%s\t%s",
ktest_current_var_names[i],
ktest_current_var_values[i]);
BOUNDS_CHECK_AND_UPDATE(ret, size);
}
ret = snprintf(msg, size, "\n");
BOUNDS_CHECK_AND_UPDATE(ret, size);
EMIT(ktest_output_buf, (int)(msg - ktest_output_buf));
}