authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2025-07-25 00:24:09-07:00
committergravatar for 396151+h57624paen@users.noreply.github.comlumanetic <396151+h57624paen@users.noreply.github.com> 2025-07-26 01:35:15-04:00
log0f4106356e298f894fe31704af350da6209019bc
tree188ce8a96804bef2e1254998c2f94b769b071a8c
parentafa66f6111c88cda6bf576367f980f2a84c33116

child_process standalone test: Test spawning a path with leading ..

Also check that FileNotFound is consistently returned when the path is missing. The new `run_relative` step will test spawning paths like: child_path: ../84385e7e669db0967d7a42765011dbe0/child missing_child_path: ../84385e7e669db0967d7a42765011dbe0/child_intentionally_missing

2 files changed, 25 insertions(+), 2 deletions(-)

test/standalone/child_process/build.zig+11
...@@ -31,5 +31,16 @@ pub fn build(b: *std.Build) void {...@@ -31,5 +31,16 @@ pub fn build(b: *std.Build) void {
31 run.addArtifactArg(child);31 run.addArtifactArg(child);
32 run.expectExitCode(0);32 run.expectExitCode(0);
3333
34 // Use a temporary directory within the cache as the CWD to test
35 // spawning the child using a path that contains a leading `..` component.
36 const run_relative = b.addRunArtifact(main);
37 run_relative.addArtifactArg(child);
38 const write_tmp_dir = b.addWriteFiles();
39 const tmp_cwd = write_tmp_dir.getDirectory();
40 run_relative.addDirectoryArg(tmp_cwd);
41 run_relative.setCwd(tmp_cwd);
42 run_relative.expectExitCode(0);
43
34 test_step.dependOn(&run.step);44 test_step.dependOn(&run.step);
45 test_step.dependOn(&run_relative.step);
35}46}
test/standalone/child_process/main.zig+14-2
...@@ -11,7 +11,14 @@ pub fn main() !void {...@@ -11,7 +11,14 @@ pub fn main() !void {
11 var it = try std.process.argsWithAllocator(gpa);11 var it = try std.process.argsWithAllocator(gpa);
12 defer it.deinit();12 defer it.deinit();
13 _ = it.next() orelse unreachable; // skip binary name13 _ = it.next() orelse unreachable; // skip binary name
14 const child_path = it.next() orelse unreachable;14 const child_path, const needs_free = child_path: {
15 const child_path = it.next() orelse unreachable;
16 const cwd_path = it.next() orelse break :child_path .{ child_path, false };
17 // If there is a third argument, it is the current CWD somewhere within the cache directory.
18 // In that case, modify the child path in order to test spawning a path with a leading `..` component.
19 break :child_path .{ try std.fs.path.relative(gpa, cwd_path, child_path), true };
20 };
21 defer if (needs_free) gpa.free(child_path);
1522
16 var child = std.process.Child.init(&.{ child_path, "hello arg" }, gpa);23 var child = std.process.Child.init(&.{ child_path, "hello arg" }, gpa);
17 child.stdin_behavior = .Pipe;24 child.stdin_behavior = .Pipe;
...@@ -39,7 +46,12 @@ pub fn main() !void {...@@ -39,7 +46,12 @@ pub fn main() !void {
39 },46 },
40 else => |term| testError("abnormal child exit: {}", .{term}),47 else => |term| testError("abnormal child exit: {}", .{term}),
41 }48 }
42 return if (parent_test_error) error.ParentTestError else {};49 if (parent_test_error) return error.ParentTestError;
50
51 // Check that FileNotFound is consistent across platforms when trying to spawn an executable that doesn't exist
52 const missing_child_path = try std.mem.concat(gpa, u8, &.{ child_path, "_intentionally_missing" });
53 defer gpa.free(missing_child_path);
54 try std.testing.expectError(error.FileNotFound, std.process.Child.run(.{ .allocator = gpa, .argv = &.{missing_child_path} }));
43}55}
4456
45var parent_test_error = false;57var parent_test_error = false;