Skip to content

Commit

Permalink
tests: refactor test execution
Browse files Browse the repository at this point in the history
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 <ahmeddan amazon c;0m >
  • Loading branch information
82marbag authored and wipawel committed Jul 6, 2021
1 parent 103dd3f commit 6c96697
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 120 deletions.
3 changes: 0 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion grub/boot/grub/grub-test.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
32 changes: 32 additions & 0 deletions include/test.h
Original file line number Diff line number Diff line change
@@ -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 */
156 changes: 40 additions & 116 deletions tests/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,134 +24,58 @@
*/
#include <console.h>
#include <ktf.h>
#include <real_mode.h>
#include <sched.h>

#ifdef KTF_UNIT_TEST
#include <cmdline.h>
#include <cpuid.h>
#include <string.h>
#include <symbols.h>
#include <test.h>

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 <cmdline.h>

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);
}
144 changes: 144 additions & 0 deletions tests/unittests.c
Original file line number Diff line number Diff line change
@@ -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 <console.h>
#include <cpuid.h>
#include <ktf.h>
#include <real_mode.h>
#include <sched.h>
#include <string.h>
#include <symbols.h>
#include <test.h>

extern char *kernel_cmdline;
#include <cmdline.h>

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;
}

0 comments on commit 6c96697

Please sign in to comment.