Skip to content

Commit

Permalink
std: add test for child_process
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
matu3ba authored Mar 12, 2022
1 parent 42d75f1 commit e5d4a69
Showing 1 changed file with 66 additions and 3 deletions.
69 changes: 66 additions & 3 deletions lib/std/child_process.zig
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,6 @@ pub const ChildProcess = struct {

try child.spawn();

// TODO collect output in a deadlock-avoiding way on Windows.
// https://github.com/ziglang/zig/issues/6343
if (builtin.os.tag == .haiku) {
const stdout_in = child.stdout.?.reader();
const stderr_in = child.stderr.?.reader();
Expand Down Expand Up @@ -1019,7 +1017,10 @@ var pipe_name_counter = std.atomic.Atomic(u32).init(1);
fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void {
var tmp_bufw: [128]u16 = undefined;

// We must make a named pipe on windows because anonymous pipes do not support async IO
// Anonymous pipes are built upon Named pipes.
// https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe
// Asynchronous (overlapped) read and write operations are not supported by anonymous pipes.
// https://docs.microsoft.com/en-us/windows/win32/ipc/anonymous-pipe-operations
const pipe_path = blk: {
var tmp_buf: [128]u8 = undefined;
// Forge a random path for the pipe.
Expand Down Expand Up @@ -1201,3 +1202,65 @@ test "createNullDelimitedEnvMap" {
}
}
}

const childstr =
\\ const std = @import("std");
\\ const builtin = @import("builtin");
\\ pub fn main() !void {
\\ var it = try std.process.argsWithAllocator(std.testing.allocator);
\\ defer it.deinit(); // no-op unless WASI or Windows
\\ _ = it.next() orelse unreachable; // skip binary name
\\ const input = it.next() orelse unreachable;
\\ var expect_helloworld = "hello world".*;
\\ try std.testing.expect(std.mem.eql(u8, &expect_helloworld, input));
\\ try std.testing.expect(it.next() == null);
\\ try std.testing.expect(!it.skip());
\\ }
;

test "build and call child_process" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
const testing = std.testing;
var it = try std.process.argsWithAllocator(std.testing.allocator);
defer it.deinit(); // no-op unless WASI or Windows

_ = it.next() orelse unreachable;
const zigexec = it.next() orelse unreachable;
try testing.expect(it.next() == null);
try testing.expect(!it.skip());
const cwd_str = try process.getCwdAlloc(testing.allocator);
defer testing.allocator.free(cwd_str);
var tmp = testing.tmpDir(.{ .no_follow = true }); // ie zig-cache/tmp/8DLgoSEqz593PAEE
defer tmp.cleanup();
const cache = "zig-cache";
const tmpdir = "tmp";
const child_name = "child"; // no need for suffixes (.exe, .wasm) due to '-femit-bin'
const suffix_zig = ".zig";
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 });
defer testing.allocator.free(child_path);

const child_zig = try mem.concat(testing.allocator, u8, &[_][]const u8{ child_path, suffix_zig });
defer testing.allocator.free(child_zig);
const emit_flag = "-femit-bin=";
const emit_bin = try mem.concat(testing.allocator, u8, &[_][]const u8{ emit_flag, child_path });
defer testing.allocator.free(emit_bin);
{
// 'zigexec build-exe path/to/child.zig -femit-bin=path/to/child' expect success
try tmp.dir.writeFile("child.zig", childstr);
const args = [_][]const u8{ zigexec, "build-exe", child_zig, emit_bin };
var procCompileChild = try ChildProcess.init(&args, testing.allocator);
defer procCompileChild.deinit();
try procCompileChild.spawn();
const ret_val = try procCompileChild.wait();
try testing.expectEqual(ret_val, .{ .Exited = 0 });
}
{
// spawn compiled file as child_process with argument 'hello world' + expect success
const args = [_][]const u8{ child_path, "hello world" };
var child_proc = try ChildProcess.init(&args, testing.allocator);
defer child_proc.deinit();
try child_proc.spawn();
const ret_val = try child_proc.wait();
try testing.expectEqual(ret_val, .{ .Exited = 0 });
}
}

0 comments on commit e5d4a69

Please sign in to comment.