diff --git a/test/standalone/child_process/build.zig b/test/standalone/child_process/build.zig index 81b40835f3..9e96aaa608 100644 --- a/test/standalone/child_process/build.zig +++ b/test/standalone/child_process/build.zig @@ -31,5 +31,16 @@ pub fn build(b: *std.Build) void { run.addArtifactArg(child); run.expectExitCode(0); + // Use a temporary directory within the cache as the CWD to test + // spawning the child using a path that contains a leading `..` component. + const run_relative = b.addRunArtifact(main); + run_relative.addArtifactArg(child); + const write_tmp_dir = b.addWriteFiles(); + const tmp_cwd = write_tmp_dir.getDirectory(); + run_relative.addDirectoryArg(tmp_cwd); + run_relative.setCwd(tmp_cwd); + run_relative.expectExitCode(0); + test_step.dependOn(&run.step); + test_step.dependOn(&run_relative.step); } diff --git a/test/standalone/child_process/main.zig b/test/standalone/child_process/main.zig index 6537f90acf..9ded383d96 100644 --- a/test/standalone/child_process/main.zig +++ b/test/standalone/child_process/main.zig @@ -11,7 +11,14 @@ pub fn main() !void { var it = try std.process.argsWithAllocator(gpa); defer it.deinit(); _ = it.next() orelse unreachable; // skip binary name - const child_path = it.next() orelse unreachable; + const child_path, const needs_free = child_path: { + const child_path = it.next() orelse unreachable; + const cwd_path = it.next() orelse break :child_path .{ child_path, false }; + // If there is a third argument, it is the current CWD somewhere within the cache directory. + // In that case, modify the child path in order to test spawning a path with a leading `..` component. + break :child_path .{ try std.fs.path.relative(gpa, cwd_path, child_path), true }; + }; + defer if (needs_free) gpa.free(child_path); var child = std.process.Child.init(&.{ child_path, "hello arg" }, gpa); child.stdin_behavior = .Pipe; @@ -39,7 +46,12 @@ pub fn main() !void { }, else => |term| testError("abnormal child exit: {}", .{term}), } - return if (parent_test_error) error.ParentTestError else {}; + if (parent_test_error) return error.ParentTestError; + + // Check that FileNotFound is consistent across platforms when trying to spawn an executable that doesn't exist + const missing_child_path = try std.mem.concat(gpa, u8, &.{ child_path, "_intentionally_missing" }); + defer gpa.free(missing_child_path); + try std.testing.expectError(error.FileNotFound, std.process.Child.run(.{ .allocator = gpa, .argv = &.{missing_child_path} })); } var parent_test_error = false;