1 // Copyright 2016 The Fuchsia Authors
2 // Copyright (c) 2013, Google, Inc. All rights reserved
3 //
4 // Use of this source code is governed by a MIT-style
5 // license that can be found in the LICENSE file or at
6 // https://opensource.org/licenses/MIT
7
8 /*
9 * Functions for unit tests. See lib/unittest/include/unittest.h for usage.
10 */
11 #include <assert.h>
12 #include <debug.h>
13 #include <err.h>
14 #include <fbl/auto_call.h>
15 #include <inttypes.h>
16 #include <kernel/mutex.h>
17 #include <kernel/thread.h>
18 #include <lib/unittest/unittest.h>
19 #include <platform.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <vm/vm_aspace.h>
27 #include <zircon/compiler.h>
28 #include <zircon/types.h>
29
30 // Ensures unittests are not run concurrently.
31 static mutex_t lock = MUTEX_INITIAL_VALUE(lock);
32
33 /**
34 * \brief Function called to dump results
35 *
36 * This function will call the out_func callback
37 */
unittest_printf(const char * format,...)38 int unittest_printf(const char* format, ...) {
39 int ret = 0;
40
41 va_list argp;
42 va_start(argp, format);
43 ret = vprintf(format, argp);
44 va_end(argp);
45
46 return ret;
47 }
48
unittest_expect_bytes(const uint8_t * expected,const char * expected_name,const uint8_t * actual,const char * actual_name,size_t len,const char * msg,const char * func,int line,bool expect_eq)49 bool unittest_expect_bytes(const uint8_t* expected,
50 const char* expected_name,
51 const uint8_t* actual,
52 const char* actual_name,
53 size_t len,
54 const char* msg,
55 const char* func,
56 int line,
57 bool expect_eq) {
58 if (!memcmp(expected, actual, len) != expect_eq) {
59
60 unittest_printf(UNITTEST_FAIL_TRACEF_FORMAT "%s:\n%s %s %s, but %s!\n",
61 func, line, msg,
62 expected_name,
63 expect_eq ? "does not match" : "matches",
64 actual_name,
65 expect_eq ? "should" : "should not");
66
67 unittest_printf("expected (%s)\n", expected_name);
68 hexdump8_very_ex(expected, len, (uint64_t)((addr_t)expected), unittest_printf);
69 unittest_printf("actual (%s)\n", actual_name);
70 hexdump8_very_ex(actual, len, (uint64_t)((addr_t)actual), unittest_printf);
71
72 return false;
73 }
74 return true;
75 }
76
77 #include <lib/console.h>
78
79 // External references to the testcase registration tables.
80 extern unittest_testcase_registration_t __start_unittest_testcases[];
81 extern unittest_testcase_registration_t __stop_unittest_testcases[];
82
usage(const char * progname)83 static void usage(const char* progname) {
84 printf("Usage:\n"
85 "%s <case>\n"
86 " where case is a specific testcase name, or...\n"
87 " all : run all tests\n"
88 " ? : list tests\n",
89 progname);
90 }
91
list_cases(void)92 static void list_cases(void) {
93 size_t count = 0;
94 size_t max_namelen = 0;
95
96 const unittest_testcase_registration_t* testcase;
97 for (testcase = __start_unittest_testcases;
98 testcase != __stop_unittest_testcases;
99 ++testcase) {
100
101 if (testcase->name) {
102 size_t namelen = strlen(testcase->name);
103 if (max_namelen < namelen)
104 max_namelen = namelen;
105 count++;
106 }
107 }
108
109 printf("There %s %zu test case%s available...\n",
110 count == 1 ? "is" : "are",
111 count,
112 count == 1 ? "" : "s");
113
114 for (testcase = __start_unittest_testcases;
115 testcase != __stop_unittest_testcases;
116 ++testcase) {
117
118 if (testcase->name)
119 printf(" %-*s : %s\n",
120 static_cast<int>(max_namelen), testcase->name,
121 testcase->desc ? testcase->desc : "<no description>");
122 }
123 }
124
run_unittest(const unittest_testcase_registration_t * testcase)125 static bool run_unittest(const unittest_testcase_registration_t* testcase) {
126 size_t max_namelen = 0;
127 size_t passed = 0;
128
129 DEBUG_ASSERT(testcase);
130 DEBUG_ASSERT(testcase->name);
131 DEBUG_ASSERT(!!testcase->tests == !!testcase->test_cnt);
132
133 for (size_t i = 0; i < testcase->test_cnt; ++i) {
134 const unittest_registration_t* test = &testcase->tests[i];
135 if (test->name) {
136 size_t namelen = strlen(test->name);
137 if (max_namelen < namelen)
138 max_namelen = namelen;
139 }
140 }
141
142 unittest_printf("%s : Running %zu test%s...\n",
143 testcase->name,
144 testcase->test_cnt,
145 testcase->test_cnt == 1 ? "" : "s");
146
147 zx_time_t testcase_start = current_time();
148
149 for (size_t i = 0; i < testcase->test_cnt; ++i) {
150 const unittest_registration_t* test = &testcase->tests[i];
151
152 printf(" %-*s : ",
153 static_cast<int>(max_namelen), test->name ? test->name : "");
154
155 zx_time_t test_start = current_time();
156 bool good = test->fn ? test->fn() : false;
157 zx_duration_t test_runtime = current_time() - test_start;
158
159 if (good) {
160 passed++;
161 } else {
162 printf(" %-*s : ",
163 static_cast<int>(max_namelen), test->name ? test->name : "");
164 }
165
166 unittest_printf("%s (%" PRIi64 " nSec)\n",
167 good ? "PASSED" : "FAILED",
168 test_runtime);
169 }
170
171 zx_duration_t testcase_runtime = current_time() - testcase_start;
172
173 unittest_printf("%s : %sll tests passed (%zu/%zu) in %" PRIi64 " nSec\n",
174 testcase->name,
175 passed != testcase->test_cnt ? "Not a" : "A",
176 passed, testcase->test_cnt,
177 testcase_runtime);
178
179 return passed == testcase->test_cnt;
180 }
181
182 // Runs the testcase specified by |arg| and returns 1 if test passes.
183 //
184 // |arg| is a const unittest_testcase_registration_t*.
run_unittest_thread_entry(void * arg)185 static int run_unittest_thread_entry(void* arg) {
186 auto* testcase = static_cast<const unittest_testcase_registration_t*>(arg);
187 return run_unittest(testcase);
188 }
189
190 // Runs |testcase| in another thread and waits for it to complete.
191 //
192 // Returns true if the test passed.
run_testcase_in_thread(const unittest_testcase_registration_t * testcase)193 static bool run_testcase_in_thread(const unittest_testcase_registration_t* testcase) {
194 fbl::RefPtr<VmAspace> aspace = VmAspace::Create(VmAspace::TYPE_USER, "unittest");
195 if (!aspace) {
196 unittest_printf("failed to create unittest user aspace\n");
197 return false;
198 }
199 auto destroy_aspace = fbl::MakeAutoCall([&]() {
200 zx_status_t status = aspace->Destroy();
201 DEBUG_ASSERT(status == ZX_OK);
202 });
203 thread_t* t = thread_create("unittest", run_unittest_thread_entry,
204 const_cast<void*>(static_cast<const void*>(testcase)),
205 DEFAULT_PRIORITY);
206 if (!t) {
207 unittest_printf("failed to create unittest thread\n");
208 return false;
209 }
210 aspace->AttachToThread(t);
211
212 thread_resume(t);
213 int success = 0;
214 zx_status_t status = thread_join(t, &success, ZX_TIME_INFINITE);
215 if (status != ZX_OK) {
216 unittest_printf("failed to join unittest thread: %d\n", status);
217 return false;
218 }
219 return success;
220 }
221
run_unittests_locked(int argc,const cmd_args * argv,uint32_t flags)222 static int run_unittests_locked(int argc, const cmd_args* argv, uint32_t flags) {
223 DEBUG_ASSERT(is_mutex_held(&lock));
224 if (argc != 2) {
225 usage(argv[0].str);
226 return 0;
227 }
228
229 const char* casename = argv[1].str;
230
231 if (!strcmp(casename, "?")) {
232 list_cases();
233 return 0;
234 }
235
236 bool run_all = !strcmp(casename, "all");
237 const unittest_testcase_registration_t* testcase;
238 size_t chosen = 0;
239 size_t passed = 0;
240
241 const size_t num_tests =
242 run_all ? __stop_unittest_testcases - __start_unittest_testcases : 1;
243 // Array of names with a NULL sentinel at the end.
244 const char** failed_names = static_cast<const char**>(calloc(num_tests + 1, sizeof(char*)));
245 const char** fn = failed_names;
246
247 for (testcase = __start_unittest_testcases;
248 testcase != __stop_unittest_testcases;
249 ++testcase) {
250
251 if (testcase->name) {
252 if (run_all || !strcmp(casename, testcase->name)) {
253 chosen++;
254
255 if (run_testcase_in_thread(testcase)) {
256 passed++;
257 } else {
258 *fn++ = testcase->name;
259 }
260 printf("\n");
261
262 if (!run_all)
263 break;
264 }
265 }
266 }
267
268 int ret = 0;
269 if (!run_all && !chosen) {
270 ret = -1;
271 unittest_printf("Test case \"%s\" not found!\n", casename);
272 list_cases();
273 } else {
274 unittest_printf("SUMMARY: Ran %zu test case%s: %zu failed\n",
275 chosen, chosen == 1 ? "" : "s", chosen - passed);
276 if (passed < chosen) {
277 ret = -1;
278 unittest_printf("\nThe following test cases failed:\n");
279 for (fn = failed_names; *fn != NULL; fn++) {
280 unittest_printf("%s\n", *fn);
281 }
282 }
283 }
284
285 free(failed_names);
286 return ret;
287 }
288
run_unittests(int argc,const cmd_args * argv,uint32_t flags)289 static int run_unittests(int argc, const cmd_args* argv, uint32_t flags) {
290 mutex_acquire(&lock);
291 const int ret = run_unittests_locked(argc, argv, flags);
292 mutex_release(&lock);
293 return ret;
294 }
295
296 STATIC_COMMAND_START
297 STATIC_COMMAND("ut", "Run unittests", run_unittests)
298 STATIC_COMMAND_END(unittests);
299