test: Add userspace tests
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#include "utest.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "syscall.h"
|
||||
|
||||
constexpr size_t LINE_MAX = 240;
|
||||
|
||||
extern const struct utest_case __start_utest_cases[];
|
||||
extern const struct utest_case __stop_utest_cases[];
|
||||
|
||||
size_t strlen(const char* str) {
|
||||
size_t len = 0;
|
||||
while (str[len])
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
int memcmp(const void* s1, const void* s2, size_t size) {
|
||||
const uint8_t* a = s1;
|
||||
const uint8_t* b = s2;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (a[i] != b[i])
|
||||
return a[i] < b[i] ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* memcpy(void* restrict dest, const void* restrict src, size_t n) {
|
||||
uint8_t* d = dest;
|
||||
const uint8_t* s = src;
|
||||
for (size_t i = 0; i < n; i++)
|
||||
d[i] = s[i];
|
||||
return dest;
|
||||
}
|
||||
|
||||
void* memset(void* dest, int value, size_t n) {
|
||||
uint8_t* d = dest;
|
||||
for (size_t i = 0; i < n; i++)
|
||||
d[i] = (uint8_t)value;
|
||||
return dest;
|
||||
}
|
||||
|
||||
bool utest_streq(const char* a, const char* b) {
|
||||
if (!a || !b)
|
||||
return a == b;
|
||||
while (*a && *a == *b) {
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
return *a == *b;
|
||||
}
|
||||
|
||||
struct line {
|
||||
char buf[LINE_MAX];
|
||||
size_t len;
|
||||
};
|
||||
|
||||
static void put(struct line* line, char c) {
|
||||
if (line->len < sizeof(line->buf) - 1)
|
||||
line->buf[line->len++] = c;
|
||||
}
|
||||
|
||||
static void put_str(struct line* line, const char* str) {
|
||||
for (; *str; str++)
|
||||
put(line, *str);
|
||||
}
|
||||
|
||||
static void put_num(struct line* line, unsigned long long value, unsigned base, bool negative, bool prefix) {
|
||||
char digits[24];
|
||||
size_t n = 0;
|
||||
do {
|
||||
digits[n++] = "0123456789abcdef"[value % base];
|
||||
value /= base;
|
||||
} while (value);
|
||||
if (negative)
|
||||
put(line, '-');
|
||||
if (prefix)
|
||||
put_str(line, "0x");
|
||||
while (n)
|
||||
put(line, digits[--n]);
|
||||
}
|
||||
|
||||
static void format(struct line* line, const char* fmt, va_list args) {
|
||||
for (; *fmt; fmt++) {
|
||||
if (*fmt != '%') {
|
||||
put(line, *fmt);
|
||||
continue;
|
||||
}
|
||||
fmt++;
|
||||
bool alt = false;
|
||||
if (*fmt == '#') {
|
||||
alt = true;
|
||||
fmt++;
|
||||
}
|
||||
int longs = 0;
|
||||
bool size = false;
|
||||
for (;; fmt++) {
|
||||
if (*fmt == 'l')
|
||||
longs++;
|
||||
else if (*fmt == 'z')
|
||||
size = true;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
switch (*fmt) {
|
||||
case 'd':
|
||||
case 'i': {
|
||||
long long v = size ? (long long)va_arg(args, ptrdiff_t)
|
||||
: longs ? va_arg(args, long long)
|
||||
: va_arg(args, int);
|
||||
put_num(line, v < 0 ? -(unsigned long long)v : (unsigned long long)v, 10, v < 0, false);
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
case 'x': {
|
||||
unsigned long long v = size ? va_arg(args, size_t)
|
||||
: longs ? va_arg(args, unsigned long long)
|
||||
: va_arg(args, unsigned int);
|
||||
put_num(line, v, *fmt == 'x' ? 16 : 10, false, alt && *fmt == 'x');
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
put_num(line, (uintptr_t)va_arg(args, void*), 16, false, true);
|
||||
break;
|
||||
case 's': {
|
||||
const char* s = va_arg(args, const char*);
|
||||
put_str(line, s ? s : "(null)");
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
put(line, (char)va_arg(args, int));
|
||||
break;
|
||||
case '%':
|
||||
put(line, '%');
|
||||
break;
|
||||
default:
|
||||
put(line, '?');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void emit(struct line* line) {
|
||||
line->buf[line->len] = 0;
|
||||
sys_syslog(SYSLOG_INFO, line->buf, line->len);
|
||||
}
|
||||
|
||||
void utest_log(const char* fmt, ...) {
|
||||
struct line line = {};
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
format(&line, fmt, args);
|
||||
va_end(args);
|
||||
emit(&line);
|
||||
}
|
||||
|
||||
void utest_fail(struct utest* test, const char* file, int line_nr, const char* fmt, ...) {
|
||||
struct line line = {};
|
||||
put_str(&line, "# ");
|
||||
put_str(&line, test->suite);
|
||||
put(&line, '.');
|
||||
put_str(&line, test->name);
|
||||
put_str(&line, ": ");
|
||||
put_str(&line, file);
|
||||
put(&line, ':');
|
||||
put_num(&line, (unsigned)line_nr, 10, false, false);
|
||||
put_str(&line, ": ");
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
format(&line, fmt, args);
|
||||
va_end(args);
|
||||
emit(&line);
|
||||
test->failed = true;
|
||||
}
|
||||
|
||||
void utest_run_all() {
|
||||
size_t index = 0;
|
||||
for (const struct utest_case* c = __start_utest_cases; c < __stop_utest_cases; c++) {
|
||||
struct utest test = {.suite = c->suite, .name = c->name};
|
||||
c->func(&test);
|
||||
|
||||
index++;
|
||||
if (test.skipped)
|
||||
utest_log("ok %zu %s.%s # SKIP %s", index, c->suite, c->name, test.skip_reason ? test.skip_reason : "");
|
||||
else if (c->xfail_reason)
|
||||
utest_log(
|
||||
"%s %zu %s.%s # TODO %s",
|
||||
test.failed ? "not ok" : "ok",
|
||||
index,
|
||||
c->suite,
|
||||
c->name,
|
||||
c->xfail_reason
|
||||
);
|
||||
else
|
||||
utest_log("%s %zu %s.%s", test.failed ? "not ok" : "ok", index, c->suite, c->name);
|
||||
}
|
||||
|
||||
utest_log("1..%zu", index);
|
||||
}
|
||||
|
||||
int main() {
|
||||
utest_run_all();
|
||||
for (;;)
|
||||
;
|
||||
}
|
||||
Reference in New Issue
Block a user