diff options
| author | Andrew Kelley <andrewrk@noreply.codeberg.org> | 2025-12-27 14:10:46 +0100 |
|---|---|---|
| committer | Andrew Kelley <andrewrk@noreply.codeberg.org> | 2025-12-27 14:10:46 +0100 |
| commit | e55e6b5528bb2f01de242fcf32b172e244e98e74 (patch) | |
| tree | 3a5eb3193d3d192c54ab0c2b7295a7f21861c27e /test | |
| parent | c3f2de5e519926eb0029062fe8e782a6f9df9c05 (diff) | |
| parent | 60a1ba0a8f3517356fa2941462f002a7f580545b (diff) | |
| download | zig-e55e6b5528bb2f01de242fcf32b172e244e98e74.tar.gz zig-e55e6b5528bb2f01de242fcf32b172e244e98e74.zip | |
Merge pull request 'std: migrate all `fs` APIs to `Io`' (#30232) from std.Io-fs into master
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30232
Diffstat (limited to 'test')
66 files changed, 835 insertions, 497 deletions
diff --git a/test/cases/disable_stack_tracing.zig b/test/cases/disable_stack_tracing.zig index 044eaf7012..36620130c9 100644 --- a/test/cases/disable_stack_tracing.zig +++ b/test/cases/disable_stack_tracing.zig @@ -5,16 +5,16 @@ pub const std_options: std.Options = .{ pub fn main() !void { var st_buf: [8]usize = undefined; var buf: [1024]u8 = undefined; - var stdout = std.fs.File.stdout().writer(&buf); + var stdout = std.Io.File.stdout().writer(std.Options.debug_io, &buf); const captured_st = try foo(&stdout.interface, &st_buf); - try std.debug.writeStackTrace(&captured_st, &stdout.interface, .no_color); + try std.debug.writeStackTrace(&captured_st, .{ .writer = &stdout.interface, .mode = .no_color }); try stdout.interface.print("stack trace index: {d}\n", .{captured_st.index}); try stdout.interface.flush(); } fn foo(w: *std.Io.Writer, st_buf: []usize) !std.builtin.StackTrace { - try std.debug.writeCurrentStackTrace(.{}, w, .no_color); + try std.debug.writeCurrentStackTrace(.{}, .{ .writer = w, .mode = .no_color }); return std.debug.captureCurrentStackTrace(.{}, st_buf); } diff --git a/test/incremental/add_decl b/test/incremental/add_decl index 9efd274b9e..99f6b2d34f 100644 --- a/test/incremental/add_decl +++ b/test/incremental/add_decl @@ -7,57 +7,63 @@ #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(foo); + try std.Io.File.stdout().writeStreamingAll(io, foo); } const foo = "good morning\n"; +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="good morning\n" #update=add new declaration #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(foo); + try std.Io.File.stdout().writeStreamingAll(io, foo); } const foo = "good morning\n"; const bar = "good evening\n"; +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="good morning\n" #update=reference new declaration #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(bar); + try std.Io.File.stdout().writeStreamingAll(io, bar); } const foo = "good morning\n"; const bar = "good evening\n"; +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="good evening\n" #update=reference missing declaration #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(qux); + try std.Io.File.stdout().writeStreamingAll(io, qux); } const foo = "good morning\n"; const bar = "good evening\n"; -#expect_error=main.zig:3:39: error: use of undeclared identifier 'qux' +const io = std.Io.Threaded.global_single_threaded.ioBasic(); +#expect_error=main.zig:3:52: error: use of undeclared identifier 'qux' #update=add missing declaration #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(qux); + try std.Io.File.stdout().writeStreamingAll(io, qux); } const foo = "good morning\n"; const bar = "good evening\n"; const qux = "good night\n"; +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="good night\n" #update=remove unused declarations #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(qux); + try std.Io.File.stdout().writeStreamingAll(io, qux); } const qux = "good night\n"; +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="good night\n" diff --git a/test/incremental/add_decl_namespaced b/test/incremental/add_decl_namespaced index 1025ae24e1..6fc22f10b9 100644 --- a/test/incremental/add_decl_namespaced +++ b/test/incremental/add_decl_namespaced @@ -7,58 +7,64 @@ #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(@This().foo); + try std.Io.File.stdout().writeStreamingAll(io, @This().foo); } const foo = "good morning\n"; +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="good morning\n" #update=add new declaration #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(@This().foo); + try std.Io.File.stdout().writeStreamingAll(io, @This().foo); } const foo = "good morning\n"; const bar = "good evening\n"; +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="good morning\n" #update=reference new declaration #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(@This().bar); + try std.Io.File.stdout().writeStreamingAll(io, @This().bar); } const foo = "good morning\n"; const bar = "good evening\n"; +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="good evening\n" #update=reference missing declaration #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(@This().qux); + try std.Io.File.stdout().writeStreamingAll(io, @This().qux); } const foo = "good morning\n"; const bar = "good evening\n"; -#expect_error=main.zig:3:46: error: root source file struct 'main' has no member named 'qux' +const io = std.Io.Threaded.global_single_threaded.ioBasic(); +#expect_error=main.zig:3:59: error: root source file struct 'main' has no member named 'qux' #expect_error=main.zig:1:1: note: struct declared here #update=add missing declaration #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(@This().qux); + try std.Io.File.stdout().writeStreamingAll(io, @This().qux); } const foo = "good morning\n"; const bar = "good evening\n"; const qux = "good night\n"; +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="good night\n" #update=remove unused declarations #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(@This().qux); + try std.Io.File.stdout().writeStreamingAll(io, @This().qux); } const qux = "good night\n"; +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="good night\n" diff --git a/test/incremental/bad_import b/test/incremental/bad_import index 9b6be8b176..20bdb9ae82 100644 --- a/test/incremental/bad_import +++ b/test/incremental/bad_import @@ -8,9 +8,10 @@ #file=main.zig pub fn main() !void { _ = @import("foo.zig"); - try std.fs.File.stdout().writeAll("success\n"); + try std.Io.File.stdout().writeStreamingAll(io, "success\n"); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #file=foo.zig comptime { _ = @import("bad.zig"); @@ -30,7 +31,8 @@ comptime { #file=main.zig pub fn main() !void { //_ = @import("foo.zig"); - try std.fs.File.stdout().writeAll("success\n"); + try std.Io.File.stdout().writeStreamingAll(io, "success\n"); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="success\n" diff --git a/test/incremental/change_embed_file b/test/incremental/change_embed_file index 85d861ab93..84c9c334c7 100644 --- a/test/incremental/change_embed_file +++ b/test/incremental/change_embed_file @@ -8,8 +8,9 @@ const std = @import("std"); const string = @embedFile("string.txt"); pub fn main() !void { - try std.fs.File.stdout().writeAll(string); + try std.Io.File.stdout().writeStreamingAll(io, string); } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #file=string.txt Hello, World! #expect_stdout="Hello, World!\n" @@ -28,8 +29,9 @@ Hello again, World! const std = @import("std"); const string = @embedFile("string.txt"); pub fn main() !void { - try std.fs.File.stdout().writeAll("a hardcoded string\n"); + try std.Io.File.stdout().writeStreamingAll(io, "a hardcoded string\n"); } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="a hardcoded string\n" #update=re-introduce reference to file @@ -37,8 +39,9 @@ pub fn main() !void { const std = @import("std"); const string = @embedFile("string.txt"); pub fn main() !void { - try std.fs.File.stdout().writeAll(string); + try std.Io.File.stdout().writeStreamingAll(io, string); } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_error=main.zig:2:27: error: unable to open 'string.txt': FileNotFound #update=recreate file diff --git a/test/incremental/change_enum_tag_type b/test/incremental/change_enum_tag_type index 906f910271..06b80ac04d 100644 --- a/test/incremental/change_enum_tag_type +++ b/test/incremental/change_enum_tag_type @@ -15,10 +15,11 @@ const Foo = enum(Tag) { pub fn main() !void { var val: Foo = undefined; val = .a; - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print("{s}\n", .{@tagName(val)}); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="a\n" #update=too many enum fields #file=main.zig @@ -33,7 +34,7 @@ const Foo = enum(Tag) { pub fn main() !void { var val: Foo = undefined; val = .a; - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print("{s}\n", .{@tagName(val)}); } comptime { @@ -42,6 +43,7 @@ comptime { std.debug.assert(@TypeOf(@intFromEnum(Foo.e)) == Tag); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_error=main.zig:7:5: error: enumeration value '4' too large for type 'u2' #update=increase tag size #file=main.zig @@ -56,8 +58,9 @@ const Foo = enum(Tag) { pub fn main() !void { var val: Foo = undefined; val = .a; - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print("{s}\n", .{@tagName(val)}); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="a\n" diff --git a/test/incremental/change_exports b/test/incremental/change_exports index b0850626d6..a36afb4ee1 100644 --- a/test/incremental/change_exports +++ b/test/incremental/change_exports @@ -17,10 +17,11 @@ pub fn main() !void { extern const bar: u32; }; S.foo(); - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print("{}\n", .{S.bar}); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="123\n" #update=add conflict @@ -39,10 +40,11 @@ pub fn main() !void { extern const other: u32; }; S.foo(); - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_error=main.zig:6:5: error: exported symbol collision: foo #expect_error=main.zig:1:1: note: other symbol here @@ -62,10 +64,11 @@ pub fn main() !void { extern const other: u32; }; S.foo(); - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="123 456\n" #update=put exports in decl @@ -87,10 +90,11 @@ pub fn main() !void { extern const other: u32; }; S.foo(); - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="123 456\n" #update=remove reference to exporting decl @@ -133,10 +137,11 @@ pub fn main() !void { extern const other: u32; }; S.foo(); - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="123 456\n" #update=reintroduce reference to exporting decl, introducing conflict @@ -158,10 +163,11 @@ pub fn main() !void { extern const other: u32; }; S.foo(); - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_error=main.zig:5:5: error: exported symbol collision: bar #expect_error=main.zig:2:1: note: other symbol here #expect_error=main.zig:6:5: error: exported symbol collision: other diff --git a/test/incremental/change_fn_type b/test/incremental/change_fn_type index df788684cd..b4286545e3 100644 --- a/test/incremental/change_fn_type +++ b/test/incremental/change_fn_type @@ -8,10 +8,11 @@ pub fn main() !void { try foo(123); } fn foo(x: u8) !void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); return stdout_writer.interface.print("{d}\n", .{x}); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="123\n" #update=change function type @@ -20,10 +21,11 @@ pub fn main() !void { try foo(123); } fn foo(x: i64) !void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); return stdout_writer.interface.print("{d}\n", .{x}); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="123\n" #update=change function argument @@ -32,8 +34,9 @@ pub fn main() !void { try foo(-42); } fn foo(x: i64) !void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); return stdout_writer.interface.print("{d}\n", .{x}); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="-42\n" diff --git a/test/incremental/change_generic_line_number b/test/incremental/change_generic_line_number index f1920c67e6..2d731b071c 100644 --- a/test/incremental/change_generic_line_number +++ b/test/incremental/change_generic_line_number @@ -4,10 +4,11 @@ #update=initial version #file=main.zig const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); fn Printer(message: []const u8) type { return struct { fn print() !void { - try std.fs.File.stdout().writeAll(message); + try std.Io.File.stdout().writeStreamingAll(io, message); } }; } @@ -19,11 +20,12 @@ pub fn main() !void { #update=change line number #file=main.zig const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); fn Printer(message: []const u8) type { return struct { fn print() !void { - try std.fs.File.stdout().writeAll(message); + try std.Io.File.stdout().writeStreamingAll(io, message); } }; } diff --git a/test/incremental/change_line_number b/test/incremental/change_line_number index 5c809b8fa9..7bafbbfbdd 100644 --- a/test/incremental/change_line_number +++ b/test/incremental/change_line_number @@ -5,14 +5,16 @@ #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll("foo\n"); + try std.Io.File.stdout().writeStreamingAll(io, "foo\n"); } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="foo\n" #update=change line number #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll("foo\n"); + try std.Io.File.stdout().writeStreamingAll(io, "foo\n"); } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="foo\n" diff --git a/test/incremental/change_panic_handler b/test/incremental/change_panic_handler index 070384887a..ebce3bc312 100644 --- a/test/incremental/change_panic_handler +++ b/test/incremental/change_panic_handler @@ -12,11 +12,12 @@ pub fn main() !u8 { } pub const panic = std.debug.FullPanic(myPanic); fn myPanic(msg: []const u8, _: ?usize) noreturn { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); stdout_writer.interface.print("panic message: {s}\n", .{msg}) catch {}; std.process.exit(0); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="panic message: integer overflow\n" #update=change the panic handler body @@ -29,11 +30,12 @@ pub fn main() !u8 { } pub const panic = std.debug.FullPanic(myPanic); fn myPanic(msg: []const u8, _: ?usize) noreturn { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); stdout_writer.interface.print("new panic message: {s}\n", .{msg}) catch {}; std.process.exit(0); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="new panic message: integer overflow\n" #update=change the panic handler function value @@ -46,9 +48,10 @@ pub fn main() !u8 { } pub const panic = std.debug.FullPanic(myPanicNew); fn myPanicNew(msg: []const u8, _: ?usize) noreturn { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); stdout_writer.interface.print("third panic message: {s}\n", .{msg}) catch {}; std.process.exit(0); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="third panic message: integer overflow\n" diff --git a/test/incremental/change_panic_handler_explicit b/test/incremental/change_panic_handler_explicit index 774b18bbfd..366bffca45 100644 --- a/test/incremental/change_panic_handler_explicit +++ b/test/incremental/change_panic_handler_explicit @@ -42,11 +42,12 @@ pub const panic = struct { pub const noreturnReturned = no_panic.noreturnReturned; }; fn myPanic(msg: []const u8, _: ?usize) noreturn { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); stdout_writer.interface.print("panic message: {s}\n", .{msg}) catch {}; std.process.exit(0); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="panic message: integer overflow\n" #update=change the panic handler body @@ -89,11 +90,12 @@ pub const panic = struct { pub const noreturnReturned = no_panic.noreturnReturned; }; fn myPanic(msg: []const u8, _: ?usize) noreturn { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); stdout_writer.interface.print("new panic message: {s}\n", .{msg}) catch {}; std.process.exit(0); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="new panic message: integer overflow\n" #update=change the panic handler function value @@ -136,9 +138,10 @@ pub const panic = struct { pub const noreturnReturned = no_panic.noreturnReturned; }; fn myPanicNew(msg: []const u8, _: ?usize) noreturn { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); stdout_writer.interface.print("third panic message: {s}\n", .{msg}) catch {}; std.process.exit(0); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="third panic message: integer overflow\n" diff --git a/test/incremental/change_shift_op b/test/incremental/change_shift_op index 41b5d19266..af849c0d5b 100644 --- a/test/incremental/change_shift_op +++ b/test/incremental/change_shift_op @@ -9,10 +9,11 @@ pub fn main() !void { try foo(0x1300); } fn foo(x: u16) !void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print("0x{x}\n", .{x << 4}); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="0x3000\n" #update=change to right shift #file=main.zig @@ -20,8 +21,9 @@ pub fn main() !void { try foo(0x1300); } fn foo(x: u16) !void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print("0x{x}\n", .{x >> 4}); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="0x130\n" diff --git a/test/incremental/change_struct_same_fields b/test/incremental/change_struct_same_fields index 7af1161326..3ba715f906 100644 --- a/test/incremental/change_struct_same_fields +++ b/test/incremental/change_struct_same_fields @@ -11,13 +11,14 @@ pub fn main() !void { try foo(&val); } fn foo(val: *const S) !void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print( "{d} {d}\n", .{ val.x, val.y }, ); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="100 200\n" #update=change struct layout @@ -28,13 +29,14 @@ pub fn main() !void { try foo(&val); } fn foo(val: *const S) !void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print( "{d} {d}\n", .{ val.x, val.y }, ); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="100 200\n" #update=change values @@ -45,11 +47,12 @@ pub fn main() !void { try foo(&val); } fn foo(val: *const S) !void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print( "{d} {d}\n", .{ val.x, val.y }, ); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="1234 5678\n" diff --git a/test/incremental/change_zon_file b/test/incremental/change_zon_file index 62f73dd3bf..a966df5471 100644 --- a/test/incremental/change_zon_file +++ b/test/incremental/change_zon_file @@ -8,8 +8,9 @@ const std = @import("std"); const message: []const u8 = @import("message.zon"); pub fn main() !void { - try std.fs.File.stdout().writeAll(message); + try std.Io.File.stdout().writeStreamingAll(io, message); } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #file=message.zon "Hello, World!\n" #expect_stdout="Hello, World!\n" @@ -29,8 +30,9 @@ pub fn main() !void { const std = @import("std"); const message: []const u8 = @import("message.zon"); pub fn main() !void { - try std.fs.File.stdout().writeAll("a hardcoded string\n"); + try std.Io.File.stdout().writeStreamingAll(io, "a hardcoded string\n"); } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_error=message.zon:1:1: error: unable to load 'message.zon': FileNotFound #expect_error=main.zig:2:37: note: file imported here @@ -44,6 +46,7 @@ pub fn main() !void { const std = @import("std"); const message: []const u8 = @import("message.zon"); pub fn main() !void { - try std.fs.File.stdout().writeAll(message); + try std.Io.File.stdout().writeStreamingAll(io, message); } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="We're back, World!\n" diff --git a/test/incremental/change_zon_file_no_result_type b/test/incremental/change_zon_file_no_result_type index 498543e4f1..6b3aa73dc6 100644 --- a/test/incremental/change_zon_file_no_result_type +++ b/test/incremental/change_zon_file_no_result_type @@ -6,8 +6,9 @@ #update=initial version #file=main.zig const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); pub fn main() !void { - try std.fs.File.stdout().writeAll(@import("foo.zon").message); + try std.Io.File.stdout().writeStreamingAll(io, @import("foo.zon").message); } #file=foo.zon .{ diff --git a/test/incremental/compile_log b/test/incremental/compile_log index 697bb26569..19ff7237f2 100644 --- a/test/incremental/compile_log +++ b/test/incremental/compile_log @@ -8,17 +8,19 @@ #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll("Hello, World!\n"); + try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="Hello, World!\n" #update=add compile log #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll("Hello, World!\n"); + try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); @compileLog("this is a log"); } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_error=main.zig:4:5: error: found compile log statement #expect_compile_log=@as(*const [13:0]u8, "this is a log") @@ -26,6 +28,7 @@ pub fn main() !void { #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll("Hello, World!\n"); + try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="Hello, World!\n" diff --git a/test/incremental/fix_astgen_failure b/test/incremental/fix_astgen_failure index 8b1b3adbf7..dca371f521 100644 --- a/test/incremental/fix_astgen_failure +++ b/test/incremental/fix_astgen_failure @@ -10,28 +10,31 @@ pub fn main() !void { } #file=foo.zig pub fn hello() !void { - try std.fs.File.stdout().writeAll("Hello, World!\n"); + try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } #expect_error=foo.zig:2:9: error: use of undeclared identifier 'std' #update=fix the error #file=foo.zig const std = @import("std"); pub fn hello() !void { - try std.fs.File.stdout().writeAll("Hello, World!\n"); + try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="Hello, World!\n" #update=add new error #file=foo.zig const std = @import("std"); pub fn hello() !void { - try std.fs.File.stdout().writeAll(hello_str); + try std.Io.File.stdout().writeStreamingAll(io, hello_str); } -#expect_error=foo.zig:3:39: error: use of undeclared identifier 'hello_str' +const io = std.Io.Threaded.global_single_threaded.ioBasic(); +#expect_error=foo.zig:3:52: error: use of undeclared identifier 'hello_str' #update=fix the new error #file=foo.zig const std = @import("std"); const hello_str = "Hello, World! Again!\n"; pub fn hello() !void { - try std.fs.File.stdout().writeAll(hello_str); + try std.Io.File.stdout().writeStreamingAll(io, hello_str); } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="Hello, World! Again!\n" diff --git a/test/incremental/function_becomes_inline b/test/incremental/function_becomes_inline index 240d7a54af..4021575842 100644 --- a/test/incremental/function_becomes_inline +++ b/test/incremental/function_becomes_inline @@ -8,9 +8,10 @@ pub fn main() !void { try foo(); } fn foo() !void { - try std.fs.File.stdout().writeAll("Hello, World!\n"); + try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="Hello, World!\n" #update=make function inline @@ -19,9 +20,10 @@ pub fn main() !void { try foo(); } inline fn foo() !void { - try std.fs.File.stdout().writeAll("Hello, World!\n"); + try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="Hello, World!\n" #update=change string @@ -30,7 +32,8 @@ pub fn main() !void { try foo(); } inline fn foo() !void { - try std.fs.File.stdout().writeAll("Hello, `inline` World!\n"); + try std.Io.File.stdout().writeStreamingAll(io, "Hello, `inline` World!\n"); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="Hello, `inline` World!\n" diff --git a/test/incremental/hello b/test/incremental/hello index dc6f02177f..48659e1879 100644 --- a/test/incremental/hello +++ b/test/incremental/hello @@ -6,14 +6,16 @@ #update=initial version #file=main.zig const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); pub fn main() !void { - try std.fs.File.stdout().writeAll("good morning\n"); + try std.Io.File.stdout().writeStreamingAll(io, "good morning\n"); } #expect_stdout="good morning\n" #update=change the string #file=main.zig const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); pub fn main() !void { - try std.fs.File.stdout().writeAll("おはようございます\n"); + try std.Io.File.stdout().writeStreamingAll(io, "おはようございます\n"); } #expect_stdout="おはようございます\n" diff --git a/test/incremental/make_decl_pub b/test/incremental/make_decl_pub index b25b117160..b193deb68c 100644 --- a/test/incremental/make_decl_pub +++ b/test/incremental/make_decl_pub @@ -12,8 +12,9 @@ pub fn main() !void { #file=foo.zig const std = @import("std"); fn hello() !void { - try std.fs.File.stdout().writeAll("Hello, World!\n"); + try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_error=main.zig:3:12: error: 'hello' is not marked 'pub' #expect_error=foo.zig:2:1: note: declared here @@ -21,6 +22,7 @@ fn hello() !void { #file=foo.zig const std = @import("std"); pub fn hello() !void { - try std.fs.File.stdout().writeAll("Hello, World!\n"); + try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="Hello, World!\n" diff --git a/test/incremental/modify_inline_fn b/test/incremental/modify_inline_fn index d485d8ffd5..19e201f1d9 100644 --- a/test/incremental/modify_inline_fn +++ b/test/incremental/modify_inline_fn @@ -8,20 +8,22 @@ const std = @import("std"); pub fn main() !void { const str = getStr(); - try std.fs.File.stdout().writeAll(str); + try std.Io.File.stdout().writeStreamingAll(io, str); } inline fn getStr() []const u8 { return "foo\n"; } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="foo\n" #update=change the string #file=main.zig const std = @import("std"); pub fn main() !void { const str = getStr(); - try std.fs.File.stdout().writeAll(str); + try std.Io.File.stdout().writeStreamingAll(io, str); } inline fn getStr() []const u8 { return "bar\n"; } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="bar\n" diff --git a/test/incremental/move_src b/test/incremental/move_src index 4f43e8ea6a..b79a25df8d 100644 --- a/test/incremental/move_src +++ b/test/incremental/move_src @@ -7,7 +7,7 @@ #file=main.zig const std = @import("std"); pub fn main() !void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print("{d} {d}\n", .{ foo(), bar() }); } fn foo() u32 { @@ -16,13 +16,14 @@ fn foo() u32 { fn bar() u32 { return 123; } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="7 123\n" #update=add newline #file=main.zig const std = @import("std"); pub fn main() !void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print("{d} {d}\n", .{ foo(), bar() }); } @@ -32,4 +33,5 @@ fn foo() u32 { fn bar() u32 { return 123; } +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="8 123\n" diff --git a/test/incremental/no_change_preserves_tag_names b/test/incremental/no_change_preserves_tag_names index 623496119d..dc89face50 100644 --- a/test/incremental/no_change_preserves_tag_names +++ b/test/incremental/no_change_preserves_tag_names @@ -7,15 +7,17 @@ #file=main.zig const std = @import("std"); var some_enum: enum { first, second } = .first; +const io = std.Io.Threaded.global_single_threaded.ioBasic(); pub fn main() !void { - try std.fs.File.stdout().writeAll(@tagName(some_enum)); + try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); } #expect_stdout="first" #update=no change #file=main.zig const std = @import("std"); var some_enum: enum { first, second } = .first; +const io = std.Io.Threaded.global_single_threaded.ioBasic(); pub fn main() !void { - try std.fs.File.stdout().writeAll(@tagName(some_enum)); + try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); } #expect_stdout="first" diff --git a/test/incremental/recursive_function_becomes_non_recursive b/test/incremental/recursive_function_becomes_non_recursive index a5a03749b8..5cee1bfbcf 100644 --- a/test/incremental/recursive_function_becomes_non_recursive +++ b/test/incremental/recursive_function_becomes_non_recursive @@ -9,11 +9,12 @@ pub fn main() !void { try foo(false); } fn foo(recurse: bool) !void { - const stdout = std.fs.File.stdout(); + const stdout = std.Io.File.stdout(); if (recurse) return foo(true); - try stdout.writeAll("non-recursive path\n"); + try stdout.writeStreamingAll(io, "non-recursive path\n"); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="non-recursive path\n" #update=eliminate recursion and change argument @@ -22,9 +23,10 @@ pub fn main() !void { try foo(true); } fn foo(recurse: bool) !void { - const stdout = std.fs.File.stdout(); - if (recurse) return stdout.writeAll("x==1\n"); - try stdout.writeAll("non-recursive path\n"); + const stdout = std.Io.File.stdout(); + if (recurse) return stdout.writeStreamingAll(io, "x==1\n"); + try stdout.writeStreamingAll(io, "non-recursive path\n"); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="x==1\n" diff --git a/test/incremental/remove_enum_field b/test/incremental/remove_enum_field index 02daf2a0fb..c964285707 100644 --- a/test/incremental/remove_enum_field +++ b/test/incremental/remove_enum_field @@ -10,10 +10,11 @@ const MyEnum = enum(u8) { bar = 2, }; pub fn main() !void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print("{}\n", .{@intFromEnum(MyEnum.foo)}); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="1\n" #update=remove enum field #file=main.zig @@ -22,9 +23,10 @@ const MyEnum = enum(u8) { bar = 2, }; pub fn main() !void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); try stdout_writer.interface.print("{}\n", .{@intFromEnum(MyEnum.foo)}); } const std = @import("std"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_error=main.zig:7:69: error: enum 'main.MyEnum' has no member named 'foo' #expect_error=main.zig:1:16: note: enum declared here diff --git a/test/incremental/unreferenced_error b/test/incremental/unreferenced_error index 505fb3d5f4..c9a3277487 100644 --- a/test/incremental/unreferenced_error +++ b/test/incremental/unreferenced_error @@ -7,36 +7,40 @@ #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(a); + try std.Io.File.stdout().writeStreamingAll(io, a); } const a = "Hello, World!\n"; +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="Hello, World!\n" #update=introduce compile error #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(a); + try std.Io.File.stdout().writeStreamingAll(io, a); } const a = @compileError("bad a"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_error=main.zig:5:11: error: bad a #update=remove error reference #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(b); + try std.Io.File.stdout().writeStreamingAll(io, b); } const a = @compileError("bad a"); const b = "Hi there!\n"; +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="Hi there!\n" #update=introduce and remove reference to error #file=main.zig const std = @import("std"); pub fn main() !void { - try std.fs.File.stdout().writeAll(a); + try std.Io.File.stdout().writeStreamingAll(io, a); } const a = "Back to a\n"; const b = @compileError("bad b"); +const io = std.Io.Threaded.global_single_threaded.ioBasic(); #expect_stdout="Back to a\n" diff --git a/test/link/bss/main.zig b/test/link/bss/main.zig index 2785a8360f..0d69f97450 100644 --- a/test/link/bss/main.zig +++ b/test/link/bss/main.zig @@ -4,7 +4,7 @@ const std = @import("std"); var buffer: [0x1000000]u64 = [1]u64{0} ** 0x1000000; pub fn main() anyerror!void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &.{}); buffer[0x10] = 1; diff --git a/test/link/elf.zig b/test/link/elf.zig index 824fc76e92..d5b0eb7519 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -1323,7 +1323,7 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step { \\extern var live_var2: i32; \\extern fn live_fn2() void; \\pub fn main() void { - \\ var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + \\ var stdout_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &.{}); \\ stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail"); \\ live_fn2(); \\} @@ -1365,7 +1365,7 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step { \\extern var live_var2: i32; \\extern fn live_fn2() void; \\pub fn main() void { - \\ var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + \\ var stdout_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &.{}); \\ stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail"); \\ live_fn2(); \\} diff --git a/test/link/macho.zig b/test/link/macho.zig index 4fc0cad0ee..ccfecefa44 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -716,7 +716,7 @@ fn testHelloZig(b: *Build, opts: Options) *Step { const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes = \\const std = @import("std"); \\pub fn main() void { - \\ std.fs.File.stdout().writeAll("Hello world!\n") catch @panic("fail"); + \\ std.Io.File.stdout().writeStreamingAll(std.Options.debug_io, "Hello world!\n") catch @panic("fail"); \\} }); @@ -868,9 +868,10 @@ fn testLayout(b: *Build, opts: Options) *Step { } fn testLinkDirectlyCppTbd(b: *Build, opts: Options) *Step { + const io = b.graph.io; const test_step = addTestStep(b, "link-directly-cpp-tbd", opts); - const sdk = std.zig.system.darwin.getSdk(b.allocator, &opts.target.result) orelse + const sdk = std.zig.system.darwin.getSdk(b.allocator, io, &opts.target.result) orelse @panic("macOS SDK is required to run the test"); const exe = addExecutable(b, opts, .{ @@ -2371,7 +2372,7 @@ fn testTlsZig(b: *Build, opts: Options) *Step { \\threadlocal var x: i32 = 0; \\threadlocal var y: i32 = -1; \\pub fn main() void { - \\ var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + \\ var stdout_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &.{}); \\ stdout_writer.interface.print("{d} {d}\n", .{x, y}) catch unreachable; \\ x -= 1; \\ y += 1; diff --git a/test/link/wasm/extern/main.zig b/test/link/wasm/extern/main.zig index 9635f64a40..51c5f84181 100644 --- a/test/link/wasm/extern/main.zig +++ b/test/link/wasm/extern/main.zig @@ -3,6 +3,6 @@ const std = @import("std"); extern const foo: u32; pub fn main() void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &.{}); stdout_writer.interface.print("Result: {d}", .{foo}) catch {}; } diff --git a/test/src/Cases.zig b/test/src/Cases.zig index b1fece44d9..230b100b68 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -1,6 +1,8 @@ const Cases = @This(); const builtin = @import("builtin"); + const std = @import("std"); +const Io = std.Io; const assert = std.debug.assert; const Allocator = std.mem.Allocator; const getExternalExecutor = std.zig.system.getExternalExecutor; @@ -8,6 +10,7 @@ const ArrayList = std.ArrayList; gpa: Allocator, arena: Allocator, +io: Io, cases: std.array_list.Managed(Case), pub const IncrementalCase = struct { @@ -313,7 +316,7 @@ pub fn addCompile( /// Each file should include a test manifest as a contiguous block of comments at /// the end of the file. The first line should be the test type, followed by a set of /// key-value config values, followed by a blank line, then the expected output. -pub fn addFromDir(ctx: *Cases, dir: std.fs.Dir, b: *std.Build) void { +pub fn addFromDir(ctx: *Cases, dir: Io.Dir, b: *std.Build) void { var current_file: []const u8 = "none"; ctx.addFromDirInner(dir, ¤t_file, b) catch |err| { std.debug.panicExtra( @@ -326,16 +329,17 @@ pub fn addFromDir(ctx: *Cases, dir: std.fs.Dir, b: *std.Build) void { fn addFromDirInner( ctx: *Cases, - iterable_dir: std.fs.Dir, + iterable_dir: Io.Dir, /// This is kept up to date with the currently being processed file so /// that if any errors occur the caller knows it happened during this file. current_file: *[]const u8, b: *std.Build, ) !void { + const io = ctx.io; var it = try iterable_dir.walk(ctx.arena); var filenames: ArrayList([]const u8) = .empty; - while (try it.next()) |entry| { + while (try it.next(io)) |entry| { if (entry.kind != .file) continue; // Ignore stuff such as .swp files @@ -347,7 +351,7 @@ fn addFromDirInner( current_file.* = filename; const max_file_size = 10 * 1024 * 1024; - const src = try iterable_dir.readFileAllocOptions(filename, ctx.arena, .limited(max_file_size), .@"1", 0); + const src = try iterable_dir.readFileAllocOptions(io, filename, ctx.arena, .limited(max_file_size), .@"1", 0); // Parse the manifest var manifest = try TestManifest.parse(ctx.arena, src); @@ -376,6 +380,12 @@ fn addFromDirInner( // Other backends don't support new liveness format continue; } + + if (backend == .selfhosted and target.cpu.arch == .aarch64) { + // https://codeberg.org/ziglang/zig/pulls/30232#issuecomment-9182045 + continue; + } + if (backend == .selfhosted and target.os.tag == .macos and target.cpu.arch == .x86_64 and builtin.cpu.arch == .aarch64) { @@ -427,9 +437,10 @@ fn addFromDirInner( } } -pub fn init(gpa: Allocator, arena: Allocator) Cases { +pub fn init(gpa: Allocator, arena: Allocator, io: Io) Cases { return .{ .gpa = gpa, + .io = io, .cases = .init(gpa), .arena = arena, }; @@ -457,6 +468,7 @@ pub fn lowerToBuildSteps( parent_step: *std.Build.Step, options: CaseTestOptions, ) void { + const io = self.io; const host = b.resolveTargetQuery(.{}); const cases_dir_path = b.build_root.join(b.allocator, &.{ "test", "cases" }) catch @panic("OOM"); @@ -591,7 +603,7 @@ pub fn lowerToBuildSteps( }, .Execution => |expected_stdout| no_exec: { const run = if (case.target.result.ofmt == .c) run_step: { - if (getExternalExecutor(&host.result, &case.target.result, .{ .link_libc = true }) != .native) { + if (getExternalExecutor(io, &host.result, &case.target.result, .{ .link_libc = true }) != .native) { // We wouldn't be able to run the compiled C code. break :no_exec; } diff --git a/test/src/convert-stack-trace.zig b/test/src/convert-stack-trace.zig index 91be53a8e5..d23623c396 100644 --- a/test/src/convert-stack-trace.zig +++ b/test/src/convert-stack-trace.zig @@ -34,20 +34,20 @@ pub fn main() !void { const gpa = arena; - var threaded: std.Io.Threaded = .init(gpa); + var threaded: std.Io.Threaded = .init(gpa, .{}); defer threaded.deinit(); const io = threaded.io(); var read_buf: [1024]u8 = undefined; var write_buf: [1024]u8 = undefined; - const in_file = try std.fs.cwd().openFile(args[1], .{}); - defer in_file.close(); + const in_file = try std.Io.Dir.cwd().openFile(io, args[1], .{}); + defer in_file.close(io); - const out_file: std.fs.File = .stdout(); + const out_file: std.Io.File = .stdout(); var in_fr = in_file.reader(io, &read_buf); - var out_fw = out_file.writer(&write_buf); + var out_fw = out_file.writer(io, &write_buf); const w = &out_fw.interface; diff --git a/test/standalone/child_process/child.zig b/test/standalone/child_process/child.zig index 2e74f30882..80e2edaa7f 100644 --- a/test/standalone/child_process/child.zig +++ b/test/standalone/child_process/child.zig @@ -8,7 +8,7 @@ pub fn main() !void { var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); const arena = arena_state.allocator(); - var threaded: std.Io.Threaded = .init(arena); + var threaded: std.Io.Threaded = .init(arena, .{}); defer threaded.deinit(); const io = threaded.io(); @@ -26,28 +26,28 @@ fn run(allocator: std.mem.Allocator, io: Io) !void { const hello_arg = "hello arg"; const a1 = args.next() orelse unreachable; if (!std.mem.eql(u8, a1, hello_arg)) { - testError("first arg: '{s}'; want '{s}'", .{ a1, hello_arg }); + testError(io, "first arg: '{s}'; want '{s}'", .{ a1, hello_arg }); } if (args.next()) |a2| { - testError("expected only one arg; got more: {s}", .{a2}); + testError(io, "expected only one arg; got more: {s}", .{a2}); } // test stdout pipe; parent verifies - try std.fs.File.stdout().writeAll("hello from stdout"); + try std.Io.File.stdout().writeStreamingAll(io, "hello from stdout"); // test stdin pipe from parent const hello_stdin = "hello from stdin"; var buf: [hello_stdin.len]u8 = undefined; - const stdin: std.fs.File = .stdin(); + const stdin: std.Io.File = .stdin(); var reader = stdin.reader(io, &.{}); const n = try reader.interface.readSliceShort(&buf); if (!std.mem.eql(u8, buf[0..n], hello_stdin)) { - testError("stdin: '{s}'; want '{s}'", .{ buf[0..n], hello_stdin }); + testError(io, "stdin: '{s}'; want '{s}'", .{ buf[0..n], hello_stdin }); } } -fn testError(comptime fmt: []const u8, args: anytype) void { - var stderr_writer = std.fs.File.stderr().writer(&.{}); +fn testError(io: Io, comptime fmt: []const u8, args: anytype) void { + var stderr_writer = std.Io.File.stderr().writer(io, &.{}); const stderr = &stderr_writer.interface; stderr.print("CHILD TEST ERROR: ", .{}) catch {}; stderr.print(fmt, args) catch {}; diff --git a/test/standalone/child_process/main.zig b/test/standalone/child_process/main.zig index 5970cdd952..98d38bdee3 100644 --- a/test/standalone/child_process/main.zig +++ b/test/standalone/child_process/main.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Io = std.Io; pub fn main() !void { // make sure safety checks are enabled even in release modes @@ -20,7 +21,7 @@ pub fn main() !void { }; defer if (needs_free) gpa.free(child_path); - var threaded: std.Io.Threaded = .init(gpa); + var threaded: Io.Threaded = .init(gpa, .{}); defer threaded.deinit(); const io = threaded.io(); @@ -28,10 +29,10 @@ pub fn main() !void { child.stdin_behavior = .Pipe; child.stdout_behavior = .Pipe; child.stderr_behavior = .Inherit; - try child.spawn(); + try child.spawn(io); const child_stdin = child.stdin.?; - try child_stdin.writeAll("hello from stdin"); // verified in child - child_stdin.close(); + try child_stdin.writeStreamingAll(io, "hello from stdin"); // verified in child + child_stdin.close(io); child.stdin = null; const hello_stdout = "hello from stdout"; @@ -39,30 +40,30 @@ pub fn main() !void { var stdout_reader = child.stdout.?.readerStreaming(io, &.{}); const n = try stdout_reader.interface.readSliceShort(&buf); if (!std.mem.eql(u8, buf[0..n], hello_stdout)) { - testError("child stdout: '{s}'; want '{s}'", .{ buf[0..n], hello_stdout }); + testError(io, "child stdout: '{s}'; want '{s}'", .{ buf[0..n], hello_stdout }); } - switch (try child.wait()) { + switch (try child.wait(io)) { .Exited => |code| { const child_ok_code = 42; // set by child if no test errors if (code != child_ok_code) { - testError("child exit code: {d}; want {d}", .{ code, child_ok_code }); + testError(io, "child exit code: {d}; want {d}", .{ code, child_ok_code }); } }, - else => |term| testError("abnormal child exit: {}", .{term}), + else => |term| testError(io, "abnormal child exit: {}", .{term}), } 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} })); + try std.testing.expectError(error.FileNotFound, std.process.Child.run(gpa, io, .{ .argv = &.{missing_child_path} })); } var parent_test_error = false; -fn testError(comptime fmt: []const u8, args: anytype) void { - var stderr_writer = std.fs.File.stderr().writer(&.{}); +fn testError(io: Io, comptime fmt: []const u8, args: anytype) void { + var stderr_writer = Io.File.stderr().writer(io, &.{}); const stderr = &stderr_writer.interface; stderr.print("PARENT TEST ERROR: ", .{}) catch {}; stderr.print(fmt, args) catch {}; diff --git a/test/standalone/cmakedefine/check.zig b/test/standalone/cmakedefine/check.zig index 782e7f4dc3..c2f89ad112 100644 --- a/test/standalone/cmakedefine/check.zig +++ b/test/standalone/cmakedefine/check.zig @@ -9,8 +9,10 @@ pub fn main() !void { const actual_path = args[1]; const expected_path = args[2]; - const actual = try std.fs.cwd().readFileAlloc(actual_path, arena, .limited(1024 * 1024)); - const expected = try std.fs.cwd().readFileAlloc(expected_path, arena, .limited(1024 * 1024)); + const io = std.Io.Threaded.global_single_threaded.ioBasic(); + + const actual = try std.Io.Dir.cwd().readFileAlloc(io, actual_path, arena, .limited(1024 * 1024)); + const expected = try std.Io.Dir.cwd().readFileAlloc(io, expected_path, arena, .limited(1024 * 1024)); // The actual output starts with a comment which we should strip out before comparing. const comment_str = "/* This file was generated by ConfigHeader using the Zig Build System. */\n"; diff --git a/test/standalone/coff_dwarf/main.zig b/test/standalone/coff_dwarf/main.zig index e7590f3f07..7e314d5b28 100644 --- a/test/standalone/coff_dwarf/main.zig +++ b/test/standalone/coff_dwarf/main.zig @@ -11,7 +11,7 @@ pub fn main() void { var di: std.debug.SelfInfo = .init; defer di.deinit(gpa); - var threaded: std.Io.Threaded = .init(gpa); + var threaded: std.Io.Threaded = .init(gpa, .{}); defer threaded.deinit(); const io = threaded.io(); diff --git a/test/standalone/dirname/build.zig b/test/standalone/dirname/build.zig index 0da85e2923..b850680ba9 100644 --- a/test/standalone/dirname/build.zig +++ b/test/standalone/dirname/build.zig @@ -59,13 +59,15 @@ pub fn build(b: *std.Build) void { // Absolute path: const abs_path = setup_abspath: { + // TODO this is a bad pattern, don't do this + const io = b.graph.io; const temp_dir = b.makeTempPath(); - var dir = std.fs.cwd().openDir(temp_dir, .{}) catch @panic("failed to open temp dir"); - defer dir.close(); + var dir = std.Io.Dir.cwd().openDir(io, temp_dir, .{}) catch @panic("failed to open temp dir"); + defer dir.close(io); - var file = dir.createFile("foo.txt", .{}) catch @panic("failed to create file"); - file.close(); + var file = dir.createFile(io, "foo.txt", .{}) catch @panic("failed to create file"); + file.close(io); break :setup_abspath std.Build.LazyPath{ .cwd_relative = temp_dir }; }; diff --git a/test/standalone/dirname/exists_in.zig b/test/standalone/dirname/exists_in.zig index 7aec1f423d..ba2de2777f 100644 --- a/test/standalone/dirname/exists_in.zig +++ b/test/standalone/dirname/exists_in.zig @@ -34,8 +34,10 @@ fn run(allocator: std.mem.Allocator) !void { return error.BadUsage; }; - var dir = try std.fs.cwd().openDir(dir_path, .{}); - defer dir.close(); + const io = std.Io.Threaded.global_single_threaded.ioBasic(); - _ = try dir.statFile(relpath); + var dir = try std.Io.Dir.cwd().openDir(io, dir_path, .{}); + defer dir.close(io); + + _ = try dir.statFile(io, relpath, .{}); } diff --git a/test/standalone/dirname/touch.zig b/test/standalone/dirname/touch.zig index 43fcabf91e..134d53d2fc 100644 --- a/test/standalone/dirname/touch.zig +++ b/test/standalone/dirname/touch.zig @@ -26,14 +26,16 @@ fn run(allocator: std.mem.Allocator) !void { return error.BadUsage; }; - const dir_path = std.fs.path.dirname(path) orelse unreachable; - const basename = std.fs.path.basename(path); + const dir_path = std.Io.Dir.path.dirname(path) orelse unreachable; + const basename = std.Io.Dir.path.basename(path); - var dir = try std.fs.cwd().openDir(dir_path, .{}); - defer dir.close(); + const io = std.Io.Threaded.global_single_threaded.ioBasic(); - _ = dir.statFile(basename) catch { - var file = try dir.createFile(basename, .{}); - file.close(); + var dir = try std.Io.Dir.cwd().openDir(io, dir_path, .{}); + defer dir.close(io); + + _ = dir.statFile(io, basename, .{}) catch { + var file = try dir.createFile(io, basename, .{}); + file.close(io); }; } diff --git a/test/standalone/entry_point/check_differ.zig b/test/standalone/entry_point/check_differ.zig index 63d1ec0294..29b333632f 100644 --- a/test/standalone/entry_point/check_differ.zig +++ b/test/standalone/entry_point/check_differ.zig @@ -6,8 +6,10 @@ pub fn main() !void { const args = try std.process.argsAlloc(arena); if (args.len != 3) return error.BadUsage; // usage: 'check_differ <path a> <path b>' - const contents_1 = try std.fs.cwd().readFileAlloc(args[1], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty - const contents_2 = try std.fs.cwd().readFileAlloc(args[2], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty + const io = std.Io.Threaded.global_single_threaded.ioBasic(); + + const contents_1 = try std.Io.Dir.cwd().readFileAlloc(io, args[1], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty + const contents_2 = try std.Io.Dir.cwd().readFileAlloc(io, args[2], arena, .limited(1024 * 1024 * 64)); // 64 MiB ought to be plenty if (std.mem.eql(u8, contents_1, contents_2)) { return error.FilesMatch; diff --git a/test/standalone/env_vars/main.zig b/test/standalone/env_vars/main.zig index 12b911404a..b85105642e 100644 --- a/test/standalone/env_vars/main.zig +++ b/test/standalone/env_vars/main.zig @@ -3,6 +3,8 @@ const builtin = @import("builtin"); // Note: the environment variables under test are set by the build.zig pub fn main() !void { + @setEvalBranchQuota(10000); + var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; defer _ = gpa.deinit(); const allocator = gpa.allocator(); diff --git a/test/standalone/glibc_compat/glibc_runtime_check.zig b/test/standalone/glibc_compat/glibc_runtime_check.zig index 82f4a54ee1..78b1f7efb3 100644 --- a/test/standalone/glibc_compat/glibc_runtime_check.zig +++ b/test/standalone/glibc_compat/glibc_runtime_check.zig @@ -28,10 +28,10 @@ extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: [*]const u8) c_int; // PR #17034 - fstat moved between libc_nonshared and libc fn checkStat() !void { - const cwdFd = std.fs.cwd().fd; + const cwd_fd = std.Io.Dir.cwd().handle; var buf: [256]u8 = @splat(0); - var result = fstatat(cwdFd, "a_file_that_definitely_does_not_exist", &buf, 0); + var result = fstatat(cwd_fd, "a_file_that_definitely_does_not_exist", &buf, 0); assert(result == -1); assert(std.posix.errno(result) == .NOENT); diff --git a/test/standalone/install_headers/check_exists.zig b/test/standalone/install_headers/check_exists.zig index 62706749aa..50ad4d0818 100644 --- a/test/standalone/install_headers/check_exists.zig +++ b/test/standalone/install_headers/check_exists.zig @@ -11,8 +11,10 @@ pub fn main() !void { var arg_it = try std.process.argsWithAllocator(arena); _ = arg_it.next(); - const cwd = std.fs.cwd(); - const cwd_realpath = try cwd.realpathAlloc(arena, "."); + const io = std.Io.Threaded.global_single_threaded.ioBasic(); + + const cwd = std.Io.Dir.cwd(); + const cwd_realpath = try cwd.realPathFileAlloc(io, ".", arena); while (arg_it.next()) |file_path| { if (file_path.len > 0 and file_path[0] == '!') { @@ -20,7 +22,7 @@ pub fn main() !void { "exclusive file check '{s}{c}{s}' failed", .{ cwd_realpath, std.fs.path.sep, file_path[1..] }, ); - if (std.fs.cwd().statFile(file_path[1..])) |_| { + if (cwd.statFile(io, file_path[1..], .{})) |_| { return error.FileFound; } else |err| switch (err) { error.FileNotFound => {}, @@ -31,7 +33,7 @@ pub fn main() !void { "inclusive file check '{s}{c}{s}' failed", .{ cwd_realpath, std.fs.path.sep, file_path }, ); - _ = try std.fs.cwd().statFile(file_path); + _ = try cwd.statFile(io, file_path, .{}); } } } diff --git a/test/standalone/ios/build.zig b/test/standalone/ios/build.zig index b07b5b17ea..b87d55993b 100644 --- a/test/standalone/ios/build.zig +++ b/test/standalone/ios/build.zig @@ -23,7 +23,9 @@ pub fn build(b: *std.Build) void { }), }); - if (std.zig.system.darwin.getSdk(b.allocator, &target.result)) |sdk| { + const io = b.graph.io; + + if (std.zig.system.darwin.getSdk(b.allocator, io, &target.result)) |sdk| { b.sysroot = sdk; exe.root_module.addSystemIncludePath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/usr/include" }) }); exe.root_module.addSystemFrameworkPath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/System/Library/Frameworks" }) }); diff --git a/test/standalone/libfuzzer/main.zig b/test/standalone/libfuzzer/main.zig index b275b6d593..0bc093d870 100644 --- a/test/standalone/libfuzzer/main.zig +++ b/test/standalone/libfuzzer/main.zig @@ -15,13 +15,13 @@ pub fn main() !void { defer args.deinit(); _ = args.skip(); // executable name - var threaded: std.Io.Threaded = .init(gpa); + var threaded: std.Io.Threaded = .init(gpa, .{}); defer threaded.deinit(); const io = threaded.io(); const cache_dir_path = args.next() orelse @panic("expected cache directory path argument"); - var cache_dir = try std.fs.cwd().openDir(cache_dir_path, .{}); - defer cache_dir.close(); + var cache_dir = try std.Io.Dir.cwd().openDir(io, cache_dir_path, .{}); + defer cache_dir.close(io); abi.fuzzer_init(.fromSlice(cache_dir_path)); abi.fuzzer_init_test(testOne, .fromSlice("test")); @@ -30,8 +30,8 @@ pub fn main() !void { const pc_digest = abi.fuzzer_coverage().id; const coverage_file_path = "v/" ++ std.fmt.hex(pc_digest); - const coverage_file = try cache_dir.openFile(coverage_file_path, .{}); - defer coverage_file.close(); + const coverage_file = try cache_dir.openFile(io, coverage_file_path, .{}); + defer coverage_file.close(io); var read_buf: [@sizeOf(abi.SeenPcsHeader)]u8 = undefined; var r = coverage_file.reader(io, &read_buf); @@ -42,6 +42,6 @@ pub fn main() !void { const expected_len = @sizeOf(abi.SeenPcsHeader) + try std.math.divCeil(usize, pcs_header.pcs_len, @bitSizeOf(usize)) * @sizeOf(usize) + pcs_header.pcs_len * @sizeOf(usize); - if (try coverage_file.getEndPos() != expected_len) + if (try coverage_file.length(io) != expected_len) return error.WrongEnd; } diff --git a/test/standalone/posix/cwd.zig b/test/standalone/posix/cwd.zig index 43dcc63bfe..3bd1ac066a 100644 --- a/test/standalone/posix/cwd.zig +++ b/test/standalone/posix/cwd.zig @@ -1,21 +1,30 @@ -const std = @import("std"); const builtin = @import("builtin"); +const std = @import("std"); +const Io = std.Io; +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; + const path_max = std.fs.max_path_bytes; pub fn main() !void { - if (builtin.target.os.tag == .wasi) { - // WASI doesn't support changing the working directory at all. - return; + switch (builtin.target.os.tag) { + .wasi => return, // WASI doesn't support changing the working directory at all. + .windows => return, // POSIX is not implemented by Windows + else => {}, } - var Allocator = std.heap.DebugAllocator(.{}){}; - const a = Allocator.allocator(); - defer std.debug.assert(Allocator.deinit() == .ok); + var debug_allocator: std.heap.DebugAllocator(.{}) = .{}; + defer assert(debug_allocator.deinit() == .ok); + const gpa = debug_allocator.allocator(); + + var threaded: std.Io.Threaded = .init(gpa, .{}); + defer threaded.deinit(); + const io = threaded.io(); try test_chdir_self(); try test_chdir_absolute(); - try test_chdir_relative(a); + try test_chdir_relative(gpa, io); } // get current working directory and expect it to match given path @@ -46,20 +55,20 @@ fn test_chdir_absolute() !void { try expect_cwd(parent); } -fn test_chdir_relative(a: std.mem.Allocator) !void { - var tmp = std.testing.tmpDir(.{}); - defer tmp.cleanup(); +fn test_chdir_relative(gpa: Allocator, io: Io) !void { + var tmp = tmpDir(io, .{}); + defer tmp.cleanup(io); // Use the tmpDir parent_dir as the "base" for the test. Then cd into the child - try tmp.parent_dir.setAsCwd(); + try std.process.setCurrentDir(io, tmp.parent_dir); // Capture base working directory path, to build expected full path var base_cwd_buf: [path_max]u8 = undefined; const base_cwd = try std.posix.getcwd(base_cwd_buf[0..]); const relative_dir_name = &tmp.sub_path; - const expected_path = try std.fs.path.resolve(a, &.{ base_cwd, relative_dir_name }); - defer a.free(expected_path); + const expected_path = try std.fs.path.resolve(gpa, &.{ base_cwd, relative_dir_name }); + defer gpa.free(expected_path); // change current working directory to new test directory try std.posix.chdir(relative_dir_name); @@ -68,8 +77,46 @@ fn test_chdir_relative(a: std.mem.Allocator) !void { const new_cwd = try std.posix.getcwd(new_cwd_buf[0..]); // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase - const resolved_cwd = try std.fs.path.resolve(a, &.{new_cwd}); - defer a.free(resolved_cwd); + const resolved_cwd = try std.fs.path.resolve(gpa, &.{new_cwd}); + defer gpa.free(resolved_cwd); try std.testing.expectEqualStrings(expected_path, resolved_cwd); } + +pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir { + var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; + std.crypto.random.bytes(&random_bytes); + var sub_path: [TmpDir.sub_path_len]u8 = undefined; + _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); + + const cwd = Io.Dir.cwd(); + var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch + @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir"); + defer cache_dir.close(io); + const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch + @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir"); + const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch + @panic("unable to make tmp dir for testing: unable to make and open the tmp dir"); + + return .{ + .dir = dir, + .parent_dir = parent_dir, + .sub_path = sub_path, + }; +} + +pub const TmpDir = struct { + dir: Io.Dir, + parent_dir: Io.Dir, + sub_path: [sub_path_len]u8, + + const random_bytes_count = 12; + const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); + + pub fn cleanup(self: *TmpDir, io: Io) void { + self.dir.close(io); + self.parent_dir.deleteTree(io, &self.sub_path) catch {}; + self.parent_dir.close(io); + self.* = undefined; + } +}; diff --git a/test/standalone/posix/relpaths.zig b/test/standalone/posix/relpaths.zig index 40e2e09464..b5b2bb5f61 100644 --- a/test/standalone/posix/relpaths.zig +++ b/test/standalone/posix/relpaths.zig @@ -1,71 +1,32 @@ // Test relative paths through POSIX APIS. These tests have to change the cwd, so // they shouldn't be Zig unit tests. -const std = @import("std"); const builtin = @import("builtin"); +const std = @import("std"); +const Io = std.Io; + pub fn main() !void { if (builtin.target.os.tag == .wasi) return; // Can link, but can't change into tmpDir - var Allocator = std.heap.DebugAllocator(.{}){}; - const a = Allocator.allocator(); - defer std.debug.assert(Allocator.deinit() == .ok); + var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + const gpa = debug_allocator.allocator(); + defer std.debug.assert(debug_allocator.deinit() == .ok); - var tmp = std.testing.tmpDir(.{}); - defer tmp.cleanup(); - - // Want to test relative paths, so cd into the tmpdir for these tests - try tmp.dir.setAsCwd(); - - try test_symlink(a, tmp); - try test_link(tmp); -} - -fn test_symlink(a: std.mem.Allocator, tmp: std.testing.TmpDir) !void { - const target_name = "symlink-target"; - const symlink_name = "symlinker"; - - // Create the target file - try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "nonsense" }); - - if (builtin.target.os.tag == .windows) { - const wtarget_name = try std.unicode.wtf8ToWtf16LeAllocZ(a, target_name); - const wsymlink_name = try std.unicode.wtf8ToWtf16LeAllocZ(a, symlink_name); - defer a.free(wtarget_name); - defer a.free(wsymlink_name); - - std.os.windows.CreateSymbolicLink(tmp.dir.fd, wsymlink_name, wtarget_name, false) catch |err| switch (err) { - // Symlink requires admin privileges on windows, so this test can legitimately fail. - error.AccessDenied => return, - else => return err, - }; - } else { - try std.posix.symlink(target_name, symlink_name); - } + var threaded: std.Io.Threaded = .init(gpa, .{}); + defer threaded.deinit(); + const io = threaded.io(); - var buffer: [std.fs.max_path_bytes]u8 = undefined; - const given = try std.posix.readlink(symlink_name, buffer[0..]); - try std.testing.expectEqualStrings(target_name, given); -} + var tmp = tmpDir(io, .{}); + defer tmp.cleanup(io); -fn getLinkInfo(fd: std.posix.fd_t) !struct { std.posix.ino_t, std.posix.nlink_t } { - if (builtin.target.os.tag == .linux) { - const stx = try std.os.linux.wrapped.statx( - fd, - "", - std.posix.AT.EMPTY_PATH, - .{ .INO = true, .NLINK = true }, - ); - std.debug.assert(stx.mask.INO); - std.debug.assert(stx.mask.NLINK); - return .{ stx.ino, stx.nlink }; - } + // Want to test relative paths, so cd into the tmpdir for these tests + try std.process.setCurrentDir(io, tmp.dir); - const st = try std.posix.fstat(fd); - return .{ st.ino, st.nlink }; + try test_link(io, tmp); } -fn test_link(tmp: std.testing.TmpDir) !void { +fn test_link(io: Io, tmp: TmpDir) !void { switch (builtin.target.os.tag) { .linux, .illumos => {}, else => return, @@ -74,29 +35,67 @@ fn test_link(tmp: std.testing.TmpDir) !void { const target_name = "link-target"; const link_name = "newlink"; - try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "example" }); + try tmp.dir.writeFile(io, .{ .sub_path = target_name, .data = "example" }); // Test 1: create the relative link from inside tmp - try std.posix.link(target_name, link_name); + try Io.Dir.hardLink(.cwd(), target_name, .cwd(), link_name, io, .{}); // Verify - const efd = try tmp.dir.openFile(target_name, .{}); - defer efd.close(); + const efd = try tmp.dir.openFile(io, target_name, .{}); + defer efd.close(io); - const nfd = try tmp.dir.openFile(link_name, .{}); - defer nfd.close(); + const nfd = try tmp.dir.openFile(io, link_name, .{}); + defer nfd.close(io); { - const eino, _ = try getLinkInfo(efd.handle); - const nino, const nlink = try getLinkInfo(nfd.handle); - try std.testing.expectEqual(eino, nino); - try std.testing.expectEqual(@as(std.posix.nlink_t, 2), nlink); + const e_stat = try efd.stat(io); + const n_stat = try nfd.stat(io); + try std.testing.expectEqual(e_stat.inode, n_stat.inode); + try std.testing.expectEqual(2, n_stat.nlink); } // Test 2: Remove the link and see the stats update - try std.posix.unlink(link_name); + try Io.Dir.cwd().deleteFile(io, link_name); { - _, const elink = try getLinkInfo(efd.handle); - try std.testing.expectEqual(@as(std.posix.nlink_t, 1), elink); + const e_stat = try efd.stat(io); + try std.testing.expectEqual(1, e_stat.nlink); } } + +pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir { + var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; + std.crypto.random.bytes(&random_bytes); + var sub_path: [TmpDir.sub_path_len]u8 = undefined; + _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); + + const cwd = Io.Dir.cwd(); + var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch + @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir"); + defer cache_dir.close(io); + const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch + @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir"); + const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch + @panic("unable to make tmp dir for testing: unable to make and open the tmp dir"); + + return .{ + .dir = dir, + .parent_dir = parent_dir, + .sub_path = sub_path, + }; +} + +pub const TmpDir = struct { + dir: Io.Dir, + parent_dir: Io.Dir, + sub_path: [sub_path_len]u8, + + const random_bytes_count = 12; + const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); + + pub fn cleanup(self: *TmpDir, io: Io) void { + self.dir.close(io); + self.parent_dir.deleteTree(io, &self.sub_path) catch {}; + self.parent_dir.close(io); + self.* = undefined; + } +}; diff --git a/test/standalone/run_cwd/check_file_exists.zig b/test/standalone/run_cwd/check_file_exists.zig index 640fc99a7a..a885c7dafd 100644 --- a/test/standalone/run_cwd/check_file_exists.zig +++ b/test/standalone/run_cwd/check_file_exists.zig @@ -8,7 +8,9 @@ pub fn main() !void { if (args.len != 2) return error.BadUsage; const path = args[1]; - std.fs.cwd().access(path, .{}) catch return error.AccessFailed; + const io = std.Io.Threaded.global_single_threaded.ioBasic(); + + std.Io.Dir.cwd().access(io, path, .{}) catch return error.AccessFailed; } const std = @import("std"); diff --git a/test/standalone/run_output_caching/main.zig b/test/standalone/run_output_caching/main.zig index e4e6332f11..9786101d32 100644 --- a/test/standalone/run_output_caching/main.zig +++ b/test/standalone/run_output_caching/main.zig @@ -1,10 +1,11 @@ const std = @import("std"); pub fn main() !void { + const io = std.Io.Threaded.global_single_threaded.ioBasic(); var args = try std.process.argsWithAllocator(std.heap.page_allocator); _ = args.skip(); const filename = args.next().?; - const file = try std.fs.cwd().createFile(filename, .{}); - defer file.close(); - try file.writeAll(filename); + const file = try std.Io.Dir.cwd().createFile(io, filename, .{}); + defer file.close(io); + try file.writeStreamingAll(io, filename); } diff --git a/test/standalone/run_output_paths/create_file.zig b/test/standalone/run_output_paths/create_file.zig index 260c36f10c..7efa8e051a 100644 --- a/test/standalone/run_output_paths/create_file.zig +++ b/test/standalone/run_output_paths/create_file.zig @@ -1,16 +1,17 @@ const std = @import("std"); pub fn main() !void { + const io = std.Io.Threaded.global_single_threaded.ioBasic(); var args = try std.process.argsWithAllocator(std.heap.page_allocator); _ = args.skip(); const dir_name = args.next().?; - const dir = try std.fs.cwd().openDir(if (std.mem.startsWith(u8, dir_name, "--dir=")) + const dir = try std.Io.Dir.cwd().openDir(io, if (std.mem.startsWith(u8, dir_name, "--dir=")) dir_name["--dir=".len..] else dir_name, .{}); const file_name = args.next().?; - const file = try dir.createFile(file_name, .{}); - var file_writer = file.writer(&.{}); + const file = try dir.createFile(io, file_name, .{}); + var file_writer = file.writer(io, &.{}); try file_writer.interface.print( \\{s} \\{s} diff --git a/test/standalone/self_exe_symlink/build.zig b/test/standalone/self_exe_symlink/build.zig index 651740c04b..137848b953 100644 --- a/test/standalone/self_exe_symlink/build.zig +++ b/test/standalone/self_exe_symlink/build.zig @@ -9,10 +9,6 @@ pub fn build(b: *std.Build) void { const optimize: std.builtin.OptimizeMode = .Debug; const target = b.graph.host; - // The test requires getFdPath in order to to get the path of the - // File returned by openSelfExe - if (!std.os.isGetFdPathSupportedOnTarget(target.result.os)) return; - const main = b.addExecutable(.{ .name = "main", .root_module = b.createModule(.{ diff --git a/test/standalone/self_exe_symlink/create-symlink.zig b/test/standalone/self_exe_symlink/create-symlink.zig index dac5891ba8..d725207320 100644 --- a/test/standalone/self_exe_symlink/create-symlink.zig +++ b/test/standalone/self_exe_symlink/create-symlink.zig @@ -14,5 +14,8 @@ pub fn main() anyerror!void { // If `exe_path` is relative to our cwd, we need to convert it to be relative to the dirname of `symlink_path`. const exe_rel_path = try std.fs.path.relative(allocator, std.fs.path.dirname(symlink_path) orelse ".", exe_path); defer allocator.free(exe_rel_path); - try std.fs.cwd().symLink(exe_rel_path, symlink_path, .{}); + + const io = std.Io.Threaded.global_single_threaded.ioBasic(); + + try std.Io.Dir.cwd().symLink(io, exe_rel_path, symlink_path, .{}); } diff --git a/test/standalone/self_exe_symlink/main.zig b/test/standalone/self_exe_symlink/main.zig index b74c4c7f95..fa2c3380b5 100644 --- a/test/standalone/self_exe_symlink/main.zig +++ b/test/standalone/self_exe_symlink/main.zig @@ -1,17 +1,22 @@ const std = @import("std"); pub fn main() !void { - var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; - defer std.debug.assert(gpa.deinit() == .ok); - const allocator = gpa.allocator(); + var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + defer if (debug_allocator.deinit() == .leak) @panic("found memory leaks"); + const gpa = debug_allocator.allocator(); - const self_path = try std.fs.selfExePathAlloc(allocator); - defer allocator.free(self_path); + var threaded: std.Io.Threaded = .init(gpa, .{}); + defer threaded.deinit(); + const io = threaded.io(); + + const self_path = try std.process.executablePathAlloc(io, gpa); + defer gpa.free(self_path); + + var self_exe = try std.process.openExecutable(io, .{}); + defer self_exe.close(io); - var self_exe = try std.fs.openSelfExe(.{}); - defer self_exe.close(); var buf: [std.fs.max_path_bytes]u8 = undefined; - const self_exe_path = try std.os.getFdPath(self_exe.handle, &buf); + const self_exe_path = buf[0..try self_exe.realPath(io, &buf)]; try std.testing.expectEqualStrings(self_exe_path, self_path); } diff --git a/test/standalone/simple/cat/main.zig b/test/standalone/simple/cat/main.zig index 9ea980aecc..0135ac4b50 100644 --- a/test/standalone/simple/cat/main.zig +++ b/test/standalone/simple/cat/main.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const fs = std.fs; +const Io = std.Io; const mem = std.mem; const warn = std.log.warn; const fatal = std.process.fatal; @@ -9,7 +9,7 @@ pub fn main() !void { defer arena_instance.deinit(); const arena = arena_instance.allocator(); - var threaded: std.Io.Threaded = .init(arena); + var threaded: std.Io.Threaded = .init(arena, .{}); defer threaded.deinit(); const io = threaded.io(); @@ -18,11 +18,11 @@ pub fn main() !void { const exe = args[0]; var catted_anything = false; var stdout_buffer: [4096]u8 = undefined; - var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer); + var stdout_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer); const stdout = &stdout_writer.interface; - var stdin_reader = fs.File.stdin().readerStreaming(io, &.{}); + var stdin_reader = Io.File.stdin().readerStreaming(io, &.{}); - const cwd = fs.cwd(); + const cwd = Io.Dir.cwd(); for (args[1..]) |arg| { if (mem.eql(u8, arg, "-")) { @@ -32,8 +32,8 @@ pub fn main() !void { } else if (mem.startsWith(u8, arg, "-")) { return usage(exe); } else { - const file = cwd.openFile(arg, .{}) catch |err| fatal("unable to open file: {t}\n", .{err}); - defer file.close(); + const file = cwd.openFile(io, arg, .{}) catch |err| fatal("unable to open file: {t}\n", .{err}); + defer file.close(io); catted_anything = true; var file_reader = file.reader(io, &.{}); diff --git a/test/standalone/simple/guess_number/main.zig b/test/standalone/simple/guess_number/main.zig index d477de2b78..b98d109f21 100644 --- a/test/standalone/simple/guess_number/main.zig +++ b/test/standalone/simple/guess_number/main.zig @@ -1,10 +1,23 @@ const builtin = @import("builtin"); const std = @import("std"); +// See https://github.com/ziglang/zig/issues/24510 +// for the plan to simplify this code. pub fn main() !void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + defer _ = debug_allocator.deinit(); + const gpa = debug_allocator.allocator(); + + var threaded: std.Io.Threaded = .init(gpa, .{}); + defer threaded.deinit(); + const io = threaded.io(); + + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); const out = &stdout_writer.interface; - const stdin: std.fs.File = .stdin(); + + var line_buffer: [20]u8 = undefined; + var stdin_reader: std.Io.File.Reader = .init(.stdin(), io, &line_buffer); + const in = &stdin_reader.interface; try out.writeAll("Welcome to the Guess Number Game in Zig.\n"); @@ -12,13 +25,15 @@ pub fn main() !void { while (true) { try out.writeAll("\nGuess a number between 1 and 100: "); - var line_buf: [20]u8 = undefined; - const amt = try stdin.read(&line_buf); - if (amt == line_buf.len) { - try out.writeAll("Input too long.\n"); - continue; - } - const line = std.mem.trimEnd(u8, line_buf[0..amt], "\r\n"); + const untrimmed_line = in.takeSentinel('\n') catch |err| switch (err) { + error.StreamTooLong => { + try out.writeAll("Line too long.\n"); + _ = try in.discardDelimiterInclusive('\n'); + continue; + }, + else => |e| return e, + }; + const line = std.mem.trimEnd(u8, untrimmed_line, "\r\n"); const guess = std.fmt.parseUnsigned(u8, line, 10) catch { try out.writeAll("Invalid number.\n"); diff --git a/test/standalone/simple/hello_world/hello.zig b/test/standalone/simple/hello_world/hello.zig index 3b2b910687..a031d6c6f0 100644 --- a/test/standalone/simple/hello_world/hello.zig +++ b/test/standalone/simple/hello_world/hello.zig @@ -1,5 +1,15 @@ const std = @import("std"); +// See https://github.com/ziglang/zig/issues/24510 +// for the plan to simplify this code. pub fn main() !void { - try std.fs.File.stdout().writeAll("Hello, World!\n"); + var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + defer _ = debug_allocator.deinit(); + const gpa = debug_allocator.allocator(); + + var threaded: std.Io.Threaded = .init(gpa, .{}); + defer threaded.deinit(); + const io = threaded.io(); + + try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } diff --git a/test/standalone/windows_argv/build.zig b/test/standalone/windows_argv/build.zig index df988d2371..afe6dd80e5 100644 --- a/test/standalone/windows_argv/build.zig +++ b/test/standalone/windows_argv/build.zig @@ -67,7 +67,7 @@ pub fn build(b: *std.Build) !void { // Only target the MSVC ABI if MSVC/Windows SDK is available const has_msvc = has_msvc: { - const sdk = std.zig.WindowsSdk.find(b.allocator, builtin.cpu.arch) catch |err| switch (err) { + const sdk = std.zig.WindowsSdk.find(b.allocator, b.graph.io, builtin.cpu.arch) catch |err| switch (err) { error.OutOfMemory => @panic("oom"), else => break :has_msvc false, }; diff --git a/test/standalone/windows_bat_args/echo-args.zig b/test/standalone/windows_bat_args/echo-args.zig index 054c4a6975..6aeb43d56c 100644 --- a/test/standalone/windows_bat_args/echo-args.zig +++ b/test/standalone/windows_bat_args/echo-args.zig @@ -5,7 +5,9 @@ pub fn main() !void { defer arena_state.deinit(); const arena = arena_state.allocator(); - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + const io = std.Options.debug_io; + + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); const stdout = &stdout_writer.interface; var args = try std.process.argsAlloc(arena); for (args[1..], 1..) |arg, i| { diff --git a/test/standalone/windows_bat_args/fuzz.zig b/test/standalone/windows_bat_args/fuzz.zig index 8b9895b52d..28749259f7 100644 --- a/test/standalone/windows_bat_args/fuzz.zig +++ b/test/standalone/windows_bat_args/fuzz.zig @@ -1,5 +1,7 @@ -const std = @import("std"); const builtin = @import("builtin"); + +const std = @import("std"); +const Io = std.Io; const Allocator = std.mem.Allocator; pub fn main() anyerror!void { @@ -7,6 +9,10 @@ pub fn main() anyerror!void { defer std.debug.assert(debug_alloc_inst.deinit() == .ok); const gpa = debug_alloc_inst.allocator(); + var threaded: Io.Threaded = .init(gpa, .{}); + defer threaded.deinit(); + const io = threaded.io(); + var it = try std.process.argsWithAllocator(gpa); defer it.deinit(); _ = it.next() orelse unreachable; // skip binary name @@ -36,11 +42,11 @@ pub fn main() anyerror!void { std.debug.print("rand seed: {}\n", .{seed}); } - var tmp = std.testing.tmpDir(.{}); - defer tmp.cleanup(); + var tmp = tmpDir(io, .{}); + defer tmp.cleanup(io); - try tmp.dir.setAsCwd(); - defer tmp.parent_dir.setAsCwd() catch {}; + try std.process.setCurrentDir(io, tmp.dir); + defer std.process.setCurrentDir(io, tmp.parent_dir) catch {}; // `child_exe_path_orig` might be relative; make it relative to our new cwd. const child_exe_path = try std.fs.path.resolve(gpa, &.{ "..\\..\\..", child_exe_path_orig }); @@ -56,15 +62,15 @@ pub fn main() anyerror!void { const preamble_len = buf.items.len; try buf.appendSlice(gpa, " %*"); - try tmp.dir.writeFile(.{ .sub_path = "args1.bat", .data = buf.items }); + try tmp.dir.writeFile(io, .{ .sub_path = "args1.bat", .data = buf.items }); buf.shrinkRetainingCapacity(preamble_len); try buf.appendSlice(gpa, " %1 %2 %3 %4 %5 %6 %7 %8 %9"); - try tmp.dir.writeFile(.{ .sub_path = "args2.bat", .data = buf.items }); + try tmp.dir.writeFile(io, .{ .sub_path = "args2.bat", .data = buf.items }); buf.shrinkRetainingCapacity(preamble_len); try buf.appendSlice(gpa, " \"%~1\" \"%~2\" \"%~3\" \"%~4\" \"%~5\" \"%~6\" \"%~7\" \"%~8\" \"%~9\""); - try tmp.dir.writeFile(.{ .sub_path = "args3.bat", .data = buf.items }); + try tmp.dir.writeFile(io, .{ .sub_path = "args3.bat", .data = buf.items }); buf.shrinkRetainingCapacity(preamble_len); var i: u64 = 0; @@ -72,19 +78,19 @@ pub fn main() anyerror!void { const rand_arg = try randomArg(gpa, rand); defer gpa.free(rand_arg); - try testExec(gpa, &.{rand_arg}, null); + try testExec(gpa, io, &.{rand_arg}, null); i += 1; } } -fn testExec(gpa: std.mem.Allocator, args: []const []const u8, env: ?*std.process.EnvMap) !void { - try testExecBat(gpa, "args1.bat", args, env); - try testExecBat(gpa, "args2.bat", args, env); - try testExecBat(gpa, "args3.bat", args, env); +fn testExec(gpa: Allocator, io: Io, args: []const []const u8, env: ?*std.process.EnvMap) !void { + try testExecBat(gpa, io, "args1.bat", args, env); + try testExecBat(gpa, io, "args2.bat", args, env); + try testExecBat(gpa, io, "args3.bat", args, env); } -fn testExecBat(gpa: std.mem.Allocator, bat: []const u8, args: []const []const u8, env: ?*std.process.EnvMap) !void { +fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8, env: ?*std.process.EnvMap) !void { const argv = try gpa.alloc([]const u8, 1 + args.len); defer gpa.free(argv); argv[0] = bat; @@ -92,8 +98,7 @@ fn testExecBat(gpa: std.mem.Allocator, bat: []const u8, args: []const []const u8 const can_have_trailing_empty_args = std.mem.eql(u8, bat, "args3.bat"); - const result = try std.process.Child.run(.{ - .allocator = gpa, + const result = try std.process.Child.run(gpa, io, .{ .env_map = env, .argv = argv, }); @@ -163,3 +168,41 @@ fn randomArg(gpa: Allocator, rand: std.Random) ![]const u8 { return buf.toOwnedSlice(gpa); } + +pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir { + var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; + std.crypto.random.bytes(&random_bytes); + var sub_path: [TmpDir.sub_path_len]u8 = undefined; + _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); + + const cwd = Io.Dir.cwd(); + var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch + @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir"); + defer cache_dir.close(io); + const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch + @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir"); + const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch + @panic("unable to make tmp dir for testing: unable to make and open the tmp dir"); + + return .{ + .dir = dir, + .parent_dir = parent_dir, + .sub_path = sub_path, + }; +} + +pub const TmpDir = struct { + dir: Io.Dir, + parent_dir: Io.Dir, + sub_path: [sub_path_len]u8, + + const random_bytes_count = 12; + const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); + + pub fn cleanup(self: *TmpDir, io: Io) void { + self.dir.close(io); + self.parent_dir.deleteTree(io, &self.sub_path) catch {}; + self.parent_dir.close(io); + self.* = undefined; + } +}; diff --git a/test/standalone/windows_bat_args/test.zig b/test/standalone/windows_bat_args/test.zig index 4690d983f3..e0d1abe806 100644 --- a/test/standalone/windows_bat_args/test.zig +++ b/test/standalone/windows_bat_args/test.zig @@ -1,20 +1,25 @@ const std = @import("std"); +const Io = std.Io; +const Allocator = std.mem.Allocator; pub fn main() anyerror!void { var debug_alloc_inst: std.heap.DebugAllocator(.{}) = .init; defer std.debug.assert(debug_alloc_inst.deinit() == .ok); const gpa = debug_alloc_inst.allocator(); + var threaded: Io.Threaded = .init(gpa, .{}); + const io = threaded.io(); + var it = try std.process.argsWithAllocator(gpa); defer it.deinit(); _ = it.next() orelse unreachable; // skip binary name const child_exe_path_orig = it.next() orelse unreachable; - var tmp = std.testing.tmpDir(.{}); - defer tmp.cleanup(); + var tmp = tmpDir(io, .{}); + defer tmp.cleanup(io); - try tmp.dir.setAsCwd(); - defer tmp.parent_dir.setAsCwd() catch {}; + try std.process.setCurrentDir(io, tmp.dir); + defer std.process.setCurrentDir(io, tmp.parent_dir) catch {}; // `child_exe_path_orig` might be relative; make it relative to our new cwd. const child_exe_path = try std.fs.path.resolve(gpa, &.{ "..\\..\\..", child_exe_path_orig }); @@ -30,53 +35,53 @@ pub fn main() anyerror!void { const preamble_len = buf.items.len; try buf.appendSlice(gpa, " %*"); - try tmp.dir.writeFile(.{ .sub_path = "args1.bat", .data = buf.items }); + try tmp.dir.writeFile(io, .{ .sub_path = "args1.bat", .data = buf.items }); buf.shrinkRetainingCapacity(preamble_len); try buf.appendSlice(gpa, " %1 %2 %3 %4 %5 %6 %7 %8 %9"); - try tmp.dir.writeFile(.{ .sub_path = "args2.bat", .data = buf.items }); + try tmp.dir.writeFile(io, .{ .sub_path = "args2.bat", .data = buf.items }); buf.shrinkRetainingCapacity(preamble_len); try buf.appendSlice(gpa, " \"%~1\" \"%~2\" \"%~3\" \"%~4\" \"%~5\" \"%~6\" \"%~7\" \"%~8\" \"%~9\""); - try tmp.dir.writeFile(.{ .sub_path = "args3.bat", .data = buf.items }); + try tmp.dir.writeFile(io, .{ .sub_path = "args3.bat", .data = buf.items }); buf.shrinkRetainingCapacity(preamble_len); // Test cases are from https://github.com/rust-lang/rust/blob/master/tests/ui/std/windows-bat-args.rs - try testExecError(error.InvalidBatchScriptArg, gpa, &.{"\x00"}); - try testExecError(error.InvalidBatchScriptArg, gpa, &.{"\n"}); - try testExecError(error.InvalidBatchScriptArg, gpa, &.{"\r"}); - try testExec(gpa, &.{ "a", "b" }, null); - try testExec(gpa, &.{ "c is for cat", "d is for dog" }, null); - try testExec(gpa, &.{ "\"", " \"" }, null); - try testExec(gpa, &.{ "\\", "\\" }, null); - try testExec(gpa, &.{">file.txt"}, null); - try testExec(gpa, &.{"whoami.exe"}, null); - try testExec(gpa, &.{"&a.exe"}, null); - try testExec(gpa, &.{"&echo hello "}, null); - try testExec(gpa, &.{ "&echo hello", "&whoami", ">file.txt" }, null); - try testExec(gpa, &.{"!TMP!"}, null); - try testExec(gpa, &.{"key=value"}, null); - try testExec(gpa, &.{"\"key=value\""}, null); - try testExec(gpa, &.{"key = value"}, null); - try testExec(gpa, &.{"key=[\"value\"]"}, null); - try testExec(gpa, &.{ "", "a=b" }, null); - try testExec(gpa, &.{"key=\"foo bar\""}, null); - try testExec(gpa, &.{"key=[\"my_value]"}, null); - try testExec(gpa, &.{"key=[\"my_value\",\"other-value\"]"}, null); - try testExec(gpa, &.{"key\\=value"}, null); - try testExec(gpa, &.{"key=\"&whoami\""}, null); - try testExec(gpa, &.{"key=\"value\"=5"}, null); - try testExec(gpa, &.{"key=[\">file.txt\"]"}, null); - try testExec(gpa, &.{"%hello"}, null); - try testExec(gpa, &.{"%PATH%"}, null); - try testExec(gpa, &.{"%%cd:~,%"}, null); - try testExec(gpa, &.{"%PATH%PATH%"}, null); - try testExec(gpa, &.{"\">file.txt"}, null); - try testExec(gpa, &.{"abc\"&echo hello"}, null); - try testExec(gpa, &.{"123\">file.txt"}, null); - try testExec(gpa, &.{"\"&echo hello&whoami.exe"}, null); - try testExec(gpa, &.{ "\"hello^\"world\"", "hello &echo oh no >file.txt" }, null); - try testExec(gpa, &.{"&whoami.exe"}, null); + try testExecError(error.InvalidBatchScriptArg, gpa, io, &.{"\x00"}); + try testExecError(error.InvalidBatchScriptArg, gpa, io, &.{"\n"}); + try testExecError(error.InvalidBatchScriptArg, gpa, io, &.{"\r"}); + try testExec(gpa, io, &.{ "a", "b" }, null); + try testExec(gpa, io, &.{ "c is for cat", "d is for dog" }, null); + try testExec(gpa, io, &.{ "\"", " \"" }, null); + try testExec(gpa, io, &.{ "\\", "\\" }, null); + try testExec(gpa, io, &.{">file.txt"}, null); + try testExec(gpa, io, &.{"whoami.exe"}, null); + try testExec(gpa, io, &.{"&a.exe"}, null); + try testExec(gpa, io, &.{"&echo hello "}, null); + try testExec(gpa, io, &.{ "&echo hello", "&whoami", ">file.txt" }, null); + try testExec(gpa, io, &.{"!TMP!"}, null); + try testExec(gpa, io, &.{"key=value"}, null); + try testExec(gpa, io, &.{"\"key=value\""}, null); + try testExec(gpa, io, &.{"key = value"}, null); + try testExec(gpa, io, &.{"key=[\"value\"]"}, null); + try testExec(gpa, io, &.{ "", "a=b" }, null); + try testExec(gpa, io, &.{"key=\"foo bar\""}, null); + try testExec(gpa, io, &.{"key=[\"my_value]"}, null); + try testExec(gpa, io, &.{"key=[\"my_value\",\"other-value\"]"}, null); + try testExec(gpa, io, &.{"key\\=value"}, null); + try testExec(gpa, io, &.{"key=\"&whoami\""}, null); + try testExec(gpa, io, &.{"key=\"value\"=5"}, null); + try testExec(gpa, io, &.{"key=[\">file.txt\"]"}, null); + try testExec(gpa, io, &.{"%hello"}, null); + try testExec(gpa, io, &.{"%PATH%"}, null); + try testExec(gpa, io, &.{"%%cd:~,%"}, null); + try testExec(gpa, io, &.{"%PATH%PATH%"}, null); + try testExec(gpa, io, &.{"\">file.txt"}, null); + try testExec(gpa, io, &.{"abc\"&echo hello"}, null); + try testExec(gpa, io, &.{"123\">file.txt"}, null); + try testExec(gpa, io, &.{"\"&echo hello&whoami.exe"}, null); + try testExec(gpa, io, &.{ "\"hello^\"world\"", "hello &echo oh no >file.txt" }, null); + try testExec(gpa, io, &.{"&whoami.exe"}, null); // Ensure that trailing space and . characters can't lead to unexpected bat/cmd script execution. // In many Windows APIs (including CreateProcess), trailing space and . characters are stripped @@ -94,14 +99,14 @@ pub fn main() anyerror!void { // > "args1.bat .. " // '"args1.bat .. "' is not recognized as an internal or external command, // operable program or batch file. - try std.testing.expectError(error.FileNotFound, testExecBat(gpa, "args1.bat .. ", &.{"abc"}, null)); + try std.testing.expectError(error.FileNotFound, testExecBat(gpa, io, "args1.bat .. ", &.{"abc"}, null)); const absolute_with_trailing = blk: { - const absolute_path = try std.fs.realpathAlloc(gpa, "args1.bat"); + const absolute_path = try Io.Dir.cwd().realPathFileAlloc(io, "args1.bat", gpa); defer gpa.free(absolute_path); break :blk try std.mem.concat(gpa, u8, &.{ absolute_path, " .. " }); }; defer gpa.free(absolute_with_trailing); - try std.testing.expectError(error.FileNotFound, testExecBat(gpa, absolute_with_trailing, &.{"abc"}, null)); + try std.testing.expectError(error.FileNotFound, testExecBat(gpa, io, absolute_with_trailing, &.{"abc"}, null)); var env = env: { var env = try std.process.getEnvMap(gpa); @@ -115,23 +120,23 @@ pub fn main() anyerror!void { break :env env; }; defer env.deinit(); - try testExec(gpa, &.{"%FOO%"}, &env); + try testExec(gpa, io, &.{"%FOO%"}, &env); // Ensure that none of the `>file.txt`s have caused file.txt to be created - try std.testing.expectError(error.FileNotFound, tmp.dir.access("file.txt", .{})); + try std.testing.expectError(error.FileNotFound, tmp.dir.access(io, "file.txt", .{})); } -fn testExecError(err: anyerror, gpa: std.mem.Allocator, args: []const []const u8) !void { - return std.testing.expectError(err, testExec(gpa, args, null)); +fn testExecError(err: anyerror, gpa: Allocator, io: Io, args: []const []const u8) !void { + return std.testing.expectError(err, testExec(gpa, io, args, null)); } -fn testExec(gpa: std.mem.Allocator, args: []const []const u8, env: ?*std.process.EnvMap) !void { - try testExecBat(gpa, "args1.bat", args, env); - try testExecBat(gpa, "args2.bat", args, env); - try testExecBat(gpa, "args3.bat", args, env); +fn testExec(gpa: Allocator, io: Io, args: []const []const u8, env: ?*std.process.EnvMap) !void { + try testExecBat(gpa, io, "args1.bat", args, env); + try testExecBat(gpa, io, "args2.bat", args, env); + try testExecBat(gpa, io, "args3.bat", args, env); } -fn testExecBat(gpa: std.mem.Allocator, bat: []const u8, args: []const []const u8, env: ?*std.process.EnvMap) !void { +fn testExecBat(gpa: Allocator, io: Io, bat: []const u8, args: []const []const u8, env: ?*std.process.EnvMap) !void { const argv = try gpa.alloc([]const u8, 1 + args.len); defer gpa.free(argv); argv[0] = bat; @@ -139,8 +144,7 @@ fn testExecBat(gpa: std.mem.Allocator, bat: []const u8, args: []const []const u8 const can_have_trailing_empty_args = std.mem.eql(u8, bat, "args3.bat"); - const result = try std.process.Child.run(.{ - .allocator = gpa, + const result = try std.process.Child.run(gpa, io, .{ .env_map = env, .argv = argv, }); @@ -160,3 +164,41 @@ fn testExecBat(gpa: std.mem.Allocator, bat: []const u8, args: []const []const u8 i += 1; } } + +pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir { + var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; + std.crypto.random.bytes(&random_bytes); + var sub_path: [TmpDir.sub_path_len]u8 = undefined; + _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); + + const cwd = Io.Dir.cwd(); + var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch + @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir"); + defer cache_dir.close(io); + const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch + @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir"); + const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch + @panic("unable to make tmp dir for testing: unable to make and open the tmp dir"); + + return .{ + .dir = dir, + .parent_dir = parent_dir, + .sub_path = sub_path, + }; +} + +pub const TmpDir = struct { + dir: Io.Dir, + parent_dir: Io.Dir, + sub_path: [sub_path_len]u8, + + const random_bytes_count = 12; + const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); + + pub fn cleanup(self: *TmpDir, io: Io) void { + self.dir.close(io); + self.parent_dir.deleteTree(io, &self.sub_path) catch {}; + self.parent_dir.close(io); + self.* = undefined; + } +}; diff --git a/test/standalone/windows_paths/relative.zig b/test/standalone/windows_paths/relative.zig index 8301549667..7b6a51b283 100644 --- a/test/standalone/windows_paths/relative.zig +++ b/test/standalone/windows_paths/relative.zig @@ -10,10 +10,14 @@ pub fn main() !void { if (args.len < 3) return error.MissingArgs; + var threaded: std.Io.Threaded = .init(allocator, .{}); + defer threaded.deinit(); + const io = threaded.io(); + const relative = try std.fs.path.relative(allocator, args[1], args[2]); defer allocator.free(relative); - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); const stdout = &stdout_writer.interface; try stdout.writeAll(relative); } diff --git a/test/standalone/windows_paths/test.zig b/test/standalone/windows_paths/test.zig index 2ec23417e6..ed4069dc61 100644 --- a/test/standalone/windows_paths/test.zig +++ b/test/standalone/windows_paths/test.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Io = std.Io; pub fn main() anyerror!void { var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); @@ -9,6 +10,8 @@ pub fn main() anyerror!void { if (args.len < 2) return error.MissingArgs; + const io = std.Io.Threaded.global_single_threaded.ioBasic(); + const exe_path = args[1]; const cwd_path = try std.process.getCwdAlloc(arena); @@ -33,39 +36,39 @@ pub fn main() anyerror!void { // With the special =X: environment variable set, drive-relative paths that // don't match the CWD's drive letter are resolved against that env var. - try checkRelative(arena, "..\\..\\bar", &.{ exe_path, drive_rel, drive_abs }, null, &alt_drive_env_map); - try checkRelative(arena, "..\\baz\\foo", &.{ exe_path, drive_abs, drive_rel }, null, &alt_drive_env_map); + try checkRelative(arena, io, "..\\..\\bar", &.{ exe_path, drive_rel, drive_abs }, null, &alt_drive_env_map); + try checkRelative(arena, io, "..\\baz\\foo", &.{ exe_path, drive_abs, drive_rel }, null, &alt_drive_env_map); // Without that environment variable set, drive-relative paths that don't match the // CWD's drive letter are resolved against the root of the drive. - try checkRelative(arena, "..\\bar", &.{ exe_path, drive_rel, drive_abs }, null, &empty_env); - try checkRelative(arena, "..\\foo", &.{ exe_path, drive_abs, drive_rel }, null, &empty_env); + try checkRelative(arena, io, "..\\bar", &.{ exe_path, drive_rel, drive_abs }, null, &empty_env); + try checkRelative(arena, io, "..\\foo", &.{ exe_path, drive_abs, drive_rel }, null, &empty_env); // Bare drive-relative path with no components - try checkRelative(arena, "bar", &.{ exe_path, drive_rel[0..2], drive_abs }, null, &empty_env); - try checkRelative(arena, "..", &.{ exe_path, drive_abs, drive_rel[0..2] }, null, &empty_env); + try checkRelative(arena, io, "bar", &.{ exe_path, drive_rel[0..2], drive_abs }, null, &empty_env); + try checkRelative(arena, io, "..", &.{ exe_path, drive_abs, drive_rel[0..2] }, null, &empty_env); // Bare drive-relative path with no components, drive-CWD set - try checkRelative(arena, "..\\bar", &.{ exe_path, drive_rel[0..2], drive_abs }, null, &alt_drive_env_map); - try checkRelative(arena, "..\\baz", &.{ exe_path, drive_abs, drive_rel[0..2] }, null, &alt_drive_env_map); + try checkRelative(arena, io, "..\\bar", &.{ exe_path, drive_rel[0..2], drive_abs }, null, &alt_drive_env_map); + try checkRelative(arena, io, "..\\baz", &.{ exe_path, drive_abs, drive_rel[0..2] }, null, &alt_drive_env_map); // Bare drive-relative path relative to the CWD should be equivalent if drive-CWD is set - try checkRelative(arena, "", &.{ exe_path, alt_drive_cwd, drive_rel[0..2] }, null, &alt_drive_env_map); - try checkRelative(arena, "", &.{ exe_path, drive_rel[0..2], alt_drive_cwd }, null, &alt_drive_env_map); + try checkRelative(arena, io, "", &.{ exe_path, alt_drive_cwd, drive_rel[0..2] }, null, &alt_drive_env_map); + try checkRelative(arena, io, "", &.{ exe_path, drive_rel[0..2], alt_drive_cwd }, null, &alt_drive_env_map); // Bare drive-relative should always be equivalent to itself - try checkRelative(arena, "", &.{ exe_path, drive_rel[0..2], drive_rel[0..2] }, null, &alt_drive_env_map); - try checkRelative(arena, "", &.{ exe_path, drive_rel[0..2], drive_rel[0..2] }, null, &alt_drive_env_map); - try checkRelative(arena, "", &.{ exe_path, drive_rel[0..2], drive_rel[0..2] }, null, &empty_env); - try checkRelative(arena, "", &.{ exe_path, drive_rel[0..2], drive_rel[0..2] }, null, &empty_env); + try checkRelative(arena, io, "", &.{ exe_path, drive_rel[0..2], drive_rel[0..2] }, null, &alt_drive_env_map); + try checkRelative(arena, io, "", &.{ exe_path, drive_rel[0..2], drive_rel[0..2] }, null, &alt_drive_env_map); + try checkRelative(arena, io, "", &.{ exe_path, drive_rel[0..2], drive_rel[0..2] }, null, &empty_env); + try checkRelative(arena, io, "", &.{ exe_path, drive_rel[0..2], drive_rel[0..2] }, null, &empty_env); } if (parsed_cwd_path.kind == .unc_absolute) { const drive_abs_path = try std.fmt.allocPrint(arena, "{c}:\\foo\\bar", .{alt_drive_letter}); { - try checkRelative(arena, drive_abs_path, &.{ exe_path, cwd_path, drive_abs_path }, null, &empty_env); - try checkRelative(arena, cwd_path, &.{ exe_path, drive_abs_path, cwd_path }, null, &empty_env); + try checkRelative(arena, io, drive_abs_path, &.{ exe_path, cwd_path, drive_abs_path }, null, &empty_env); + try checkRelative(arena, io, cwd_path, &.{ exe_path, drive_abs_path, cwd_path }, null, &empty_env); } } else if (parsed_cwd_path.kind == .drive_absolute) { const cur_drive_letter = parsed_cwd_path.root[0]; @@ -73,14 +76,14 @@ pub fn main() anyerror!void { const unc_cwd = try std.fmt.allocPrint(arena, "\\\\127.0.0.1\\{c}$\\{s}", .{ cur_drive_letter, path_beyond_root }); { - try checkRelative(arena, cwd_path, &.{ exe_path, unc_cwd, cwd_path }, null, &empty_env); - try checkRelative(arena, unc_cwd, &.{ exe_path, cwd_path, unc_cwd }, null, &empty_env); + try checkRelative(arena, io, cwd_path, &.{ exe_path, unc_cwd, cwd_path }, null, &empty_env); + try checkRelative(arena, io, unc_cwd, &.{ exe_path, cwd_path, unc_cwd }, null, &empty_env); } { const drive_abs = cwd_path; const drive_rel = parsed_cwd_path.root[0..2]; - try checkRelative(arena, "", &.{ exe_path, drive_abs, drive_rel }, null, &empty_env); - try checkRelative(arena, "", &.{ exe_path, drive_rel, drive_abs }, null, &empty_env); + try checkRelative(arena, io, "", &.{ exe_path, drive_abs, drive_rel }, null, &empty_env); + try checkRelative(arena, io, "", &.{ exe_path, drive_rel, drive_abs }, null, &empty_env); } } else { return error.UnexpectedPathType; @@ -89,13 +92,13 @@ pub fn main() anyerror!void { fn checkRelative( allocator: std.mem.Allocator, + io: Io, expected_stdout: []const u8, argv: []const []const u8, cwd: ?[]const u8, env_map: ?*const std.process.EnvMap, ) !void { - const result = try std.process.Child.run(.{ - .allocator = allocator, + const result = try std.process.Child.run(allocator, io, .{ .argv = argv, .cwd = cwd, .env_map = env_map, diff --git a/test/standalone/windows_spawn/hello.zig b/test/standalone/windows_spawn/hello.zig index fb4a827e23..51e6aaf8bc 100644 --- a/test/standalone/windows_spawn/hello.zig +++ b/test/standalone/windows_spawn/hello.zig @@ -1,7 +1,8 @@ const std = @import("std"); pub fn main() !void { - var stdout_writer = std.fs.File.stdout().writerStreaming(&.{}); + const io = std.Options.debug_io; + var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{}); const stdout = &stdout_writer.interface; try stdout.writeAll("hello from exe\n"); } diff --git a/test/standalone/windows_spawn/main.zig b/test/standalone/windows_spawn/main.zig index 10ee35f4df..c9522bf4de 100644 --- a/test/standalone/windows_spawn/main.zig +++ b/test/standalone/windows_spawn/main.zig @@ -1,29 +1,35 @@ const std = @import("std"); +const Io = std.Io; +const Allocator = std.mem.Allocator; const windows = std.os.windows; const utf16Literal = std.unicode.utf8ToUtf16LeStringLiteral; pub fn main() anyerror!void { - var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; - defer if (gpa.deinit() == .leak) @panic("found memory leaks"); - const allocator = gpa.allocator(); + var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + defer if (debug_allocator.deinit() == .leak) @panic("found memory leaks"); + const gpa = debug_allocator.allocator(); - var it = try std.process.argsWithAllocator(allocator); + var threaded: std.Io.Threaded = .init(gpa, .{}); + defer threaded.deinit(); + const io = threaded.io(); + + var it = try std.process.argsWithAllocator(gpa); defer it.deinit(); _ = it.next() orelse unreachable; // skip binary name const hello_exe_cache_path = it.next() orelse unreachable; - var tmp = std.testing.tmpDir(.{}); - defer tmp.cleanup(); + var tmp = tmpDir(io, .{}); + defer tmp.cleanup(io); - const tmp_absolute_path = try tmp.dir.realpathAlloc(allocator, "."); - defer allocator.free(tmp_absolute_path); - const tmp_absolute_path_w = try std.unicode.utf8ToUtf16LeAllocZ(allocator, tmp_absolute_path); - defer allocator.free(tmp_absolute_path_w); - const cwd_absolute_path = try std.fs.cwd().realpathAlloc(allocator, "."); - defer allocator.free(cwd_absolute_path); - const tmp_relative_path = try std.fs.path.relative(allocator, cwd_absolute_path, tmp_absolute_path); - defer allocator.free(tmp_relative_path); + const tmp_absolute_path = try tmp.dir.realPathFileAlloc(io, ".", gpa); + defer gpa.free(tmp_absolute_path); + const tmp_absolute_path_w = try std.unicode.utf8ToUtf16LeAllocZ(gpa, tmp_absolute_path); + defer gpa.free(tmp_absolute_path_w); + const cwd_absolute_path = try Io.Dir.cwd().realPathFileAlloc(io, ".", gpa); + defer gpa.free(cwd_absolute_path); + const tmp_relative_path = try std.fs.path.relative(gpa, cwd_absolute_path, tmp_absolute_path); + defer gpa.free(tmp_relative_path); // Clear PATH std.debug.assert(windows.kernel32.SetEnvironmentVariableW( @@ -38,10 +44,10 @@ pub fn main() anyerror!void { ) == windows.TRUE); // No PATH, so it should fail to find anything not in the cwd - try testExecError(error.FileNotFound, allocator, "something_missing"); + try testExecError(error.FileNotFound, gpa, io, "something_missing"); // make sure we don't get error.BadPath traversing out of cwd with a relative path - try testExecError(error.FileNotFound, allocator, "..\\.\\.\\.\\\\..\\more_missing"); + try testExecError(error.FileNotFound, gpa, io, "..\\.\\.\\.\\\\..\\more_missing"); std.debug.assert(windows.kernel32.SetEnvironmentVariableW( utf16Literal("PATH"), @@ -49,82 +55,82 @@ pub fn main() anyerror!void { ) == windows.TRUE); // Move hello.exe into the tmp dir which is now added to the path - try std.fs.cwd().copyFile(hello_exe_cache_path, tmp.dir, "hello.exe", .{}); + try Io.Dir.cwd().copyFile(hello_exe_cache_path, tmp.dir, "hello.exe", io, .{}); // with extension should find the .exe (case insensitive) - try testExec(allocator, "HeLLo.exe", "hello from exe\n"); + try testExec(gpa, io, "HeLLo.exe", "hello from exe\n"); // without extension should find the .exe (case insensitive) - try testExec(allocator, "heLLo", "hello from exe\n"); + try testExec(gpa, io, "heLLo", "hello from exe\n"); // with invalid cwd - try std.testing.expectError(error.FileNotFound, testExecWithCwd(allocator, "hello.exe", "missing_dir", "")); + try std.testing.expectError(error.FileNotFound, testExecWithCwd(gpa, io, "hello.exe", "missing_dir", "")); // now add a .bat - try tmp.dir.writeFile(.{ .sub_path = "hello.bat", .data = "@echo hello from bat" }); + try tmp.dir.writeFile(io, .{ .sub_path = "hello.bat", .data = "@echo hello from bat" }); // and a .cmd - try tmp.dir.writeFile(.{ .sub_path = "hello.cmd", .data = "@echo hello from cmd" }); + try tmp.dir.writeFile(io, .{ .sub_path = "hello.cmd", .data = "@echo hello from cmd" }); // with extension should find the .bat (case insensitive) - try testExec(allocator, "heLLo.bat", "hello from bat\r\n"); + try testExec(gpa, io, "heLLo.bat", "hello from bat\r\n"); // with extension should find the .cmd (case insensitive) - try testExec(allocator, "heLLo.cmd", "hello from cmd\r\n"); + try testExec(gpa, io, "heLLo.cmd", "hello from cmd\r\n"); // without extension should find the .exe (since its first in PATHEXT) - try testExec(allocator, "heLLo", "hello from exe\n"); + try testExec(gpa, io, "heLLo", "hello from exe\n"); // now rename the exe to not have an extension - try renameExe(tmp.dir, "hello.exe", "hello"); + try renameExe(tmp.dir, io, "hello.exe", "hello"); // with extension should now fail - try testExecError(error.FileNotFound, allocator, "hello.exe"); + try testExecError(error.FileNotFound, gpa, io, "hello.exe"); // without extension should succeed (case insensitive) - try testExec(allocator, "heLLo", "hello from exe\n"); + try testExec(gpa, io, "heLLo", "hello from exe\n"); - try tmp.dir.makeDir("something"); - try renameExe(tmp.dir, "hello", "something/hello.exe"); + try tmp.dir.createDir(io, "something", .default_dir); + try renameExe(tmp.dir, io, "hello", "something/hello.exe"); - const relative_path_no_ext = try std.fs.path.join(allocator, &.{ tmp_relative_path, "something/hello" }); - defer allocator.free(relative_path_no_ext); + const relative_path_no_ext = try std.fs.path.join(gpa, &.{ tmp_relative_path, "something/hello" }); + defer gpa.free(relative_path_no_ext); // Giving a full relative path to something/hello should work - try testExec(allocator, relative_path_no_ext, "hello from exe\n"); + try testExec(gpa, io, relative_path_no_ext, "hello from exe\n"); // But commands with path separators get excluded from PATH searching, so this will fail - try testExecError(error.FileNotFound, allocator, "something/hello"); + try testExecError(error.FileNotFound, gpa, io, "something/hello"); // Now that .BAT is the first PATHEXT that should be found, this should succeed - try testExec(allocator, "heLLo", "hello from bat\r\n"); + try testExec(gpa, io, "heLLo", "hello from bat\r\n"); // Add a hello.exe that is not a valid executable - try tmp.dir.writeFile(.{ .sub_path = "hello.exe", .data = "invalid" }); + try tmp.dir.writeFile(io, .{ .sub_path = "hello.exe", .data = "invalid" }); // Trying to execute it with extension will give InvalidExe. This is a special // case for .EXE extensions, where if they ever try to get executed but they are // invalid, that gets treated as a fatal error wherever they are found and InvalidExe // is returned immediately. - try testExecError(error.InvalidExe, allocator, "hello.exe"); + try testExecError(error.InvalidExe, gpa, io, "hello.exe"); // Same thing applies to the command with no extension--even though there is a // hello.bat that could be executed, it should stop after it tries executing // hello.exe and getting InvalidExe. - try testExecError(error.InvalidExe, allocator, "hello"); + try testExecError(error.InvalidExe, gpa, io, "hello"); // If we now rename hello.exe to have no extension, it will behave differently - try renameExe(tmp.dir, "hello.exe", "hello"); + try renameExe(tmp.dir, io, "hello.exe", "hello"); // Now, trying to execute it without an extension should treat InvalidExe as recoverable // and skip over it and find hello.bat and execute that - try testExec(allocator, "hello", "hello from bat\r\n"); + try testExec(gpa, io, "hello", "hello from bat\r\n"); // If we rename the invalid exe to something else - try renameExe(tmp.dir, "hello", "goodbye"); + try renameExe(tmp.dir, io, "hello", "goodbye"); // Then we should now get FileNotFound when trying to execute 'goodbye', // since that is what the original error will be after searching for 'goodbye' // in the cwd. It will try to execute 'goodbye' from the PATH but the InvalidExe error // should be ignored in this case. - try testExecError(error.FileNotFound, allocator, "goodbye"); + try testExecError(error.FileNotFound, gpa, io, "goodbye"); // Now let's set the tmp dir as the cwd and set the path only include the "something" sub dir - try tmp.dir.setAsCwd(); - defer tmp.parent_dir.setAsCwd() catch {}; - const something_subdir_abs_path = try std.mem.concatWithSentinel(allocator, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0); - defer allocator.free(something_subdir_abs_path); + try std.process.setCurrentDir(io, tmp.dir); + defer std.process.setCurrentDir(io, tmp.parent_dir) catch {}; + const something_subdir_abs_path = try std.mem.concatWithSentinel(gpa, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0); + defer gpa.free(something_subdir_abs_path); std.debug.assert(windows.kernel32.SetEnvironmentVariableW( utf16Literal("PATH"), @@ -133,37 +139,37 @@ pub fn main() anyerror!void { // Now trying to execute goodbye should give error.InvalidExe since it's the original // error that we got when trying within the cwd - try testExecError(error.InvalidExe, allocator, "goodbye"); + try testExecError(error.InvalidExe, gpa, io, "goodbye"); // hello should still find the .bat - try testExec(allocator, "hello", "hello from bat\r\n"); + try testExec(gpa, io, "hello", "hello from bat\r\n"); // If we rename something/hello.exe to something/goodbye.exe - try renameExe(tmp.dir, "something/hello.exe", "something/goodbye.exe"); + try renameExe(tmp.dir, io, "something/hello.exe", "something/goodbye.exe"); // And try to execute goodbye, then the one in something should be found // since the one in cwd is an invalid executable - try testExec(allocator, "goodbye", "hello from exe\n"); + try testExec(gpa, io, "goodbye", "hello from exe\n"); // If we use an absolute path to execute the invalid goodbye - const goodbye_abs_path = try std.mem.join(allocator, "\\", &.{ tmp_absolute_path, "goodbye" }); - defer allocator.free(goodbye_abs_path); + const goodbye_abs_path = try std.mem.join(gpa, "\\", &.{ tmp_absolute_path, "goodbye" }); + defer gpa.free(goodbye_abs_path); // then the PATH should not be searched and we should get InvalidExe - try testExecError(error.InvalidExe, allocator, goodbye_abs_path); + try testExecError(error.InvalidExe, gpa, io, goodbye_abs_path); // If we try to exec but provide a cwd that is an absolute path, the PATH // should still be searched and the goodbye.exe in something should be found. - try testExecWithCwd(allocator, "goodbye", tmp_absolute_path, "hello from exe\n"); + try testExecWithCwd(gpa, io, "goodbye", tmp_absolute_path, "hello from exe\n"); // introduce some extra path separators into the path which is dealt with inside the spawn call. const denormed_something_subdir_size = std.mem.replacementSize(u16, something_subdir_abs_path, utf16Literal("\\"), utf16Literal("\\\\\\\\")); - const denormed_something_subdir_abs_path = try allocator.allocSentinel(u16, denormed_something_subdir_size, 0); - defer allocator.free(denormed_something_subdir_abs_path); + const denormed_something_subdir_abs_path = try gpa.allocSentinel(u16, denormed_something_subdir_size, 0); + defer gpa.free(denormed_something_subdir_abs_path); _ = std.mem.replace(u16, something_subdir_abs_path, utf16Literal("\\"), utf16Literal("\\\\\\\\"), denormed_something_subdir_abs_path); - const denormed_something_subdir_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, denormed_something_subdir_abs_path); - defer allocator.free(denormed_something_subdir_wtf8); + const denormed_something_subdir_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(gpa, denormed_something_subdir_abs_path); + defer gpa.free(denormed_something_subdir_wtf8); // clear the path to ensure that the match comes from the cwd std.debug.assert(windows.kernel32.SetEnvironmentVariableW( @@ -171,21 +177,21 @@ pub fn main() anyerror!void { null, ) == windows.TRUE); - try testExecWithCwd(allocator, "goodbye", denormed_something_subdir_wtf8, "hello from exe\n"); + try testExecWithCwd(gpa, io, "goodbye", denormed_something_subdir_wtf8, "hello from exe\n"); // normalization should also work if the non-normalized path is found in the PATH var. std.debug.assert(windows.kernel32.SetEnvironmentVariableW( utf16Literal("PATH"), denormed_something_subdir_abs_path, ) == windows.TRUE); - try testExec(allocator, "goodbye", "hello from exe\n"); + try testExec(gpa, io, "goodbye", "hello from exe\n"); // now make sure we can launch executables "outside" of the cwd - var subdir_cwd = try tmp.dir.openDir(denormed_something_subdir_wtf8, .{}); - defer subdir_cwd.close(); + var subdir_cwd = try tmp.dir.openDir(io, denormed_something_subdir_wtf8, .{}); + defer subdir_cwd.close(io); - try renameExe(tmp.dir, "something/goodbye.exe", "hello.exe"); - try subdir_cwd.setAsCwd(); + try renameExe(tmp.dir, io, "something/goodbye.exe", "hello.exe"); + try std.process.setCurrentDir(io, subdir_cwd); // clear the PATH again std.debug.assert(windows.kernel32.SetEnvironmentVariableW( @@ -194,33 +200,32 @@ pub fn main() anyerror!void { ) == windows.TRUE); // while we're at it make sure non-windows separators work fine - try testExec(allocator, "../hello", "hello from exe\n"); + try testExec(gpa, io, "../hello", "hello from exe\n"); } -fn testExecError(err: anyerror, allocator: std.mem.Allocator, command: []const u8) !void { - return std.testing.expectError(err, testExec(allocator, command, "")); +fn testExecError(err: anyerror, gpa: Allocator, io: Io, command: []const u8) !void { + return std.testing.expectError(err, testExec(gpa, io, command, "")); } -fn testExec(allocator: std.mem.Allocator, command: []const u8, expected_stdout: []const u8) !void { - return testExecWithCwd(allocator, command, null, expected_stdout); +fn testExec(gpa: Allocator, io: Io, command: []const u8, expected_stdout: []const u8) !void { + return testExecWithCwd(gpa, io, command, null, expected_stdout); } -fn testExecWithCwd(allocator: std.mem.Allocator, command: []const u8, cwd: ?[]const u8, expected_stdout: []const u8) !void { - const result = try std.process.Child.run(.{ - .allocator = allocator, +fn testExecWithCwd(gpa: Allocator, io: Io, command: []const u8, cwd: ?[]const u8, expected_stdout: []const u8) !void { + const result = try std.process.Child.run(gpa, io, .{ .argv = &[_][]const u8{command}, .cwd = cwd, }); - defer allocator.free(result.stdout); - defer allocator.free(result.stderr); + defer gpa.free(result.stdout); + defer gpa.free(result.stderr); try std.testing.expectEqualStrings("", result.stderr); try std.testing.expectEqualStrings(expected_stdout, result.stdout); } -fn renameExe(dir: std.fs.Dir, old_sub_path: []const u8, new_sub_path: []const u8) !void { +fn renameExe(dir: Io.Dir, io: Io, old_sub_path: []const u8, new_sub_path: []const u8) !void { var attempt: u5 = 0; - while (true) break dir.rename(old_sub_path, new_sub_path) catch |err| switch (err) { + while (true) break dir.rename(old_sub_path, dir, new_sub_path, io) catch |err| switch (err) { error.AccessDenied => { if (attempt == 13) return error.AccessDenied; // give the kernel a chance to finish closing the executable handle @@ -231,3 +236,41 @@ fn renameExe(dir: std.fs.Dir, old_sub_path: []const u8, new_sub_path: []const u8 else => |e| return e, }; } + +pub fn tmpDir(io: Io, opts: Io.Dir.OpenOptions) TmpDir { + var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; + std.crypto.random.bytes(&random_bytes); + var sub_path: [TmpDir.sub_path_len]u8 = undefined; + _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); + + const cwd = Io.Dir.cwd(); + var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch + @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir"); + defer cache_dir.close(io); + const parent_dir = cache_dir.createDirPathOpen(io, "tmp", .{}) catch + @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir"); + const dir = parent_dir.createDirPathOpen(io, &sub_path, .{ .open_options = opts }) catch + @panic("unable to make tmp dir for testing: unable to make and open the tmp dir"); + + return .{ + .dir = dir, + .parent_dir = parent_dir, + .sub_path = sub_path, + }; +} + +pub const TmpDir = struct { + dir: Io.Dir, + parent_dir: Io.Dir, + sub_path: [sub_path_len]u8, + + const random_bytes_count = 12; + const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); + + pub fn cleanup(self: *TmpDir, io: Io) void { + self.dir.close(io); + self.parent_dir.deleteTree(io, &self.sub_path) catch {}; + self.parent_dir.close(io); + self.* = undefined; + } +}; diff --git a/test/tests.zig b/test/tests.zig index 0911ae2e46..aa3c018a62 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -187,29 +187,30 @@ const test_targets = blk: { .link_libc = true, }, - .{ - .target = .{ - .cpu_arch = .aarch64, - .os_tag = .linux, - .abi = .none, - }, - .use_llvm = false, - .use_lld = false, - .optimize_mode = .ReleaseFast, - .strip = true, - }, - .{ - .target = .{ - .cpu_arch = .aarch64, - .cpu_model = .{ .explicit = &std.Target.aarch64.cpu.neoverse_n1 }, - .os_tag = .linux, - .abi = .none, - }, - .use_llvm = false, - .use_lld = false, - .optimize_mode = .ReleaseFast, - .strip = true, - }, + // Disabled due to https://codeberg.org/ziglang/zig/pulls/30232#issuecomment-9203351 + //.{ + // .target = .{ + // .cpu_arch = .aarch64, + // .os_tag = .linux, + // .abi = .none, + // }, + // .use_llvm = false, + // .use_lld = false, + // .optimize_mode = .ReleaseFast, + // .strip = true, + //}, + //.{ + // .target = .{ + // .cpu_arch = .aarch64, + // .cpu_model = .{ .explicit = &std.Target.aarch64.cpu.neoverse_n1 }, + // .os_tag = .linux, + // .abi = .none, + // }, + // .use_llvm = false, + // .use_lld = false, + // .optimize_mode = .ReleaseFast, + // .strip = true, + //}, .{ .target = .{ @@ -1204,17 +1205,18 @@ const test_targets = blk: { }, }, - .{ - .target = .{ - .cpu_arch = .aarch64, - .os_tag = .macos, - .abi = .none, - }, - .use_llvm = false, - .use_lld = false, - .optimize_mode = .ReleaseFast, - .strip = true, - }, + // Disabled due to https://codeberg.org/ziglang/zig/pulls/30232#issuecomment-9203351 + //.{ + // .target = .{ + // .cpu_arch = .aarch64, + // .os_tag = .macos, + // .abi = .none, + // }, + // .use_llvm = false, + // .use_lld = false, + // .optimize_mode = .ReleaseFast, + // .strip = true, + //}, .{ .target = .{ @@ -2024,6 +2026,7 @@ pub fn addLinkTests( pub fn addCliTests(b: *std.Build) *Step { const step = b.step("test-cli", "Test the command line interface"); const s = std.fs.path.sep_str; + const io = b.graph.io; { // Test `zig init`. @@ -2132,14 +2135,14 @@ pub fn addCliTests(b: *std.Build) *Step { const tmp_path = b.makeTempPath(); const unformatted_code = " // no reason for indent"; - var dir = std.fs.cwd().openDir(tmp_path, .{}) catch @panic("unhandled"); - defer dir.close(); - dir.writeFile(.{ .sub_path = "fmt1.zig", .data = unformatted_code }) catch @panic("unhandled"); - dir.writeFile(.{ .sub_path = "fmt2.zig", .data = unformatted_code }) catch @panic("unhandled"); - dir.makeDir("subdir") catch @panic("unhandled"); - var subdir = dir.openDir("subdir", .{}) catch @panic("unhandled"); - defer subdir.close(); - subdir.writeFile(.{ .sub_path = "fmt3.zig", .data = unformatted_code }) catch @panic("unhandled"); + var dir = std.Io.Dir.cwd().openDir(io, tmp_path, .{}) catch @panic("unhandled"); + defer dir.close(io); + dir.writeFile(io, .{ .sub_path = "fmt1.zig", .data = unformatted_code }) catch @panic("unhandled"); + dir.writeFile(io, .{ .sub_path = "fmt2.zig", .data = unformatted_code }) catch @panic("unhandled"); + dir.createDir(io, "subdir", .default_dir) catch @panic("unhandled"); + var subdir = dir.openDir(io, "subdir", .{}) catch @panic("unhandled"); + defer subdir.close(io); + subdir.writeFile(io, .{ .sub_path = "fmt3.zig", .data = unformatted_code }) catch @panic("unhandled"); // Test zig fmt affecting only the appropriate files. const run1 = b.addSystemCommand(&.{ b.graph.zig_exe, "fmt", "fmt1.zig" }); @@ -2629,11 +2632,12 @@ pub fn addCases( ) !void { const arena = b.allocator; const gpa = b.allocator; + const io = b.graph.io; - var cases = @import("src/Cases.zig").init(gpa, arena); + var cases = @import("src/Cases.zig").init(gpa, arena, io); - var dir = try b.build_root.handle.openDir("test/cases", .{ .iterate = true }); - defer dir.close(); + var dir = try b.build_root.handle.openDir(io, "test/cases", .{ .iterate = true }); + defer dir.close(io); cases.addFromDir(dir, b); try @import("cases.zig").addCases(&cases, build_options, b); @@ -2678,7 +2682,9 @@ pub fn addDebuggerTests(b: *std.Build, options: DebuggerContext.Options) ?*Step return step; } -pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void { +pub fn addIncrementalTests(b: *std.Build, test_step: *Step, test_filters: []const []const u8) !void { + const io = b.graph.io; + const incr_check = b.addExecutable(.{ .name = "incr-check", .root_module = b.createModule(.{ @@ -2688,12 +2694,17 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void { }), }); - var dir = try b.build_root.handle.openDir("test/incremental", .{ .iterate = true }); - defer dir.close(); + var dir = try b.build_root.handle.openDir(io, "test/incremental", .{ .iterate = true }); + defer dir.close(io); var it = try dir.walk(b.graph.arena); - while (try it.next()) |entry| { + while (try it.next(io)) |entry| { if (entry.kind != .file) continue; + if (std.mem.endsWith(u8, entry.basename, ".swp")) continue; + + for (test_filters) |test_filter| { + if (std.mem.indexOf(u8, entry.path, test_filter)) |_| break; + } else if (test_filters.len > 0) continue; const run = b.addRunArtifact(incr_check); run.setName(b.fmt("incr-check '{s}'", .{entry.basename})); @@ -2702,6 +2713,11 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void { run.addFileArg(b.path("test/incremental/").path(b, entry.path)); run.addArgs(&.{ "--zig-lib-dir", b.fmt("{f}", .{b.graph.zig_lib_directory}) }); + if (b.enable_qemu) run.addArg("-fqemu"); + if (b.enable_wine) run.addArg("-fwine"); + if (b.enable_wasmtime) run.addArg("-fwasmtime"); + if (b.enable_darling) run.addArg("-fdarling"); + run.addCheck(.{ .expect_term = .{ .Exited = 0 } }); test_step.dependOn(&run.step); |
