Luigit
repositories / termux-janitor

termux-janitor

Interactive cleanup assistant for Termux: transparent, safe, confirmed disk reclamation.

owned by admin

tests/fixtures/cli_help.zig

Raw
const std = @import("std");
const spec_data = @import("spec_data");

pub const StandardInput = enum { ignored };
pub const WorkingDirectory = enum { empty };
pub const ExpectedOutput = enum { generated_cli_help };

pub const Fixture = struct {
    temporary: std.testing.TmpDir,
    executable: ?[:0]u8 = null,
    result: ?std.process.RunResult = null,
    argv_ready: bool = false,
    stdin_ignored: bool = false,

    pub fn init() !Fixture {
        return .{ .temporary = std.testing.tmpDir(.{ .iterate = true }) };
    }

    pub fn deinit(fixture: *Fixture) void {
        if (fixture.result) |result| {
            std.testing.allocator.free(result.stdout);
            std.testing.allocator.free(result.stderr);
        }
        if (fixture.executable) |executable| std.testing.allocator.free(executable);
        fixture.temporary.cleanup();
        fixture.* = undefined;
    }

    pub fn given_program(fixture: *Fixture, executable_name: []const u8) !void {
        std.debug.assert(fixture.executable == null);
        try std.testing.expectEqualStrings(spec_data.cli_executable, executable_name);
        fixture.executable = try std.Io.Dir.cwd().realPathFileAlloc(
            std.testing.io,
            "zig-out/bin/termux-janitor",
            std.testing.allocator,
        );
    }

    pub fn given_standard_input(fixture: *Fixture, mode: StandardInput) !void {
        std.debug.assert(!fixture.stdin_ignored);
        try std.testing.expectEqual(StandardInput.ignored, mode);
        fixture.stdin_ignored = true;
    }

    pub fn given_working_directory(fixture: *Fixture, state: WorkingDirectory) !void {
        try std.testing.expectEqual(WorkingDirectory.empty, state);
        var entries = fixture.temporary.dir.iterate();
        try std.testing.expect(try entries.next(std.testing.io) == null);
    }

    pub fn given_arguments(fixture: *Fixture, arguments: []const []const u8) !void {
        std.debug.assert(!fixture.argv_ready);
        try std.testing.expectEqual(@as(usize, 1), arguments.len);
        try std.testing.expectEqualStrings(spec_data.cliOption(.help).spelling, arguments[0]);
        fixture.argv_ready = true;
    }

    pub fn when_run(fixture: *Fixture) !void {
        const executable = fixture.executable orelse return error.ExecutableNotConfigured;
        if (!fixture.argv_ready) return error.ArgumentsNotConfigured;
        if (!fixture.stdin_ignored) return error.StandardInputNotConfigured;
        const arguments = [_][]const u8{ executable, spec_data.cliOption(.help).spelling };
        fixture.result = try std.process.run(std.testing.allocator, std.testing.io, .{
            .argv = &arguments,
            .stderr_limit = .limited(@intCast(spec_data.limit_value.captured_stderr_bytes)),
            .stdout_limit = .limited(@intCast(spec_data.limit_value.captured_stdout_bytes)),
            .reserve_amount = spec_data.cli_help.len,
            .cwd = .{ .dir = fixture.temporary.dir },
            .environ_map = null,
            .expand_arg0 = .no_expand,
            .progress_node = .none,
            .create_no_window = true,
            .disable_aslr = false,
            .timeout = .{ .duration = .{
                .raw = .fromSeconds(@intCast(spec_data.limit_value.test_process_timeout)),
                .clock = .awake,
            } },
        });
    }

    pub fn then_inv_help_success(fixture: *Fixture) !void {
        switch (fixture.resultRef().term) {
            .exited => |status| try std.testing.expectEqual(
                spec_data.cliExitCode(.success),
                status,
            ),
            else => return error.UnexpectedTermination,
        }
    }

    pub fn then_inv_help_stdout(fixture: *Fixture, expected: ExpectedOutput) !void {
        try std.testing.expectEqual(ExpectedOutput.generated_cli_help, expected);
        const stdout = fixture.resultRef().stdout;
        try std.testing.expectEqualStrings(spec_data.cli_help, stdout);
        try std.testing.expect(std.mem.startsWith(
            u8,
            stdout,
            "Usage: termux-janitor [OPTIONS]\n\n",
        ));
        try std.testing.expect(std.mem.indexOf(u8, stdout, "\\n") == null);
    }

    pub fn then_inv_help_options(fixture: *Fixture) !void {
        const stdout = fixture.resultRef().stdout;
        for (spec_data.cli_options) |option| {
            try std.testing.expect(std.mem.indexOf(u8, stdout, option.spelling) != null);
            try std.testing.expect(std.mem.indexOf(u8, stdout, option.summary) != null);
        }
        for (spec_data.cli_argument_rules) |rule| {
            try std.testing.expect(std.mem.indexOf(u8, stdout, rule.argument) != null);
        }
    }

    pub fn forbid_no_help_stderr(fixture: *Fixture) !void {
        try std.testing.expectEqualStrings("", fixture.resultRef().stderr);
    }

    pub fn forbid_no_help_fixture_mutation(fixture: *Fixture) !void {
        var entries = fixture.temporary.dir.iterate();
        try std.testing.expect(try entries.next(std.testing.io) == null);
    }

    fn resultRef(fixture: *Fixture) *const std.process.RunResult {
        if (fixture.result) |*result| return result;
        @panic("process has not run");
    }
};