1// Copyright 2016 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <unittest/unittest.h>
6
7#include <stdbool.h>
8#include <stddef.h>
9#include <stdint.h>
10#include <stdio.h>
11#include <string.h>
12#include <sys/time.h>
13
14#include <pretty/hexdump.h>
15
16#include "watchdog.h"
17
18#ifdef UNITTEST_CRASH_HANDLER_SUPPORTED
19#include "crash-handler.h"
20#include "crash-list.h"
21#endif // UNITTEST_CRASH_HANDLER_SUPPORTED
22
23using nsecs_t = uint64_t;
24
25static nsecs_t now() {
26#ifdef __Fuchsia__
27    return zx_clock_get_monotonic();
28#else
29    // clock_gettime(CLOCK_MONOTONIC) would be better but may not exist on the host
30    struct timeval tv;
31    if (gettimeofday(&tv, nullptr) < 0)
32        return 0u;
33    return tv.tv_sec * 1000000000ull + tv.tv_usec * 1000ull;
34#endif
35}
36
37/**
38 * \brief Default function to dump unit test results
39 *
40 * \param[in] line is the buffer to dump
41 * \param[in] len is the length of the buffer to dump
42 * \param[in] arg can be any kind of arguments needed to dump the values
43 */
44static void default_printf(const char* line, int len, void* arg) {
45    fputs(line, stdout);
46    fflush(stdout);
47}
48
49// Default output function is the printf
50static test_output_func out_func = default_printf;
51// Buffer the argument to be sent to the output function
52static void* out_func_arg = nullptr;
53
54// Controls the behavior of unittest_printf.
55// To override, specify v=N on the command line.
56int utest_verbosity_level = 0;
57
58// Controls the types of tests which are executed.
59// Multiple test types can be "OR-ed" together to
60// run a subset of all tests.
61test_type_t utest_test_type = static_cast<test_type>(TEST_DEFAULT);
62
63/**
64 * \brief Function called to dump results
65 *
66 * This function will call the out_func callback
67 */
68void unittest_printf_critical(const char* format, ...) {
69    static char print_buffer[PRINT_BUFFER_SIZE];
70
71    va_list argp;
72    va_start(argp, format);
73
74    if (out_func) {
75        // Format the string
76        vsnprintf(print_buffer, PRINT_BUFFER_SIZE, format, argp);
77        out_func(print_buffer, PRINT_BUFFER_SIZE, out_func_arg);
78    }
79
80    va_end(argp);
81}
82
83bool unittest_expect_bytes_eq(const uint8_t* expected, const uint8_t* actual, size_t len,
84                              const char* msg) {
85    if (memcmp(expected, actual, len)) {
86        printf("%s. expected\n", msg);
87        hexdump8(expected, len);
88        printf("actual\n");
89        hexdump8(actual, len);
90        return false;
91    }
92    return true;
93}
94
95bool unittest_expect_str_eq(const char* str1_value, const char* str2_value,
96                            const char* str1_expr, const char* str2_expr,
97                            const char* msg,
98                            const char* source_filename, int source_line_num,
99                            const char* source_function) {
100    if (strcmp(str1_value, str2_value)) {
101        unittest_printf_critical(
102            UNITTEST_FAIL_TRACEF_FORMAT
103            "%s:\n"
104            "        Comparison failed: strings not equal:\n"
105            "        String 1 expression: %s\n"
106            "        String 2 expression: %s\n"
107            "        String 1 value: \"%s\"\n"
108            "        String 2 value: \"%s\"\n",
109            source_filename, source_line_num, source_function,
110            msg, str1_expr, str2_expr, str1_value, str2_value);
111        return false;
112    }
113    return true;
114}
115
116bool unittest_expect_str_ne(const char* str1_value, const char* str2_value,
117                            const char* str1_expr, const char* str2_expr,
118                            const char* msg,
119                            const char* source_filename, int source_line_num,
120                            const char* source_function) {
121    if (!strcmp(str1_value, str2_value)) {
122        unittest_printf_critical(
123            UNITTEST_FAIL_TRACEF_FORMAT
124            "%s:\n"
125            "        Comparison failed: strings are equal,"
126            " but expected different strings:\n"
127            "        String 1 expression: %s\n"
128            "        String 2 expression: %s\n"
129            "        Value of both strings: \"%s\"\n",
130            source_filename, source_line_num, source_function,
131            msg, str1_expr, str2_expr, str1_value);
132        return false;
133    }
134    return true;
135}
136
137bool unittest_expect_str_str(const char* str1_value, const char* str2_value,
138                            const char* str1_expr, const char* str2_expr,
139                            const char* msg,
140                            const char* source_filename, int source_line_num,
141                            const char* source_function) {
142    if (!strstr(str1_value, str2_value)) {
143        unittest_printf_critical(
144            UNITTEST_FAIL_TRACEF_FORMAT
145            "%s:\n"
146            "        Comparison failed: String 1 does not"
147            " contain String 2:\n"
148            "        String 1 expression: %s\n"
149            "        String 2 expression: %s\n"
150            "        Value of both strings: \"%s\"\n",
151            source_filename, source_line_num, source_function,
152            msg, str1_expr, str2_expr, str1_value);
153        return false;
154    }
155    return true;
156}
157
158void unittest_set_output_function(test_output_func fun, void* arg) {
159    out_func = fun;
160    out_func_arg = arg;
161}
162
163void unittest_restore_output_function() {
164    out_func = default_printf;
165    out_func_arg = nullptr;
166}
167
168int unittest_set_verbosity_level(int new_level) {
169    int out = utest_verbosity_level;
170    utest_verbosity_level = new_level;
171    return out;
172}
173
174#ifdef UNITTEST_CRASH_HANDLER_SUPPORTED
175void unittest_register_crash(struct test_info* current_test_info, zx_handle_t handle) {
176    crash_list_register(current_test_info->crash_list, handle);
177}
178
179bool unittest_run_death_fn(void (*fn_to_run)(void*), void* arg) {
180    test_result_t test_result;
181    zx_status_t status = run_fn_with_crash_handler(fn_to_run, arg, &test_result);
182    return status == ZX_OK && test_result == TEST_CRASHED;
183}
184
185bool unittest_run_no_death_fn(void (*fn_to_run)(void*), void* arg) {
186    test_result_t test_result;
187    zx_status_t status = run_fn_with_crash_handler(fn_to_run, arg, &test_result);
188    return status == ZX_OK && test_result != TEST_CRASHED;
189}
190#endif // UNITTEST_CRASH_HANDLER_SUPPORTED
191
192static void unittest_run_test(const char* name,
193                              bool (*test)(),
194                              struct test_info** current_test_info,
195                              bool* all_success,
196                              bool enable_crash_handler) {
197    unittest_printf_critical("    %-51s [RUNNING]", name);
198    nsecs_t start_time = now();
199    test_info test_info = {.all_ok = true, nullptr};
200    *current_test_info = &test_info;
201    // The crash handler is disabled by default. To enable, the test should
202    // be run with RUN_TEST_ENABLE_CRASH_HANDLER.
203    if (enable_crash_handler) {
204#ifdef UNITTEST_CRASH_HANDLER_SUPPORTED
205        test_info.crash_list = crash_list_new();
206
207        test_result_t test_result;
208        zx_status_t status =
209            run_test_with_crash_handler(test_info.crash_list, test, &test_result);
210        if (status != ZX_OK || test_result == TEST_FAILED) {
211            test_info.all_ok = false;
212        }
213
214        // Check if there were any processes registered to crash but didn't.
215        bool missing_crash = crash_list_delete(test_info.crash_list);
216        if (missing_crash) {
217            // TODO: display which expected crash did not occur.
218            UNITTEST_FAIL_TRACEF("Expected crash did not occur\n");
219            test_info.all_ok = false;
220        }
221#else  // UNITTEST_CRASH_HANDLER_SUPPORTED
222        UNITTEST_FAIL_TRACEF("Crash tests not supported\n");
223        test_info.all_ok = false;
224#endif // UNITTEST_CRASH_HANDLER_SUPPORTED
225    } else if (!test()) {
226        test_info.all_ok = false;
227    }
228
229    // Recheck all_ok in case there was a failure in a C++ destructor
230    // after the "return" statement in END_TEST.
231    if (!test_info.all_ok) {
232        *all_success = false;
233    }
234
235    nsecs_t end_time = now();
236    uint64_t time_taken_ms = (end_time - start_time) / 1000000;
237    unittest_printf_critical(" [%s] (%d ms)\n", test_info.all_ok ? "PASSED" : "FAILED",
238                             static_cast<int>(time_taken_ms));
239
240    *current_test_info = nullptr;
241}
242
243template <typename F>
244void run_with_watchdog(test_type_t test_type, const char* name, F fn) {
245    if (watchdog_is_enabled()) {
246        watchdog_start(test_type, name);
247        fn();
248        watchdog_cancel();
249    } else {
250        fn();
251    }
252}
253
254void unittest_run_named_test(const char* name, bool (*test)(), test_type_t test_type,
255                             struct test_info** current_test_info, bool* all_success,
256                             bool enable_crash_handler) {
257    if (utest_test_type & test_type) {
258        run_with_watchdog(test_type, name, [&]() {
259            unittest_run_test(name, test, current_test_info, all_success, enable_crash_handler);
260        });
261    } else {
262        unittest_printf_critical("    %-51s [IGNORED]\n", name);
263    }
264}
265
266void unittest_cancel_timeout(void) {
267    watchdog_cancel();
268}
269