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