Build Zig skeleton with canonical checks
This commit is contained in:
@@ -29,7 +29,14 @@ redgate health
|
||||
|
||||
Every implementation slice needs carry-their-weight tests: regular tests that prove the intended payoff works, plus adversarial tests for realistic failure, boundary, or regression cases. Prefer protocol/headless/replay/fixture tests over brittle UI snapshots unless rendering behavior is the claim. See req: testing/001 through req: testing/004.
|
||||
|
||||
No implementation build/test command exists yet. When the Zig project is created, record the canonical build/test command here.
|
||||
Canonical implementation checks:
|
||||
|
||||
```sh
|
||||
zig build
|
||||
zig build test
|
||||
zig build run -- --help
|
||||
```
|
||||
|
||||
|
||||
## Work tracking
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "mim",
|
||||
.root_module = mod,
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
const run_step = b.step("run", "Run mim");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
const tests = b.addTest(.{
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}),
|
||||
});
|
||||
const test_cmd = b.addRunArtifact(tests);
|
||||
const test_step = b.step("test", "Run unit tests");
|
||||
test_step.dependOn(&test_cmd.step);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub const version = "0.1.0-dev";
|
||||
|
||||
const help_text =
|
||||
\\mim - mobile-first terminal code editor for SSH sessions
|
||||
\\
|
||||
\\Usage:
|
||||
\\ mim [--help]
|
||||
\\ mim [--version]
|
||||
\\
|
||||
\\This skeleton intentionally starts with only the canonical smoke path.
|
||||
\\Future editor behavior is added through verified product slices.
|
||||
\\
|
||||
;
|
||||
|
||||
const Command = union(enum) {
|
||||
help,
|
||||
version,
|
||||
smoke,
|
||||
unknown: []const u8,
|
||||
};
|
||||
|
||||
fn parseArgs(args: []const []const u8) Command {
|
||||
if (args.len <= 1) return .smoke;
|
||||
|
||||
const arg = args[1];
|
||||
if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) return .help;
|
||||
if (std.mem.eql(u8, arg, "--version")) return .version;
|
||||
return .{ .unknown = arg };
|
||||
}
|
||||
|
||||
fn versionText() []const u8 {
|
||||
return "mim " ++ version ++ "\n";
|
||||
}
|
||||
|
||||
pub fn main(init: std.process.Init) !u8 {
|
||||
var args = std.process.Args.Iterator.init(init.minimal.args);
|
||||
_ = args.skip();
|
||||
|
||||
const first_arg = args.next();
|
||||
const command = if (first_arg) |arg|
|
||||
parseArgs(&.{ "mim", arg })
|
||||
else
|
||||
parseArgs(&.{"mim"});
|
||||
|
||||
const stdout = std.Io.File.stdout();
|
||||
const stderr = std.Io.File.stderr();
|
||||
|
||||
switch (command) {
|
||||
.help => try stdout.writeStreamingAll(init.io, help_text),
|
||||
.version => try stdout.writeStreamingAll(init.io, versionText()),
|
||||
.smoke => try stdout.writeStreamingAll(init.io, help_text),
|
||||
.unknown => |arg| {
|
||||
try stderr.writeStreamingAll(init.io, "mim: unknown argument '");
|
||||
try stderr.writeStreamingAll(init.io, arg);
|
||||
try stderr.writeStreamingAll(init.io, "'\nTry 'mim --help'.\n");
|
||||
return 64;
|
||||
},
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
test "regular: help text names the binary and smoke boundary" {
|
||||
try std.testing.expect(std.mem.indexOf(u8, help_text, "mim - mobile-first terminal code editor") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, help_text, "canonical smoke path") != null);
|
||||
}
|
||||
|
||||
test "regular: version output is stable enough for smoke checks" {
|
||||
try std.testing.expectEqualStrings("mim 0.1.0-dev\n", versionText());
|
||||
}
|
||||
|
||||
test "adversarial: unknown arguments are rejected instead of ignored" {
|
||||
const argv = [_][]const u8{ "mim", "--definitely-not-supported" };
|
||||
const parsed = parseArgs(&argv);
|
||||
switch (parsed) {
|
||||
.unknown => |arg| try std.testing.expectEqualStrings("--definitely-not-supported", arg),
|
||||
else => return error.ExpectedUnknownArgument,
|
||||
}
|
||||
}
|
||||
|
||||
test "adversarial: extra positional arguments are not treated as files yet" {
|
||||
const argv = [_][]const u8{ "mim", "some-file.zig" };
|
||||
const parsed = parseArgs(&argv);
|
||||
switch (parsed) {
|
||||
.unknown => |arg| try std.testing.expectEqualStrings("some-file.zig", arg),
|
||||
else => return error.ExpectedUnknownArgument,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user