| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <unittest/unittest.h> |
| |
| #include <stdbool.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/time.h> |
| |
| #include <pretty/hexdump.h> |
| |
| #ifdef UNITTEST_CRASH_HANDLER_SUPPORTED |
| #include "crash-handler.h" |
| #include "crash-list.h" |
| #endif // UNITTEST_CRASH_HANDLER_SUPPORTED |
| |
| using nsecs_t = uint64_t; |
| |
| static nsecs_t now() { |
| #ifdef __Fuchsia__ |
| return zx_clock_get(ZX_CLOCK_MONOTONIC); |
| #else |
| // clock_gettime(CLOCK_MONOTONIC) would be better but may not exist on the host |
| struct timeval tv; |
| if (gettimeofday(&tv, nullptr) < 0) |
| return 0u; |
| return tv.tv_sec * 1000000000ull + tv.tv_usec * 1000ull; |
| #endif |
| } |
| |
| /** |
| * \brief Default function to dump unit test results |
| * |
| * \param[in] line is the buffer to dump |
| * \param[in] len is the length of the buffer to dump |
| * \param[in] arg can be any kind of arguments needed to dump the values |
| */ |
| static void default_printf(const char* line, int len, void* arg) { |
| fputs(line, stdout); |
| fflush(stdout); |
| } |
| |
| // Default output function is the printf |
| static test_output_func out_func = default_printf; |
| // Buffer the argument to be sent to the output function |
| static void* out_func_arg = nullptr; |
| |
| // Controls the behavior of unittest_printf. |
| // To override, specify v=N on the command line. |
| int utest_verbosity_level = 0; |
| |
| // Controls the types of tests which are executed. |
| // Multiple test types can be "OR-ed" together to |
| // run a subset of all tests. |
| test_type_t utest_test_type = static_cast<test_type>(TEST_DEFAULT); |
| |
| /** |
| * \brief Function called to dump results |
| * |
| * This function will call the out_func callback |
| */ |
| void unittest_printf_critical(const char* format, ...) { |
| static char print_buffer[PRINT_BUFFER_SIZE]; |
| |
| va_list argp; |
| va_start(argp, format); |
| |
| if (out_func) { |
| // Format the string |
| vsnprintf(print_buffer, PRINT_BUFFER_SIZE, format, argp); |
| out_func(print_buffer, PRINT_BUFFER_SIZE, out_func_arg); |
| } |
| |
| va_end(argp); |
| } |
| |
| bool unittest_expect_bytes_eq(const uint8_t* expected, const uint8_t* actual, size_t len, |
| const char* msg) { |
| if (memcmp(expected, actual, len)) { |
| printf("%s. expected\n", msg); |
| hexdump8(expected, len); |
| printf("actual\n"); |
| hexdump8(actual, len); |
| return false; |
| } |
| return true; |
| } |
| |
| bool unittest_expect_str_eq(const char* expected, const char* actual, size_t len, const char* msg) { |
| if (strncmp(expected, actual, len)) { |
| printf("%s. expected\n'%s'\nactual\n'%s'\n", msg, expected, actual); |
| return false; |
| } |
| return true; |
| } |
| |
| void unittest_set_output_function(test_output_func fun, void* arg) { |
| out_func = fun; |
| out_func_arg = arg; |
| } |
| |
| int unittest_set_verbosity_level(int new_level) { |
| int out = utest_verbosity_level; |
| utest_verbosity_level = new_level; |
| return out; |
| } |
| |
| #ifdef UNITTEST_CRASH_HANDLER_SUPPORTED |
| void unittest_register_crash(struct test_info* current_test_info, zx_handle_t handle) { |
| crash_list_register(current_test_info->crash_list, handle); |
| } |
| |
| bool unittest_run_death_fn(void (*fn_to_run)(void*), void* arg) { |
| test_result_t test_result; |
| zx_status_t status = run_fn_with_crash_handler(fn_to_run, arg, &test_result); |
| return status == ZX_OK && test_result == TEST_CRASHED; |
| } |
| |
| bool unittest_run_no_death_fn(void (*fn_to_run)(void*), void* arg) { |
| test_result_t test_result; |
| zx_status_t status = run_fn_with_crash_handler(fn_to_run, arg, &test_result); |
| return status == ZX_OK && test_result != TEST_CRASHED; |
| } |
| #endif // UNITTEST_CRASH_HANDLER_SUPPORTED |
| |
| void unittest_run_named_test(const char* name, bool (*test)(), test_type_t test_type, |
| struct test_info** current_test_info, bool* all_success, |
| bool enable_crash_handler) { |
| if (utest_test_type & test_type) { |
| unittest_printf_critical(" %-51s [RUNNING]", name); |
| nsecs_t start_time = now(); |
| test_info test_info = {.all_ok = true, nullptr}; |
| *current_test_info = &test_info; |
| // The crash handler is disabled by default. To enable, the test should |
| // be run with RUN_TEST_ENABLE_CRASH_HANDLER. |
| if (enable_crash_handler) { |
| #ifdef UNITTEST_CRASH_HANDLER_SUPPORTED |
| test_info.crash_list = crash_list_new(); |
| |
| test_result_t test_result; |
| zx_status_t status = |
| run_test_with_crash_handler(test_info.crash_list, test, &test_result); |
| if (status != ZX_OK || test_result == TEST_FAILED) { |
| test_info.all_ok = false; |
| } |
| |
| // Check if there were any processes registered to crash but didn't. |
| bool missing_crash = crash_list_delete(test_info.crash_list); |
| if (missing_crash) { |
| // TODO: display which expected crash did not occur. |
| UNITTEST_FAIL_TRACEF("Expected crash did not occur\n"); |
| test_info.all_ok = false; |
| } |
| #else // UNITTEST_CRASH_HANDLER_SUPPORTED |
| UNITTEST_FAIL_TRACEF("Crash tests not supported\n"); |
| test_info.all_ok = false; |
| #endif // UNITTEST_CRASH_HANDLER_SUPPORTED |
| } else if (!test()) { |
| test_info.all_ok = false; |
| } |
| |
| // Recheck all_ok in case there was a failure in a C++ destructor |
| // after the "return" statement in END_TEST. |
| if (!test_info.all_ok) { |
| *all_success = false; |
| } |
| |
| nsecs_t end_time = now(); |
| uint64_t time_taken_ms = (end_time - start_time) / 1000000; |
| unittest_printf_critical(" [%s] (%d ms)\n", test_info.all_ok ? "PASSED" : "FAILED", |
| static_cast<int>(time_taken_ms)); |
| |
| *current_test_info = nullptr; |
| } else { |
| unittest_printf_critical(" %-51s [IGNORED]\n", name); |
| } |
| } |