commit eb19d938ddd0c281cbea8919a15f2e60ff501746
parent 097285f7d59781c754fb21ae42dce8bf46fb1dda
Author: Sylvia Ivory <git@sivory.net>
Date: Thu, 15 Jan 2026 19:22:55 -0800
Compile test suite
Diffstat:
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);
+ }
+}