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"); } };