From 6c9669730083e6646761e360bedca660c9e21439 Mon Sep 17 00:00:00 2001 From: 82marbag <69267416+82marbag@users.noreply.github.com> Date: Sat, 26 Jun 2021 11:34:36 -0400 Subject: [PATCH] tests: refactor test execution Tests are now compiled into the kernel image and executed individually by specifying the test name as an option while booting with grub. The README file has been updated to show an example; another is in grub-test.cfg. Signed-off-by: Daniele Ahmed --- Makefile | 3 - README.md | 6 ++ grub/boot/grub/grub-test.cfg | 2 +- include/test.h | 32 +++++++ tests/test.c | 156 +++++++++-------------------------- tests/unittests.c | 144 ++++++++++++++++++++++++++++++++ 6 files changed, 223 insertions(+), 120 deletions(-) create mode 100644 include/test.h create mode 100644 tests/unittests.c diff --git a/Makefile b/Makefile index 65211622..5f30dcfd 100644 --- a/Makefile +++ b/Makefile @@ -71,9 +71,6 @@ COMMON_INCLUDES += -I$(PFMLIB_INCLUDE) endif COMMON_FLAGS := $(COMMON_INCLUDES) -pipe -MP -MMD -m64 -D__x86_64__ -ifneq ($(UNITTEST),) -COMMON_FLAGS += -DKTF_UNIT_TEST -endif ifeq ($(CONFIG_LIBPFM),y) COMMON_FLAGS += -DKTF_PMU diff --git a/README.md b/README.md index dfb32778..33affe77 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,12 @@ vcpus=1 You need to generate a bootable ISO for this. +### Adding new tests + +New tests can be added by adding a new function in a file in the `tests` folder. Each test signature must +be the same as `test_fn` provided in `test.h`. Tests can be enabled in `grub.cfg` by adding the option with key `tests` and values +the comma-separated list of function names, such as `tests=test1,test2,unit_tests`. + ## Style The style for this project is defined in `.clang-format` file in the main directory of this repository. diff --git a/grub/boot/grub/grub-test.cfg b/grub/boot/grub/grub-test.cfg index 703120b4..2d774b13 100644 --- a/grub/boot/grub/grub-test.cfg +++ b/grub/boot/grub/grub-test.cfg @@ -5,6 +5,6 @@ terminal_input --append serial terminal_output --append serial menuentry "kernel64" { - multiboot /boot/kernel64.bin integer=42 boolean=1 string=foo badstring=toolong booleantwo + multiboot /boot/kernel64.bin integer=42 boolean=1 string=foo badstring=toolong booleantwo tests=unit_tests boot } diff --git a/include/test.h b/include/test.h new file mode 100644 index 00000000..5af7f101 --- /dev/null +++ b/include/test.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef KTF_TEST_H +#define KTF_TEST_H + +#define MAX_OPT_TESTS_LEN 128 + +typedef int(test_fn)(void *arg); + +#endif /* KTF_TEST_H */ diff --git a/tests/test.c b/tests/test.c index c6b24d91..0e969242 100644 --- a/tests/test.c +++ b/tests/test.c @@ -24,134 +24,58 @@ */ #include #include -#include #include - -#ifdef KTF_UNIT_TEST -#include -#include #include +#include +#include -extern char *kernel_cmdline; - -static char opt_string[4]; -string_cmd("string", opt_string); - -static char opt_badstring[5]; -string_cmd("badstring", opt_badstring); - -static unsigned long opt_ulong; -ulong_cmd("integer", opt_ulong); - -static bool opt_bool = 0; -bool_cmd("boolean", opt_bool); - -static bool opt_booltwo = 0; -bool_cmd("booleantwo", opt_booltwo); - -static char memmove_string[4]; -static char range_string[] = "123456"; -static char *src, *dst; +static const char opt_test_delims[] = ","; +#include -static void cpu_freq_expect(const char *cpu_str, uint64_t expectation) { - uint64_t result = get_cpu_freq(cpu_str); - if (result != expectation) { - printk("Could not parse cpu frequency from string '%s'\n", cpu_str); - printk("Expectation vs. result: %llu vs. %llu\n", expectation, result); - BUG(); +enum get_next_test_result { + TESTS_DONE, + TESTS_FOUND, + TESTS_ERROR, +}; +typedef enum get_next_test_result get_next_test_result_t; + +static char opt_tests[MAX_OPT_TESTS_LEN]; +string_cmd("tests", opt_tests); + +static get_next_test_result_t get_next_test(test_fn **out_test_fn, char **out_name) { + static char *opt = opt_tests; + + *out_name = strtok(opt, opt_test_delims); + + opt = NULL; + if (*out_name) { + *out_test_fn = symbol_address(*out_name); + if (!*out_test_fn) { + printk("Symbol for test %s not found\n", *out_name); + return TESTS_ERROR; + } + return TESTS_FOUND; } - - printk("Got CPU string '%s' and frequency '%llu'\n", cpu_str, result); - return; + return TESTS_DONE; } -static int __user_text func(void *arg) { return 0; } - -static int ktf_unit_tests(void) { - usermode_call(func, NULL); - - printk("\nLet the UNITTESTs begin\n"); - printk("Commandline parsing: %s\n", kernel_cmdline); - - if (strcmp(opt_string, "foo")) { - printk("String parameter opt_string != foo: %s\n", opt_string); - BUG(); - } - else { - printk("String parameter parsing works!\n"); - } - - if (strcmp(opt_badstring, "tool")) { - printk("String parameter opt_badstring != tool: %s\n", opt_badstring); - BUG(); - } - else { - printk("String parameter parsing works!\n"); - } - - if (opt_ulong != 42) { - printk("Integer parameter opt_ulong != 42: %d\n", opt_ulong); - BUG(); - } - else { - printk("Integer parameter parsing works!\n"); - } +void test_main(void) { + char *name; + test_fn *fn = NULL; + unsigned n = 0; - if (!opt_bool || !opt_booltwo) { - printk("Boolean parameter opt_bool != true: %d\n", opt_bool); - printk("Boolean parameter opt_booltwo != true: %d\n", opt_booltwo); - BUG(); - } - else { - printk("Boolean parameter parsing works!\n"); - } + printk("\nRunning tests\n"); - printk("\nMemmove testing:\n"); - (void) memmove(memmove_string, opt_string, sizeof(opt_string)); - if (!strcmp(memmove_string, opt_string)) { - printk("Moving around memory works!\n"); - } - else { - printk("Memmove'ing did not work: %s (%p) != %s (%p)\n", memmove_string, - memmove_string, opt_string, opt_string); - } + while (get_next_test(&fn, &name) == TESTS_FOUND) { + int rc; - src = (char *) range_string; - dst = (char *) range_string + 2; - (void) memmove(dst, src, 4); - if (!strcmp(range_string, "121234")) { - printk("Moving around memory with overlaping ranges works!\n"); + printk("Running test: %s\n", name); + rc = fn(NULL); + printk("Test %s returned: 0x%x\n", name, rc); + n++; } - else { - printk("Overlaping memmove'ing did not work: %s != %s\n", range_string, "121234"); - } - - cpu_freq_expect("Intel(R) Xeon(R) CPU E3-1270 V2 @ 3.50GHz", 3500000000); - cpu_freq_expect("Intel(R) Celeron(R) CPU J1900 @ 1.99GHz", 1990000000); - cpu_freq_expect("AMD G-T48L Processor", 0); - cpu_freq_expect("Intel(R) Atom(TM) CPU E3815 @ 1.46GHz", 1460000000); - cpu_freq_expect("Intel(R) Atom(TM) CPU E620 @ 600MHz", 600000000); - cpu_freq_expect("VIA C7 Processor 1000MHz", 1000000000); - cpu_freq_expect("Intel(R) Core(TM) i7 CPU 950 @ 3.07GHz", 3070000000); - cpu_freq_expect("AMD Ryzen Threadripper 1950X 16-Core Processor", 0); - cpu_freq_expect("Prototyp Amazing Foo One @ 1GHz", 1000000000); - cpu_freq_expect("Prototyp Amazing Foo Two @ 1.00GHz", 1000000000); - - printk("Long mode to real mode transition:\n"); - long_to_real(); - - return 0; -} -#else -static int ktf_unit_tests(void) { return 0; } -#endif - -void test_main(void) { - printk("\nTest:\n"); - - ktf_unit_tests(); wait_for_all_tasks(); - printk("Test done\n"); + printk("Tests completed: %u\n", n); } diff --git a/tests/unittests.c b/tests/unittests.c new file mode 100644 index 00000000..cc4a3c1a --- /dev/null +++ b/tests/unittests.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2021 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +extern char *kernel_cmdline; +#include + +static char opt_string[4]; +string_cmd("string", opt_string); + +static char opt_badstring[5]; +string_cmd("badstring", opt_badstring); + +static unsigned long opt_ulong; +ulong_cmd("integer", opt_ulong); + +static bool opt_bool = 0; +bool_cmd("boolean", opt_bool); + +static bool opt_booltwo = 0; +bool_cmd("booleantwo", opt_booltwo); + +static char memmove_string[4]; +static char range_string[] = "123456"; +static char *src, *dst; + +static void cpu_freq_expect(const char *cpu_str, uint64_t expectation) { + uint64_t result = get_cpu_freq(cpu_str); + if (result != expectation) { + printk("Could not parse cpu frequency from string '%s'\n", cpu_str); + printk("Expectation vs. result: %llu vs. %llu\n", expectation, result); + BUG(); + } + + printk("Got CPU string '%s' and frequency '%llu'\n", cpu_str, result); + return; +} + +static int __user_text func(void *arg) { return 0; } + +int unit_tests(void *_unused) { + usermode_call(func, NULL); + + printk("\nLet the UNITTESTs begin\n"); + printk("Commandline parsing: %s\n", kernel_cmdline); + + if (strcmp(opt_string, "foo")) { + printk("String parameter opt_string != foo: %s\n", opt_string); + BUG(); + } + else { + printk("String parameter parsing works!\n"); + } + + if (strcmp(opt_badstring, "tool")) { + printk("String parameter opt_badstring != tool: %s\n", opt_badstring); + BUG(); + } + else { + printk("String parameter parsing works!\n"); + } + + if (opt_ulong != 42) { + printk("Integer parameter opt_ulong != 42: %d\n", opt_ulong); + BUG(); + } + else { + printk("Integer parameter parsing works!\n"); + } + + if (!opt_bool || !opt_booltwo) { + printk("Boolean parameter opt_bool != true: %d\n", opt_bool); + printk("Boolean parameter opt_booltwo != true: %d\n", opt_booltwo); + BUG(); + } + else { + printk("Boolean parameter parsing works!\n"); + } + + printk("\nMemmove testing:\n"); + (void) memmove(memmove_string, opt_string, sizeof(opt_string)); + if (!strcmp(memmove_string, opt_string)) { + printk("Moving around memory works!\n"); + } + else { + printk("Memmove'ing did not work: %s (%p) != %s (%p)\n", memmove_string, + memmove_string, opt_string, opt_string); + } + + src = (char *) range_string; + dst = (char *) range_string + 2; + (void) memmove(dst, src, 4); + if (!strcmp(range_string, "121234")) { + printk("Moving around memory with overlaping ranges works!\n"); + } + else { + printk("Overlaping memmove'ing did not work: %s != %s\n", range_string, "121234"); + } + + cpu_freq_expect("Intel(R) Xeon(R) CPU E3-1270 V2 @ 3.50GHz", 3500000000); + cpu_freq_expect("Intel(R) Celeron(R) CPU J1900 @ 1.99GHz", 1990000000); + cpu_freq_expect("AMD G-T48L Processor", 0); + cpu_freq_expect("Intel(R) Atom(TM) CPU E3815 @ 1.46GHz", 1460000000); + cpu_freq_expect("Intel(R) Atom(TM) CPU E620 @ 600MHz", 600000000); + cpu_freq_expect("VIA C7 Processor 1000MHz", 1000000000); + cpu_freq_expect("Intel(R) Core(TM) i7 CPU 950 @ 3.07GHz", 3070000000); + cpu_freq_expect("AMD Ryzen Threadripper 1950X 16-Core Processor", 0); + cpu_freq_expect("Prototyp Amazing Foo One @ 1GHz", 1000000000); + cpu_freq_expect("Prototyp Amazing Foo Two @ 1.00GHz", 1000000000); + + printk("Long mode to real mode transition:\n"); + long_to_real(); + + return 0; +}