sylveos

Toy Operating System
Log | Files | Refs

commit eb19d938ddd0c281cbea8919a15f2e60ff501746
parent 097285f7d59781c754fb21ae42dce8bf46fb1dda
Author: Sylvia Ivory <git@sivory.net>
Date:   Thu, 15 Jan 2026 19:22:55 -0800

Compile test suite

Diffstat:
Mbuild.zig | 20+++++++++++++++++---
Ainclude/fake-pi.h | 1+
Ainclude/rpi.h | 13+++++++++++++
Ainclude/test-helper.h | 29+++++++++++++++++++++++++++++
Asrc/test-runner.zig | 47+++++++++++++++++++++++++++++++++++++++++++++++
Atests/gpio.zig | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 159 insertions(+), 3 deletions(-)

diff --git a/build.zig b/build.zig @@ -32,8 +32,6 @@ fn add_exe_fake_pi(exe_name: []const u8, path: std.Build.LazyPath, b: *std.Build .name = exe_name_fake, .linkage = .dynamic, .root_module = fake_pi, - .use_lld = true, - .use_llvm = true, }); b.installArtifact(exe); @@ -140,14 +138,30 @@ fn handle_lab(name: []const u8, b: *std.Build, add_exe: add_exe_ty) !void { } } +fn handle_test(name: []const u8, b: *std.Build) !void { + if (std.mem.eql(u8, name, "gpio")) { + try @import("tests/gpio.zig").build(b); + } else { + @panic("invalid test name"); + } +} + pub fn build(b: *std.Build) !void { const fake_pi = b.option(bool, "fake-pi", "build with fake-pi"); const add_exe = if (fake_pi orelse false) &add_exe_fake_pi else &add_exe_real; const lab_opt = b.option([]const u8, "lab", "compile specific lab"); - if (lab_opt) |lab_name| { try handle_lab(lab_name, b, add_exe); + + return; + } + + const test_opt = b.option([]const u8, "test", "compile specific test suite"); + if (test_opt) |test_name| { + try handle_test(test_name, b); + + return; } _ = try add_exe(@tagName(config.name), b.path("src/root.zig"), b); diff --git a/include/fake-pi.h b/include/fake-pi.h @@ -0,0 +1 @@ +#include "fake-random.h" diff --git a/include/rpi.h b/include/rpi.h @@ -0,0 +1,13 @@ +void gpio_set_input(unsigned pin); +void gpio_set_output(unsigned pin); + +void gpio_write(unsigned pin, unsigned val); +int gpio_read(unsigned pin); + +void gpio_set_on(unsigned pin); +void gpio_set_off(unsigned pin); + +int output(const char *format, ...); +void delay_cycles(unsigned ticks); + +void notmain(void); diff --git a/include/test-helper.h b/include/test-helper.h @@ -0,0 +1,29 @@ +#include "rpi.h" +#include "pi-random.h" + +// run <fp>: +// 1. on the values [0..32) +// 2. with random input <ntrials> times. +static inline void +run_test(const char *name, void (*fp)(unsigned), int ntrials) { + printf("testing: <%s>\n", name); + // test pins 0..32, then a bunch of random. + for(int i = 0; i < 32; i++) + fp(i); + for(int i = 0; i < ntrials; i++) + fp(fake_random()); +} + +// run the different tests <ntrials> times. +static inline void test_gpio_set_output(int ntrials) { + run_test(__FUNCTION__, gpio_set_output, ntrials); +} +static inline void test_gpio_set_input(int ntrials) { + run_test(__FUNCTION__, gpio_set_input, ntrials); +} +static inline void test_gpio_set_on(int ntrials) { + run_test(__FUNCTION__, gpio_set_on, ntrials); +} +static inline void test_gpio_set_off(int ntrials) { + run_test(__FUNCTION__, gpio_set_off, ntrials); +} diff --git a/src/test-runner.zig b/src/test-runner.zig @@ -0,0 +1,47 @@ +const std = @import("std"); + +const gpio = @import("devices/gpio.zig"); +const root = @import("root.zig"); +const c = @cImport({ + @cInclude("rpi.h"); +}); + +export fn output(format: [*:0]const u8, ...) u32 { + var ap = @cVaStart(); + defer @cVaEnd(&ap); + + _ = format; + // TODO; is format necessary? + return 0; +} + +export fn gpio_read(pin: u8) bool { + return gpio.read(pin) catch false; +} +export fn gpio_write(pin: u8, state: bool) void { + gpio.write(pin, state) catch {}; +} + +export fn gpio_set_on(pin: u8) void { + gpio.set_on(pin) catch {}; +} +export fn gpio_set_off(pin: u8) void { + gpio.set_off(pin) catch {}; +} + +export fn gpio_set_input(pin: u8) void { + gpio.set_input(pin) catch {}; +} +export fn gpio_set_output(pin: u8) void { + gpio.set_output(pin) catch {}; +} + +export fn delay_cycles(cycles: u32) void { + // TODO; + _ = cycles; +} + +pub fn kmain() void { + gpio.initialize(0x2000_0000); + c.notmain(); +} diff --git a/tests/gpio.zig b/tests/gpio.zig @@ -0,0 +1,52 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) !void { + const relative_path = b.path("tests/gpio"); + const path = try relative_path.getPath3(b, null).toString(b.allocator); + defer b.allocator.free(path); + + var dir = try std.fs.openDirAbsolute(path, .{ .access_sub_paths = false, .iterate = true }); + defer dir.close(); + + var iter = dir.iterate(); + while (try iter.next()) |entry| { + if (entry.kind != .file) continue; + if (!std.mem.endsWith(u8, entry.name, ".c")) continue; + + // C files + const runner = b.createModule(.{ + .root_source_file = b.path("src/test-runner.zig"), + .target = b.graph.host, + .optimize = .ReleaseSafe, + .link_libc = true, + }); + runner.addCSourceFile(.{ .file = b.path(b.pathJoin(&.{ "tests/gpio", entry.name })) }); + runner.addIncludePath(b.path("include/")); + + // TODO; this is duplicated code + const fake_pi = b.createModule(.{ + .root_source_file = b.path("fake-pi/main.zig"), + .target = b.graph.host, + .optimize = .ReleaseFast, + }); + + fake_pi.addImport("boot", runner); + + // TODO; C should get its own module so we can have fake-pi be in debug for faster comp + const CFlags = &.{"-O2"}; + fake_pi.addCSourceFile(.{ .file = b.path("include/fake-random.c"), .flags = CFlags }); + fake_pi.addCSourceFile(.{ .file = b.path("include/pi-random.c"), .flags = CFlags }); + fake_pi.addIncludePath(b.path("include/")); + + const exe_name = try std.fmt.allocPrint(b.allocator, "{s}-test", .{entry.name}); + defer b.allocator.free(exe_name); + + const exe = b.addExecutable(.{ + .name = exe_name, + .linkage = .dynamic, + .root_module = fake_pi, + }); + + b.installArtifact(exe); + } +}