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