/* * 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 #include #include #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)); }