Skip to content

Commit c746d7a

Browse files
Techatrixandrewrk
authored andcommitted
test: check output file caching of run steps with side-effects
1 parent 7aea384 commit c746d7a

File tree

3 files changed

+148
-0
lines changed

3 files changed

+148
-0
lines changed

test/standalone/build.zig.zon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@
176176
.run_output_paths = .{
177177
.path = "run_output_paths",
178178
},
179+
.run_output_caching = .{
180+
.path = "run_output_caching",
181+
},
179182
},
180183
.paths = .{
181184
"build.zig",
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
const std = @import("std");
2+
3+
pub fn build(b: *std.Build) void {
4+
const test_step = b.step("test", "Test it");
5+
b.default_step = test_step;
6+
7+
const target = b.standardTargetOptions(.{});
8+
const optimize = b.standardOptimizeOption(.{});
9+
10+
const exe = b.addExecutable(.{
11+
.name = "create-file",
12+
.root_source_file = b.path("main.zig"),
13+
.target = target,
14+
.optimize = optimize,
15+
});
16+
17+
{
18+
const run_random_with_sideeffects_first = b.addRunArtifact(exe);
19+
run_random_with_sideeffects_first.setName("run with side-effects (first)");
20+
run_random_with_sideeffects_first.has_side_effects = true;
21+
22+
const run_random_with_sideeffects_second = b.addRunArtifact(exe);
23+
run_random_with_sideeffects_second.setName("run with side-effects (second)");
24+
run_random_with_sideeffects_second.has_side_effects = true;
25+
26+
// ensure that "second" runs after "first"
27+
run_random_with_sideeffects_second.step.dependOn(&run_random_with_sideeffects_first.step);
28+
29+
const first_output = run_random_with_sideeffects_first.addOutputFileArg("a.txt");
30+
const second_output = run_random_with_sideeffects_second.addOutputFileArg("a.txt");
31+
32+
const expect_uncached_dependencies = CheckOutputCaching.init(b, false, &.{ first_output, second_output });
33+
test_step.dependOn(&expect_uncached_dependencies.step);
34+
35+
const expect_unequal_output = CheckPathEquality.init(b, true, &.{ first_output, second_output });
36+
test_step.dependOn(&expect_unequal_output.step);
37+
38+
const check_first_output = b.addCheckFile(first_output, .{ .expected_matches = &.{"a.txt"} });
39+
test_step.dependOn(&check_first_output.step);
40+
const check_second_output = b.addCheckFile(second_output, .{ .expected_matches = &.{"a.txt"} });
41+
test_step.dependOn(&check_second_output.step);
42+
}
43+
44+
{
45+
const run_random_without_sideeffects_1 = b.addRunArtifact(exe);
46+
run_random_without_sideeffects_1.setName("run without side-effects (A)");
47+
48+
const run_random_without_sideeffects_2 = b.addRunArtifact(exe);
49+
run_random_without_sideeffects_2.setName("run without side-effects (B)");
50+
51+
run_random_without_sideeffects_2.step.dependOn(&run_random_without_sideeffects_1.step);
52+
53+
const first_output = run_random_without_sideeffects_1.addOutputFileArg("a.txt");
54+
const second_output = run_random_without_sideeffects_2.addOutputFileArg("a.txt");
55+
56+
const expect_cached_dependencies = CheckOutputCaching.init(b, true, &.{second_output});
57+
test_step.dependOn(&expect_cached_dependencies.step);
58+
59+
const expect_equal_output = CheckPathEquality.init(b, true, &.{ first_output, second_output });
60+
test_step.dependOn(&expect_equal_output.step);
61+
62+
const check_first_output = b.addCheckFile(first_output, .{ .expected_matches = &.{"a.txt"} });
63+
test_step.dependOn(&check_first_output.step);
64+
const check_second_output = b.addCheckFile(second_output, .{ .expected_matches = &.{"a.txt"} });
65+
test_step.dependOn(&check_second_output.step);
66+
}
67+
}
68+
69+
const CheckOutputCaching = struct {
70+
step: std.Build.Step,
71+
expect_caching: bool,
72+
73+
pub fn init(owner: *std.Build, expect_caching: bool, output_paths: []const std.Build.LazyPath) *CheckOutputCaching {
74+
const check = owner.allocator.create(CheckOutputCaching) catch @panic("OOM");
75+
check.* = .{
76+
.step = std.Build.Step.init(.{
77+
.id = .custom,
78+
.name = "check output caching",
79+
.owner = owner,
80+
.makeFn = make,
81+
}),
82+
.expect_caching = expect_caching,
83+
};
84+
for (output_paths) |output_path| {
85+
output_path.addStepDependencies(&check.step);
86+
}
87+
return check;
88+
}
89+
90+
fn make(step: *std.Build.Step, _: std.Progress.Node) !void {
91+
const check: *CheckOutputCaching = @fieldParentPtr("step", step);
92+
93+
for (step.dependencies.items) |dependency| {
94+
if (check.expect_caching) {
95+
if (dependency.result_cached) continue;
96+
return step.fail("expected '{s}' step to be cached, but it was not", .{dependency.name});
97+
} else {
98+
if (!dependency.result_cached) continue;
99+
return step.fail("expected '{s}' step to not be cached, but it was", .{dependency.name});
100+
}
101+
}
102+
}
103+
};
104+
105+
const CheckPathEquality = struct {
106+
step: std.Build.Step,
107+
expected_equality: bool,
108+
output_paths: []const std.Build.LazyPath,
109+
110+
pub fn init(owner: *std.Build, expected_equality: bool, output_paths: []const std.Build.LazyPath) *CheckPathEquality {
111+
const check = owner.allocator.create(CheckPathEquality) catch @panic("OOM");
112+
check.* = .{
113+
.step = std.Build.Step.init(.{
114+
.id = .custom,
115+
.name = "check output path equality",
116+
.owner = owner,
117+
.makeFn = make,
118+
}),
119+
.expected_equality = expected_equality,
120+
.output_paths = owner.allocator.dupe(std.Build.LazyPath, output_paths) catch @panic("OOM"),
121+
};
122+
for (output_paths) |output_path| {
123+
output_path.addStepDependencies(&check.step);
124+
}
125+
return check;
126+
}
127+
128+
fn make(step: *std.Build.Step, _: std.Progress.Node) !void {
129+
const check: *CheckPathEquality = @fieldParentPtr("step", step);
130+
std.debug.assert(check.output_paths.len != 0);
131+
for (check.output_paths[0 .. check.output_paths.len - 1], check.output_paths[1..]) |a, b| {
132+
try std.testing.expectEqual(check.expected_equality, std.mem.eql(u8, a.getPath(step.owner), b.getPath(step.owner)));
133+
}
134+
}
135+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const std = @import("std");
2+
3+
pub fn main() !void {
4+
var args = try std.process.argsWithAllocator(std.heap.page_allocator);
5+
_ = args.skip();
6+
const filename = args.next().?;
7+
const file = try std.fs.createFileAbsolute(filename, .{});
8+
defer file.close();
9+
try file.writeAll(filename);
10+
}

0 commit comments

Comments
 (0)