|
| 1 | +### exetest |
| 2 | + |
| 3 | +CLI testing for Zig. |
| 4 | + |
| 5 | +### Installation |
| 6 | + |
| 7 | +1. Fetch the latest release: |
| 8 | + |
| 9 | + ```shell |
| 10 | + zig fetch --save=exetest https://github.com/pyk/exetest/archive/v0.1.0.tar.gz |
| 11 | + ``` |
| 12 | + |
| 13 | + This updates `build.zig.zon`. |
| 14 | + |
| 15 | +2. Write your test file. Example: `test/echo.zig`. |
| 16 | + |
| 17 | + ```zig |
| 18 | + const std = @import("std"); |
| 19 | + const exetest = @import("exetest"); |
| 20 | + const testing = std.testing; |
| 21 | +
|
| 22 | + test "echo" { |
| 23 | + const argv = &[_][]const u8{"echo", "hello"}; |
| 24 | + var result = try exetest.run(.{ .argv = argv }); |
| 25 | + defer result.deinit(); |
| 26 | +
|
| 27 | + try testing.expectEqualStrings("hello\n", result.stdout); |
| 28 | + } |
| 29 | + ``` |
| 30 | + |
| 31 | +3. Register the test in `build.zig`: |
| 32 | + |
| 33 | + ```zig |
| 34 | + const std = @import("std"); |
| 35 | + const exetest = @import("exetest"); |
| 36 | +
|
| 37 | + pub fn build(b: *std.Build) void { |
| 38 | + // ... |
| 39 | + const echo_test = exetest.add(b, .{ |
| 40 | + .name = "echo", |
| 41 | + .test_file = b.path("test/echo.zig"), |
| 42 | + }); |
| 43 | +
|
| 44 | + const test_step = b.step("test", "Run tests"); |
| 45 | + test_step.dependOn(&echo_test.step); |
| 46 | + } |
| 47 | + ``` |
| 48 | + |
| 49 | +4. Run the tests: |
| 50 | + |
| 51 | + ```shell |
| 52 | + zig build test --summary all |
| 53 | + ``` |
| 54 | + |
| 55 | +See minimal Zig project |
| 56 | +[exetest-example](https://github.com/pyk/exetest-example). |
| 57 | + |
| 58 | +### Usage |
| 59 | + |
| 60 | +There are only 2 functions: |
| 61 | + |
| 62 | +- `add(b, options)` registers a CLI test step in `build.zig`. `zig build test` |
| 63 | + will run the tests. |
| 64 | +- `run(options)` spawns a child process, captures stdout and stderr (up to a |
| 65 | + limit), waits for the child to finish, and returns a `RunResult`. |
| 66 | + |
| 67 | +Basic run and stdout assertion: |
| 68 | + |
| 69 | +```zig |
| 70 | +const std = @import("std"); |
| 71 | +const exetest = @import("exetest"); |
| 72 | +const testing = std.testing; |
| 73 | +
|
| 74 | +test "echo" { |
| 75 | + const argv = &[_][]const u8{"echo", "hello"}; |
| 76 | + var result = try exetest.run(.{ .argv = argv }); |
| 77 | + defer result.deinit(); |
| 78 | +
|
| 79 | + try testing.expectEqualStrings("hello\n", result.stdout); |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | +Write to stdin and capture stdout: |
| 84 | + |
| 85 | +```zig |
| 86 | +const std = @import("std"); |
| 87 | +const exetest = @import("exetest"); |
| 88 | +const testing = std.testing; |
| 89 | +
|
| 90 | +test "cat" { |
| 91 | + const argv = &[_][]const u8{"cat"}; |
| 92 | + const input = "a\nb\n"; |
| 93 | + var r = try exetest.run(.{ .argv = argv, .stdin = input }); |
| 94 | + defer r.deinit(); |
| 95 | +
|
| 96 | + try testing.expectEqualStrings(input, r.stdout); |
| 97 | +} |
| 98 | +``` |
| 99 | + |
| 100 | +Limit how many stdin bytes are sent: |
| 101 | + |
| 102 | +```zig |
| 103 | +const payload = large_slice; // some []const u8 |
| 104 | +var res = try exetest.run(.{ |
| 105 | + .argv = argv, |
| 106 | + .stdin = payload, |
| 107 | + .max_stdin_bytes = 1024, // truncate to 1 KiB |
| 108 | +}); |
| 109 | +defer res.deinit(); |
| 110 | +``` |
| 111 | + |
| 112 | +And you can do much more: |
| 113 | + |
| 114 | +- Capture `stderr` output. |
| 115 | +- Limit how much output is captured. |
| 116 | +- Run with a custom working directory. |
| 117 | +- Run with a custom environment map. |
| 118 | +- Handle exit status and signals. |
| 119 | +- Detect spawn errors. |
| 120 | + |
| 121 | +See [src/test_exe.zig](./src/test_exe.zig). |
| 122 | + |
| 123 | +Some additional notes about the usage of `allocator`: |
| 124 | + |
| 125 | +- `run` uses `testing.allocator` by default. |
| 126 | +- You can pass a different allocator in `RunOptions.allocator` to control where |
| 127 | + captured buffers are allocated. |
| 128 | + |
| 129 | +### Development |
| 130 | + |
| 131 | +Install the Zig toolchain via mise (optional): |
| 132 | + |
| 133 | +```shell |
| 134 | +mise trust |
| 135 | +mise install |
| 136 | +``` |
| 137 | + |
| 138 | +Run tests: |
| 139 | + |
| 140 | +```bash |
| 141 | +zig build test --summary all |
| 142 | +``` |
| 143 | + |
| 144 | +Build library: |
| 145 | + |
| 146 | +```bash |
| 147 | +zig build |
| 148 | +``` |
| 149 | + |
| 150 | +### License |
| 151 | + |
| 152 | +See [LICENSE](./LICENSE). |
0 commit comments