-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ktest is pretty simple and based off of some of my other C unit testing frameworks. it is based off of two structures, the `struct ktest_module` and `struct ktest_unit`. Every module has an array of `struct ktest_unit` entrise, which are called in series. The `struct ktest_unit` has a callback to run that set of tests, and then returns the number of failures. All the failures are collected together and reported at the end. It also includes some convience macros in the form of `ktest_assert_*` macros, which use _Geenric to detect the type of the input and do the correct comparison. We make use of a new `.ktest` section in the binary to find all of our `struct ktest_module` entries at runtime. The macro __ktest can be used to place a module into that section. Testing can be turned on or off via the `CONFIG_KERNEL_TESTS` config option.
- Loading branch information
Showing
9 changed files
with
305 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ SECTIONS | |
*(.text) | ||
*(.rodata) | ||
*(.rodata.*) | ||
KTEST_SECTION | ||
} | ||
|
||
/* Read-write data (initialized) */ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
#ifndef INCLUDE_PROTURA_KTEST_H | ||
#define INCLUDE_PROTURA_KTEST_H | ||
|
||
#include <protura/types.h> | ||
|
||
struct ktest; | ||
|
||
struct ktest_unit { | ||
int (*test) (struct ktest *); | ||
const char *name; | ||
}; | ||
|
||
#define KTEST_UNIT_INIT(nm, t) \ | ||
{ \ | ||
.test = (t), \ | ||
.name = (nm), \ | ||
} | ||
|
||
struct ktest_module { | ||
const char *name; | ||
const struct ktest_unit *tests; | ||
size_t test_count; | ||
}; | ||
|
||
#define __ktest __used __section(".ktest") | ||
|
||
enum ktest_value_type { | ||
KTEST_VALUE_INT64, | ||
KTEST_VALUE_UINT64, | ||
KTEST_VALUE_PTR, | ||
KTEST_VALUE_STR, | ||
KTEST_VALUE_MEM, | ||
}; | ||
|
||
struct ktest_value { | ||
enum ktest_value_type type; | ||
const char *value_string; | ||
union { | ||
int64_t int64; | ||
uint64_t uint64; | ||
struct { | ||
const char *ptr; | ||
size_t len; | ||
}; | ||
}; | ||
}; | ||
|
||
static inline struct ktest_value __ktest_make_int64(const char *str, int64_t int64) | ||
{ | ||
return (struct ktest_value) { .type = KTEST_VALUE_INT64, .value_string = str, .int64 = int64 }; | ||
} | ||
|
||
static inline struct ktest_value __ktest_make_uint64(const char *str, uint64_t uint64) | ||
{ | ||
return (struct ktest_value) { .type = KTEST_VALUE_UINT64, .value_string = str, .uint64 = uint64 }; | ||
} | ||
|
||
static inline struct ktest_value __ktest_make_ptr(const char *str, const void *ptr) | ||
{ | ||
return (struct ktest_value) { .type = KTEST_VALUE_PTR, .value_string = str, .ptr = ptr }; | ||
} | ||
|
||
static inline struct ktest_value __ktest_make_str(const char *str, const void *ptr) | ||
{ | ||
return (struct ktest_value) { .type = KTEST_VALUE_STR, .value_string = str, .ptr = ptr }; | ||
} | ||
|
||
static inline struct ktest_value __ktest_make_mem(const char *str, size_t len, const void *ptr) | ||
{ | ||
return (struct ktest_value) { .type = KTEST_VALUE_MEM, .value_string = str, .len = len, .ptr = ptr }; | ||
} | ||
|
||
#define __ktest_make_value(vs, v) \ | ||
_Generic((v), \ | ||
int64_t: __ktest_make_int64, \ | ||
uint64_t: __ktest_make_uint64, \ | ||
char *: __ktest_make_str, \ | ||
const char *: __ktest_make_str, \ | ||
default: _Generic((v - v), \ | ||
ptrdiff_t: __ktest_make_ptr, \ | ||
default: __ktest_make_int64 \ | ||
) \ | ||
) (vs, v) | ||
|
||
#define ktest_assert_equal(kt, expect, act) \ | ||
({ \ | ||
struct ktest_value v1 = __ktest_make_value(#expect, expect); \ | ||
struct ktest_value v2 = __ktest_make_value(#act, act); \ | ||
ktest_assert_equal_value_func((kt), &v1, &v2, __func__); \ | ||
}) | ||
|
||
#define ktest_assert_notequal(kt, expect, act) \ | ||
({ \ | ||
struct ktest_value v1 = __ktest_make_value(#expect, expect); \ | ||
struct ktest_value v2 = __ktest_make_value(#act, act); \ | ||
ktest_assert_notequal_value_func((kt), &v1, &v2, __func__); \ | ||
}) | ||
|
||
#define ktest_assert_mem(kt, expect, act, len) \ | ||
({ \ | ||
struct ktest_value v1 = __ktest_make_mem(#expect, (expect), (len)); \ | ||
struct ktest_value v2 = __ktest_make_mem(#act, (act), (len)); \ | ||
ktest_assert_notequal_value_func((kt), &v1, &v2, __func__); \ | ||
}) | ||
|
||
int ktest_assert_equal_value_func(struct ktest *, struct ktest_value *expected, struct ktest_value *actual, const char *func); | ||
int ktest_assert_notequal_value_func(struct ktest *, struct ktest_value *expected, struct ktest_value *actual, const char *func); | ||
|
||
void ktest_init(void); | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
/* | ||
* Copyright (C) 2019 Matt Kilgore | ||
* | ||
* This program is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU General Public License v2 as published by the | ||
* Free Software Foundation. | ||
*/ | ||
|
||
#include <protura/types.h> | ||
#include <protura/debug.h> | ||
#include <protura/snprintf.h> | ||
#include <protura/string.h> | ||
#include <protura/cmdline.h> | ||
#include <protura/ktest.h> | ||
|
||
#include <arch/reset.h> | ||
|
||
struct ktest { | ||
int cur_test; | ||
int total_tests; | ||
}; | ||
|
||
extern struct ktest_module __ktest_start; | ||
extern struct ktest_module __ktest_end; | ||
|
||
static int run_module(struct ktest_module *module) | ||
{ | ||
char buf[256]; | ||
const struct ktest_unit *tests = module->tests; | ||
struct ktest ktest; | ||
memset(&ktest, 0, sizeof(ktest)); | ||
int error_count = 0; | ||
|
||
kp(KP_NORMAL, "==== Starting tests for %s ====\n", module->name); | ||
|
||
int i; | ||
for (i = 0; i < module->test_count; i++) { | ||
ktest.cur_test = 0; | ||
|
||
kp(KP_NORMAL, "== #%d: %s ==\n", i, tests[i].name); | ||
|
||
int errs = (tests + i)->test (&ktest); | ||
|
||
if (errs != 0) | ||
snprintf(buf, sizeof(buf), "FAIL -> %d", errs); | ||
else | ||
snprintf(buf, sizeof(buf), "PASS"); | ||
|
||
kp(KP_NORMAL, "== Result: %s ==\n", buf); | ||
|
||
error_count += errs; | ||
} | ||
|
||
kp(KP_NORMAL, "==== Finished tests for %s ====\n", module->name); | ||
|
||
if (error_count == 0) | ||
snprintf(buf, sizeof(buf), "PASS"); | ||
else | ||
snprintf(buf, sizeof(buf), "FAIL -> %d ", error_count); | ||
kp(KP_NORMAL, "==== Result: %s ====\n", buf); | ||
|
||
return error_count; | ||
} | ||
|
||
static void run_ktest_modules(void) | ||
{ | ||
int errs = 0; | ||
struct ktest_module *start = &__ktest_start; | ||
struct ktest_module *end = &__ktest_end; | ||
|
||
for (; start < end; start++) | ||
errs += run_module(start); | ||
|
||
char buf[255]; | ||
|
||
if (!errs) | ||
snprintf(buf, sizeof(buf), "PASS"); | ||
else | ||
snprintf(buf, sizeof(buf), "FAIL -> %d", errs); | ||
|
||
kp(KP_NORMAL, "==== Full test run: %s ====\n", buf); | ||
} | ||
|
||
|
||
static int ktest_value_comp(struct ktest_value *v1, struct ktest_value *v2) | ||
{ | ||
if (v1->type != v2->type) | ||
return 1; | ||
|
||
switch (v1->type) { | ||
case KTEST_VALUE_INT64: | ||
return v1->int64 == v2->int64; | ||
|
||
case KTEST_VALUE_UINT64: | ||
return v1->uint64 == v2->uint64; | ||
|
||
case KTEST_VALUE_PTR: | ||
return v1->ptr == v2->ptr; | ||
|
||
case KTEST_VALUE_STR: | ||
return !strcmp(v1->ptr, v2->ptr); | ||
|
||
case KTEST_VALUE_MEM: | ||
return !memcmp(v1->ptr, v2->ptr, v1->len); | ||
} | ||
|
||
return 1; | ||
} | ||
|
||
#if 0 | ||
static void ktest_value_show(const char *prefix, const char *asdf, struct ktest_value *v) | ||
{ | ||
|
||
} | ||
#endif | ||
|
||
int ktest_assert_equal_value_func(struct ktest *ktest, struct ktest_value *expected, struct ktest_value *actual, const char *func) | ||
{ | ||
char buf[255]; | ||
ktest->total_tests++; | ||
ktest->cur_test++; | ||
|
||
int result = ktest_value_comp(expected, actual); | ||
|
||
if (result) | ||
snprintf(buf, sizeof(buf), "PASS"); | ||
else | ||
snprintf(buf, sizeof(buf), "FAIL"); | ||
|
||
kp(KP_NORMAL, " [%02d:%03d] %s: %s == %s: %s\n", ktest->total_tests, ktest->cur_test, func, expected->value_string, actual->value_string, buf); | ||
|
||
return !result; | ||
} | ||
|
||
int ktest_assert_notequal_value_func(struct ktest *ktest, struct ktest_value *expected, struct ktest_value *actual, const char *func) | ||
{ | ||
char buf[255]; | ||
ktest->total_tests++; | ||
ktest->cur_test++; | ||
|
||
int result = ktest_value_comp(expected, actual) == 0; | ||
|
||
if (result) | ||
snprintf(buf, sizeof(buf), "PASS"); | ||
else | ||
snprintf(buf, sizeof(buf), "FAIL"); | ||
|
||
kp(KP_NORMAL, " [%02d:%03d] %s: %s != %s: %s\n", ktest->total_tests, ktest->cur_test, func, expected->value_string, actual->value_string, buf); | ||
|
||
return !result; | ||
} | ||
|
||
void ktest_init(void) | ||
{ | ||
run_ktest_modules(); | ||
} | ||
|