From c746d7a35d6ba26d783863ef8fa86123891e1d38 Mon Sep 17 00:00:00 2001 From: Techatrix Date: Wed, 3 Jul 2024 23:00:16 +0200 Subject: [PATCH] test: check output file caching of run steps with side-effects --- test/standalone/build.zig.zon | 3 + test/standalone/run_output_caching/build.zig | 135 +++++++++++++++++++ test/standalone/run_output_caching/main.zig | 10 ++ 3 files changed, 148 insertions(+) create mode 100644 test/standalone/run_output_caching/build.zig create mode 100644 test/standalone/run_output_caching/main.zig diff --git a/test/standalone/build.zig.zon b/test/standalone/build.zig.zon index eb355ad259..8e4d727642 100644 --- a/test/standalone/build.zig.zon +++ b/test/standalone/build.zig.zon @@ -176,6 +176,9 @@ .run_output_paths = .{ .path = "run_output_paths", }, + .run_output_caching = .{ + .path = "run_output_caching", + }, }, .paths = .{ "build.zig", diff --git a/test/standalone/run_output_caching/build.zig b/test/standalone/run_output_caching/build.zig new file mode 100644 index 0000000000..7888bf4f51 --- /dev/null +++ b/test/standalone/run_output_caching/build.zig @@ -0,0 +1,135 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const exe = b.addExecutable(.{ + .name = "create-file", + .root_source_file = b.path("main.zig"), + .target = target, + .optimize = optimize, + }); + + { + const run_random_with_sideeffects_first = b.addRunArtifact(exe); + run_random_with_sideeffects_first.setName("run with side-effects (first)"); + run_random_with_sideeffects_first.has_side_effects = true; + + const run_random_with_sideeffects_second = b.addRunArtifact(exe); + run_random_with_sideeffects_second.setName("run with side-effects (second)"); + run_random_with_sideeffects_second.has_side_effects = true; + + // ensure that "second" runs after "first" + run_random_with_sideeffects_second.step.dependOn(&run_random_with_sideeffects_first.step); + + const first_output = run_random_with_sideeffects_first.addOutputFileArg("a.txt"); + const second_output = run_random_with_sideeffects_second.addOutputFileArg("a.txt"); + + const expect_uncached_dependencies = CheckOutputCaching.init(b, false, &.{ first_output, second_output }); + test_step.dependOn(&expect_uncached_dependencies.step); + + const expect_unequal_output = CheckPathEquality.init(b, true, &.{ first_output, second_output }); + test_step.dependOn(&expect_unequal_output.step); + + const check_first_output = b.addCheckFile(first_output, .{ .expected_matches = &.{"a.txt"} }); + test_step.dependOn(&check_first_output.step); + const check_second_output = b.addCheckFile(second_output, .{ .expected_matches = &.{"a.txt"} }); + test_step.dependOn(&check_second_output.step); + } + + { + const run_random_without_sideeffects_1 = b.addRunArtifact(exe); + run_random_without_sideeffects_1.setName("run without side-effects (A)"); + + const run_random_without_sideeffects_2 = b.addRunArtifact(exe); + run_random_without_sideeffects_2.setName("run without side-effects (B)"); + + run_random_without_sideeffects_2.step.dependOn(&run_random_without_sideeffects_1.step); + + const first_output = run_random_without_sideeffects_1.addOutputFileArg("a.txt"); + const second_output = run_random_without_sideeffects_2.addOutputFileArg("a.txt"); + + const expect_cached_dependencies = CheckOutputCaching.init(b, true, &.{second_output}); + test_step.dependOn(&expect_cached_dependencies.step); + + const expect_equal_output = CheckPathEquality.init(b, true, &.{ first_output, second_output }); + test_step.dependOn(&expect_equal_output.step); + + const check_first_output = b.addCheckFile(first_output, .{ .expected_matches = &.{"a.txt"} }); + test_step.dependOn(&check_first_output.step); + const check_second_output = b.addCheckFile(second_output, .{ .expected_matches = &.{"a.txt"} }); + test_step.dependOn(&check_second_output.step); + } +} + +const CheckOutputCaching = struct { + step: std.Build.Step, + expect_caching: bool, + + pub fn init(owner: *std.Build, expect_caching: bool, output_paths: []const std.Build.LazyPath) *CheckOutputCaching { + const check = owner.allocator.create(CheckOutputCaching) catch @panic("OOM"); + check.* = .{ + .step = std.Build.Step.init(.{ + .id = .custom, + .name = "check output caching", + .owner = owner, + .makeFn = make, + }), + .expect_caching = expect_caching, + }; + for (output_paths) |output_path| { + output_path.addStepDependencies(&check.step); + } + return check; + } + + fn make(step: *std.Build.Step, _: std.Progress.Node) !void { + const check: *CheckOutputCaching = @fieldParentPtr("step", step); + + for (step.dependencies.items) |dependency| { + if (check.expect_caching) { + if (dependency.result_cached) continue; + return step.fail("expected '{s}' step to be cached, but it was not", .{dependency.name}); + } else { + if (!dependency.result_cached) continue; + return step.fail("expected '{s}' step to not be cached, but it was", .{dependency.name}); + } + } + } +}; + +const CheckPathEquality = struct { + step: std.Build.Step, + expected_equality: bool, + output_paths: []const std.Build.LazyPath, + + pub fn init(owner: *std.Build, expected_equality: bool, output_paths: []const std.Build.LazyPath) *CheckPathEquality { + const check = owner.allocator.create(CheckPathEquality) catch @panic("OOM"); + check.* = .{ + .step = std.Build.Step.init(.{ + .id = .custom, + .name = "check output path equality", + .owner = owner, + .makeFn = make, + }), + .expected_equality = expected_equality, + .output_paths = owner.allocator.dupe(std.Build.LazyPath, output_paths) catch @panic("OOM"), + }; + for (output_paths) |output_path| { + output_path.addStepDependencies(&check.step); + } + return check; + } + + fn make(step: *std.Build.Step, _: std.Progress.Node) !void { + const check: *CheckPathEquality = @fieldParentPtr("step", step); + std.debug.assert(check.output_paths.len != 0); + for (check.output_paths[0 .. check.output_paths.len - 1], check.output_paths[1..]) |a, b| { + try std.testing.expectEqual(check.expected_equality, std.mem.eql(u8, a.getPath(step.owner), b.getPath(step.owner))); + } + } +}; diff --git a/test/standalone/run_output_caching/main.zig b/test/standalone/run_output_caching/main.zig new file mode 100644 index 0000000000..f801971e83 --- /dev/null +++ b/test/standalone/run_output_caching/main.zig @@ -0,0 +1,10 @@ +const std = @import("std"); + +pub fn main() !void { + var args = try std.process.argsWithAllocator(std.heap.page_allocator); + _ = args.skip(); + const filename = args.next().?; + const file = try std.fs.createFileAbsolute(filename, .{}); + defer file.close(); + try file.writeAll(filename); +}