authorgravatar for matu3ba@users.noreply.github.commatu3ba <matu3ba@users.noreply.github.com> 2022-03-12 09:25:18+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-03-12 10:25:18+02:00
loge5d4a694ea7dd251e10d6434c9321b5e0a548d4b
treefda28a28da2b9b8c7b2f1bb274a7703139045517
parent42d75f1a254653cbd4d441b300248c37d5f8d5a2
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

std: add test for child_process

- Cli operations should be refactored, since the standard test runner has an expected argument structure. This would also ensure that the test cli is usable as tested library with checks for subprocess error or success instead of "hacky shell script interfaces". - Default paths generation based on tmpDir would also be useful. - Anonymous pipes on windows are generated from named pipes - Async IO does not work on anonymous pipes - Remove finished TODO

1 files changed, 66 insertions(+), 3 deletions(-)

lib/std/child_process.zig+66-3
......@@ -383,8 +383,6 @@ pub const ChildProcess = struct {
383383
384384 try child.spawn();
385385
386 // TODO collect output in a deadlock-avoiding way on Windows.
387 // https://github.com/ziglang/zig/issues/6343
388386 if (builtin.os.tag == .haiku) {
389387 const stdout_in = child.stdout.?.reader();
390388 const stderr_in = child.stderr.?.reader();
......@@ -1019,7 +1017,10 @@ var pipe_name_counter = std.atomic.Atomic(u32).init(1);
10191017fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void {
10201018 var tmp_bufw: [128]u16 = undefined;
10211019
1022 // We must make a named pipe on windows because anonymous pipes do not support async IO
1020 // Anonymous pipes are built upon Named pipes.
1021 // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe
1022 // Asynchronous (overlapped) read and write operations are not supported by anonymous pipes.
1023 // https://docs.microsoft.com/en-us/windows/win32/ipc/anonymous-pipe-operations
10231024 const pipe_path = blk: {
10241025 var tmp_buf: [128]u8 = undefined;
10251026 // Forge a random path for the pipe.
......@@ -1201,3 +1202,65 @@ test "createNullDelimitedEnvMap" {
12011202 }
12021203 }
12031204}
1205
1206const childstr =
1207 \\ const std = @import("std");
1208 \\ const builtin = @import("builtin");
1209 \\ pub fn main() !void {
1210 \\ var it = try std.process.argsWithAllocator(std.testing.allocator);
1211 \\ defer it.deinit(); // no-op unless WASI or Windows
1212 \\ _ = it.next() orelse unreachable; // skip binary name
1213 \\ const input = it.next() orelse unreachable;
1214 \\ var expect_helloworld = "hello world".*;
1215 \\ try std.testing.expect(std.mem.eql(u8, &expect_helloworld, input));
1216 \\ try std.testing.expect(it.next() == null);
1217 \\ try std.testing.expect(!it.skip());
1218 \\ }
1219;
1220
1221test "build and call child_process" {
1222 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1223 const testing = std.testing;
1224 var it = try std.process.argsWithAllocator(std.testing.allocator);
1225 defer it.deinit(); // no-op unless WASI or Windows
1226
1227 _ = it.next() orelse unreachable;
1228 const zigexec = it.next() orelse unreachable;
1229 try testing.expect(it.next() == null);
1230 try testing.expect(!it.skip());
1231 const cwd_str = try process.getCwdAlloc(testing.allocator);
1232 defer testing.allocator.free(cwd_str);
1233 var tmp = testing.tmpDir(.{ .no_follow = true }); // ie zig-cache/tmp/8DLgoSEqz593PAEE
1234 defer tmp.cleanup();
1235 const cache = "zig-cache";
1236 const tmpdir = "tmp";
1237 const child_name = "child"; // no need for suffixes (.exe, .wasm) due to '-femit-bin'
1238 const suffix_zig = ".zig";
1239 const child_path = try fs.path.join(testing.allocator, &[_][]const u8{ cwd_str, std.fs.path.sep_str, cache, tmpdir, &tmp.sub_path, child_name });
1240 defer testing.allocator.free(child_path);
1241
1242 const child_zig = try mem.concat(testing.allocator, u8, &[_][]const u8{ child_path, suffix_zig });
1243 defer testing.allocator.free(child_zig);
1244 const emit_flag = "-femit-bin=";
1245 const emit_bin = try mem.concat(testing.allocator, u8, &[_][]const u8{ emit_flag, child_path });
1246 defer testing.allocator.free(emit_bin);
1247 {
1248 // 'zigexec build-exe path/to/child.zig -femit-bin=path/to/child' expect success
1249 try tmp.dir.writeFile("child.zig", childstr);
1250 const args = [_][]const u8{ zigexec, "build-exe", child_zig, emit_bin };
1251 var procCompileChild = try ChildProcess.init(&args, testing.allocator);
1252 defer procCompileChild.deinit();
1253 try procCompileChild.spawn();
1254 const ret_val = try procCompileChild.wait();
1255 try testing.expectEqual(ret_val, .{ .Exited = 0 });
1256 }
1257 {
1258 // spawn compiled file as child_process with argument 'hello world' + expect success
1259 const args = [_][]const u8{ child_path, "hello world" };
1260 var child_proc = try ChildProcess.init(&args, testing.allocator);
1261 defer child_proc.deinit();
1262 try child_proc.spawn();
1263 const ret_val = try child_proc.wait();
1264 try testing.expectEqual(ret_val, .{ .Exited = 0 });
1265 }
1266}