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.
31static 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 */
38int 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
49bool 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.
80extern unittest_testcase_registration_t __start_unittest_testcases[];
81extern unittest_testcase_registration_t __stop_unittest_testcases[];
82
83static 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
92static 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
125static 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 (%" PRIu64 " 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 %" PRIu64 " 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*.
185static 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.
193static 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
222static 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 %d test case%s: %d 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
289static 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
296STATIC_COMMAND_START
297STATIC_COMMAND("ut", "Run unittests", run_unittests)
298STATIC_COMMAND_END(unittests);
299