Add runtime testing harness
This commit is contained in:
parent
f8f7e40535
commit
35cfbd1686
9 changed files with 103 additions and 13 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -43,3 +43,5 @@ zig-cache
|
||||||
|
|
||||||
# Build dir
|
# Build dir
|
||||||
bin/*
|
bin/*
|
||||||
|
*.pyc
|
||||||
|
__pycache__
|
||||||
|
|
36
build.zig
36
build.zig
|
@ -25,7 +25,9 @@ pub fn build(b: *Builder) void {
|
||||||
var build_path = b.option([]const u8, "build-path", "path to build to") orelse "bin";
|
var build_path = b.option([]const u8, "build-path", "path to build to") orelse "bin";
|
||||||
var src_path = b.option([]const u8, "source-path", "path to source") orelse "src";
|
var src_path = b.option([]const u8, "source-path", "path to source") orelse "src";
|
||||||
var target = b.option([]const u8, "target", "target to build/run for") orelse "x86";
|
var target = b.option([]const u8, "target", "target to build/run for") orelse "x86";
|
||||||
|
const rt_test = b.option(bool, "rt-test", "enable/disable runtime testing") orelse false;
|
||||||
const builtin_target = if (mem.eql(u8, target, "x86")) builtin.Arch.i386 else unreachable;
|
const builtin_target = if (mem.eql(u8, target, "x86")) builtin.Arch.i386 else unreachable;
|
||||||
|
const zig_path = b.option([]const u8, "zig-path", "the path to the zig binary to use for rt testing") orelse "/snap/bin/zig";
|
||||||
|
|
||||||
b.makePath(build_path) catch unreachable;
|
b.makePath(build_path) catch unreachable;
|
||||||
var grub_path = concat(b.allocator, build_path, "/iso/boot/grub") catch unreachable;
|
var grub_path = concat(b.allocator, build_path, "/iso/boot/grub") catch unreachable;
|
||||||
|
@ -60,20 +62,25 @@ pub fn build(b: *Builder) void {
|
||||||
b.default_step.dependOn(link_step);
|
b.default_step.dependOn(link_step);
|
||||||
for (iso_step.toSlice()) |step| b.default_step.dependOn(step);
|
for (iso_step.toSlice()) |step| b.default_step.dependOn(step);
|
||||||
|
|
||||||
buildRun(b, builtin_target, build_path, iso_path.toSlice(), debug);
|
buildRun(b, builtin_target, build_path, iso_path.toSlice(), debug, rt_test);
|
||||||
buildDebug(b);
|
buildDebug(b);
|
||||||
buildTest(b, src_path);
|
buildTest(b, src_path, rt_test, target, zig_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn buildTest(b: *Builder, src_path: []const u8) void {
|
fn buildTest(b: *Builder, src_path: []const u8, rt_test: bool, target: []const u8, zig_path: []const u8) void {
|
||||||
const step = b.step("test", "Run all tests");
|
const step = b.step("test", "Run tests");
|
||||||
const src_path2 = concat(b.allocator, src_path, "/") catch unreachable;
|
if (rt_test) {
|
||||||
for (src_files.toSlice()) |file| {
|
const script = b.addSystemCommand([][]const u8{ "python3", "test/rt-test.py", target, zig_path});
|
||||||
var file_src = concat(b.allocator, src_path2.toSlice(), file) catch unreachable;
|
step.dependOn(&script.step);
|
||||||
file_src.append(".zig") catch unreachable;
|
} else {
|
||||||
const tst = b.addTest(file_src.toSlice());
|
const src_path2 = concat(b.allocator, src_path, "/") catch unreachable;
|
||||||
tst.setMainPkgPath(".");
|
for (src_files.toSlice()) |file| {
|
||||||
step.dependOn(&tst.step);
|
var file_src = concat(b.allocator, src_path2.toSlice(), file) catch unreachable;
|
||||||
|
file_src.append(".zig") catch unreachable;
|
||||||
|
const tst = b.addTest(file_src.toSlice());
|
||||||
|
tst.setMainPkgPath(".");
|
||||||
|
step.dependOn(&tst.step);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +96,7 @@ fn buildDebug(b: *Builder) void {
|
||||||
step.dependOn(&cmd.step);
|
step.dependOn(&cmd.step);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn buildRun(b: *Builder, target: builtin.Arch, build_path: []const u8, iso_path: []const u8, debug: bool) void {
|
fn buildRun(b: *Builder, target: builtin.Arch, build_path: []const u8, iso_path: []const u8, debug: bool, rt_test: bool) void {
|
||||||
const step = b.step("run", "Run with qemu");
|
const step = b.step("run", "Run with qemu");
|
||||||
const qemu = if (target == builtin.Arch.i386) "qemu-system-i386" else unreachable;
|
const qemu = if (target == builtin.Arch.i386) "qemu-system-i386" else unreachable;
|
||||||
var qemu_flags = ArrayList([]const u8).init(b.allocator);
|
var qemu_flags = ArrayList([]const u8).init(b.allocator);
|
||||||
|
@ -107,6 +114,11 @@ fn buildRun(b: *Builder, target: builtin.Arch, build_path: []const u8, iso_path:
|
||||||
"-s",
|
"-s",
|
||||||
"-S",
|
"-S",
|
||||||
}) catch unreachable;
|
}) catch unreachable;
|
||||||
|
if (rt_test)
|
||||||
|
qemu_flags.appendSlice([][]const u8{
|
||||||
|
"-display",
|
||||||
|
"none"
|
||||||
|
}) catch unreachable;
|
||||||
const cmd = b.addSystemCommand(qemu_flags.toSlice());
|
const cmd = b.addSystemCommand(qemu_flags.toSlice());
|
||||||
step.dependOn(&cmd.step);
|
step.dependOn(&cmd.step);
|
||||||
}
|
}
|
||||||
|
|
|
@ -305,4 +305,5 @@ pub fn init() void {
|
||||||
|
|
||||||
// Load the TSS
|
// Load the TSS
|
||||||
arch.ltr();
|
arch.ltr();
|
||||||
|
log.logInfo("Done\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,4 +122,5 @@ pub fn closeInterruptGate(index: u8) void {
|
||||||
pub fn init() void {
|
pub fn init() void {
|
||||||
log.logInfo("Init idt\n");
|
log.logInfo("Init idt\n");
|
||||||
arch.lidt(&idt_ptr);
|
arch.lidt(&idt_ptr);
|
||||||
|
log.logInfo("Done\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,6 +273,7 @@ pub fn getFrequency() u32 {
|
||||||
/// Initialise the PIT with a handler to IRQ 0.
|
/// Initialise the PIT with a handler to IRQ 0.
|
||||||
///
|
///
|
||||||
pub fn init() void {
|
pub fn init() void {
|
||||||
|
log.logInfo("Init pit\n");
|
||||||
// Set up counter 0 at 1000hz in a square wave mode counting in binary
|
// Set up counter 0 at 1000hz in a square wave mode counting in binary
|
||||||
const f: u32 = 10000;
|
const f: u32 = 10000;
|
||||||
setupCounter(OCW_SELECT_COUNTER_0, f, OCW_MODE_SQUARE_WAVE_GENERATOR | OCW_BINARY_COUNT_BINARY);
|
setupCounter(OCW_SELECT_COUNTER_0, f, OCW_MODE_SQUARE_WAVE_GENERATOR | OCW_BINARY_COUNT_BINARY);
|
||||||
|
@ -281,4 +282,5 @@ pub fn init() void {
|
||||||
|
|
||||||
// Installs 'pitHandler' to IRQ0 (pic.IRQ_PIT)
|
// Installs 'pitHandler' to IRQ0 (pic.IRQ_PIT)
|
||||||
irq.registerIrq(pic.IRQ_PIT, pitHandler);
|
irq.registerIrq(pic.IRQ_PIT, pitHandler);
|
||||||
|
log.logInfo("Done\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,10 +32,11 @@ pub export fn kmain(mb_info: *multiboot.multiboot_info_t, mb_magic: u32) void {
|
||||||
|
|
||||||
log.logInfo("Init arch " ++ @tagName(builtin.arch) ++ "\n");
|
log.logInfo("Init arch " ++ @tagName(builtin.arch) ++ "\n");
|
||||||
arch.init(&mem_profile, &fixed_allocator.allocator);
|
arch.init(&mem_profile, &fixed_allocator.allocator);
|
||||||
|
log.logInfo("Arch init done\n");
|
||||||
vga.init();
|
vga.init();
|
||||||
tty.init();
|
tty.init();
|
||||||
|
|
||||||
log.logInfo("Finished init\n");
|
log.logInfo("Init done\n");
|
||||||
tty.print("Hello Pluto from kernel :)\n");
|
tty.print("Hello Pluto from kernel :)\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -644,6 +644,7 @@ pub fn init() void {
|
||||||
printLogo();
|
printLogo();
|
||||||
displayPageNumber();
|
displayPageNumber();
|
||||||
updateCursor();
|
updateCursor();
|
||||||
|
log.logInfo("Done\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resetGlobals() void {
|
fn resetGlobals() void {
|
||||||
|
|
6
test/kernel/arch/x86/rt-test.py
Normal file
6
test/kernel/arch/x86/rt-test.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
def getTestCases(TestCase):
|
||||||
|
return [
|
||||||
|
TestCase("GDT init", [r"Init gdt", r"Done"]),
|
||||||
|
TestCase("IDT init", [r"Init idt", r"Done"]),
|
||||||
|
TestCase("PIT init", [r"Init pit", r".+", "Done"])
|
||||||
|
]
|
64
test/rt-test.py
Normal file
64
test/rt-test.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import subprocess
|
||||||
|
import signal
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import datetime
|
||||||
|
import os
|
||||||
|
import importlib.util
|
||||||
|
|
||||||
|
class TestCase:
|
||||||
|
def __init__(self, name, expected):
|
||||||
|
self.name = name
|
||||||
|
self.expected = expected
|
||||||
|
|
||||||
|
def test_failure(case, exp, expected_idx, found):
|
||||||
|
print("FAILURE: %s #%d, expected '%s', found '%s'" %(case.name, expected_idx + 1, exp, found))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def test_pass(case, exp, expected_idx, found):
|
||||||
|
print("PASS: %s #%d, expected '%s', found '%s'" %(case.name, expected_idx + 1, exp, found))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
arch = sys.argv[1]
|
||||||
|
zig_path = sys.argv[2]
|
||||||
|
spec = importlib.util.spec_from_file_location("arch", "test/kernel/arch/" + arch + "/rt-test.py")
|
||||||
|
arch_module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(arch_module)
|
||||||
|
|
||||||
|
# The list of log statements to look for before arch init is called
|
||||||
|
pre_archinit_cases = [
|
||||||
|
TestCase("Arch init starts", [r"Init arch \w+"])
|
||||||
|
]
|
||||||
|
# The list of log statements to look for after arch init is called
|
||||||
|
post_archinit_cases = [
|
||||||
|
TestCase("Arch init finishes", [r"Arch init done"]),
|
||||||
|
TestCase("TTY init", [r"Init tty", r"Done"]),
|
||||||
|
TestCase("Init finishes", [r"Init done"])
|
||||||
|
]
|
||||||
|
# All log statements to look for, including the arch-specific ones
|
||||||
|
cases = pre_archinit_cases + arch_module.getTestCases(TestCase) + post_archinit_cases
|
||||||
|
|
||||||
|
if len(cases) > 0:
|
||||||
|
proc = subprocess.Popen(zig_path + " build run -Drt-test=true", stdout=subprocess.PIPE, shell=True)
|
||||||
|
case_idx = 0
|
||||||
|
# Go through the cases
|
||||||
|
while case_idx < len(cases):
|
||||||
|
case = cases[case_idx]
|
||||||
|
expected_idx = 0
|
||||||
|
# Go through the expected log messages
|
||||||
|
while expected_idx < len(case.expected):
|
||||||
|
e = "\[INFO\] " + case.expected[expected_idx]
|
||||||
|
line = proc.stdout.readline().decode("utf-8")
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
line = line.strip()
|
||||||
|
pattern = re.compile(e)
|
||||||
|
# Pass if the line matches the expected pattern, else fail
|
||||||
|
if pattern.fullmatch(line):
|
||||||
|
test_pass(case, e, expected_idx, line)
|
||||||
|
else:
|
||||||
|
test_failure(case, e, expected_idx, line)
|
||||||
|
expected_idx += 1
|
||||||
|
case_idx += 1
|
||||||
|
proc.kill()
|
||||||
|
sys.exit(0)
|
Loading…
Reference in a new issue