Commit 4ceaa0595a
lib/std/fs/test.zig
@@ -0,0 +1,169 @@
+const std = @import("../std.zig");
+const builtin = std.builtin;
+const fs = std.fs;
+const File = std.fs.File;
+
+test "openSelfExe" {
+ const self_exe_file = try std.fs.openSelfExe();
+ self_exe_file.close();
+}
+
+const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.millisecond;
+
+test "open file with exclusive nonblocking lock twice" {
+ const dir = fs.cwd();
+ const filename = "file_nonblocking_lock_test.txt";
+
+ const file1 = try dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
+ defer file1.close();
+
+ const file2 = dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
+ std.debug.assert(std.meta.eql(file2, error.WouldBlock));
+
+ dir.deleteFile(filename) catch |err| switch (err) {
+ error.FileNotFound => {},
+ else => return err,
+ };
+}
+
+test "open file with lock twice, make sure it wasn't open at the same time" {
+ if (builtin.single_threaded) return;
+
+ const filename = "file_lock_test.txt";
+
+ var contexts = [_]FileLockTestContext{
+ .{ .filename = filename, .create = true, .lock = .Exclusive },
+ .{ .filename = filename, .create = true, .lock = .Exclusive },
+ };
+ try run_lock_file_test(&contexts);
+
+ // Check for an error
+ var was_error = false;
+ for (contexts) |context, idx| {
+ if (context.err) |err| {
+ was_error = true;
+ std.debug.warn("\nError in context {}: {}\n", .{ idx, err });
+ }
+ }
+ if (was_error) builtin.panic("There was an error in contexts", null);
+
+ std.debug.assert(!contexts[0].overlaps(&contexts[1]));
+
+ fs.cwd().deleteFile(filename) catch |err| switch (err) {
+ error.FileNotFound => {},
+ else => return err,
+ };
+}
+
+test "create file, lock and read from multiple process at once" {
+ if (builtin.single_threaded) return error.SkipZigTest;
+
+ if (true) {
+ // https://github.com/ziglang/zig/issues/5006
+ return error.SkipZigTest;
+ }
+
+ const filename = "file_read_lock_test.txt";
+ const filedata = "Hello, world!\n";
+
+ try fs.cwd().writeFile(filename, filedata);
+
+ var contexts = [_]FileLockTestContext{
+ .{ .filename = filename, .create = false, .lock = .Shared },
+ .{ .filename = filename, .create = false, .lock = .Shared },
+ .{ .filename = filename, .create = false, .lock = .Exclusive },
+ };
+
+ try run_lock_file_test(&contexts);
+
+ var was_error = false;
+ for (contexts) |context, idx| {
+ if (context.err) |err| {
+ was_error = true;
+ std.debug.warn("\nError in context {}: {}\n", .{ idx, err });
+ }
+ }
+ if (was_error) builtin.panic("There was an error in contexts", null);
+
+ std.debug.assert(contexts[0].overlaps(&contexts[1]));
+ std.debug.assert(!contexts[2].overlaps(&contexts[0]));
+ std.debug.assert(!contexts[2].overlaps(&contexts[1]));
+ if (contexts[0].bytes_read.? != filedata.len) {
+ std.debug.warn("\n bytes_read: {}, expected: {} \n", .{ contexts[0].bytes_read, filedata.len });
+ }
+ std.debug.assert(contexts[0].bytes_read.? == filedata.len);
+ std.debug.assert(contexts[1].bytes_read.? == filedata.len);
+
+ fs.cwd().deleteFile(filename) catch |err| switch (err) {
+ error.FileNotFound => {},
+ else => return err,
+ };
+}
+
+const FileLockTestContext = struct {
+ filename: []const u8,
+ pid: if (builtin.os.tag == .windows) ?void else ?std.os.pid_t = null,
+
+ // use file.createFile
+ create: bool,
+ // the type of lock to use
+ lock: File.Lock,
+
+ // Output variables
+ err: ?(File.OpenError || std.os.ReadError) = null,
+ start_time: u64 = 0,
+ end_time: u64 = 0,
+ bytes_read: ?usize = null,
+
+ fn overlaps(self: *const @This(), other: *const @This()) bool {
+ return (self.start_time < other.end_time) and (self.end_time > other.start_time);
+ }
+
+ fn run(ctx: *@This()) void {
+ var file: File = undefined;
+ if (ctx.create) {
+ file = fs.cwd().createFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| {
+ ctx.err = err;
+ return;
+ };
+ } else {
+ file = fs.cwd().openFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| {
+ ctx.err = err;
+ return;
+ };
+ }
+ defer file.close();
+
+ ctx.start_time = std.time.milliTimestamp();
+
+ if (!ctx.create) {
+ var buffer: [100]u8 = undefined;
+ ctx.bytes_read = 0;
+ while (true) {
+ const amt = file.read(buffer[0..]) catch |err| {
+ ctx.err = err;
+ return;
+ };
+ if (amt == 0) break;
+ ctx.bytes_read.? += amt;
+ }
+ }
+
+ std.time.sleep(FILE_LOCK_TEST_SLEEP_TIME);
+
+ ctx.end_time = std.time.milliTimestamp();
+ }
+};
+
+fn run_lock_file_test(contexts: []FileLockTestContext) !void {
+ var threads = std.ArrayList(*std.Thread).init(std.testing.allocator);
+ defer {
+ for (threads.toSlice()) |thread| {
+ thread.wait();
+ }
+ threads.deinit();
+ }
+ for (contexts) |*ctx, idx| {
+ try threads.append(try std.Thread.spawn(ctx, FileLockTestContext.run));
+ }
+}
lib/std/fs.zig
@@ -1584,13 +1584,6 @@ pub fn openSelfExe() OpenSelfExeError!File {
return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, .{});
}
-test "openSelfExe" {
- switch (builtin.os.tag) {
- .linux, .macosx, .ios, .windows, .freebsd, .dragonfly => (try openSelfExe()).close(),
- else => return error.SkipZigTest, // Unsupported OS.
- }
-}
-
pub const SelfExePathError = os.ReadLinkError || os.SysCtlError;
/// `selfExePath` except allocates the result on the heap.
@@ -1678,163 +1671,9 @@ test "" {
_ = copyFileAbsolute;
_ = updateFileAbsolute;
_ = Dir.copyFile;
+ _ = @import("fs/test.zig");
_ = @import("fs/path.zig");
_ = @import("fs/file.zig");
_ = @import("fs/get_app_data_dir.zig");
_ = @import("fs/watch.zig");
}
-
-const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.millisecond;
-
-test "open file with exclusive nonblocking lock twice" {
- const dir = cwd();
- const filename = "file_nonblocking_lock_test.txt";
-
- const file1 = try dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
- defer file1.close();
-
- const file2 = dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
- std.debug.assert(std.meta.eql(file2, error.WouldBlock));
-
- dir.deleteFile(filename) catch |err| switch (err) {
- error.FileNotFound => {},
- else => return err,
- };
-}
-
-test "open file with lock twice, make sure it wasn't open at the same time" {
- if (builtin.single_threaded) return;
-
- const filename = "file_lock_test.txt";
-
- var contexts = [_]FileLockTestContext{
- .{ .filename = filename, .create = true, .lock = .Exclusive },
- .{ .filename = filename, .create = true, .lock = .Exclusive },
- };
- try run_lock_file_test(&contexts);
-
- // Check for an error
- var was_error = false;
- for (contexts) |context, idx| {
- if (context.err) |err| {
- was_error = true;
- std.debug.warn("\nError in context {}: {}\n", .{ idx, err });
- }
- }
- if (was_error) builtin.panic("There was an error in contexts", null);
-
- std.debug.assert(!contexts[0].overlaps(&contexts[1]));
-
- cwd().deleteFile(filename) catch |err| switch (err) {
- error.FileNotFound => {},
- else => return err,
- };
-}
-
-test "create file, lock and read from multiple process at once" {
- if (builtin.single_threaded) return;
-
- const filename = "file_read_lock_test.txt";
- const filedata = "Hello, world!\n";
-
- try std.fs.cwd().writeFile(filename, filedata);
-
- var contexts = [_]FileLockTestContext{
- .{ .filename = filename, .create = false, .lock = .Shared },
- .{ .filename = filename, .create = false, .lock = .Shared },
- .{ .filename = filename, .create = false, .lock = .Exclusive },
- };
-
- try run_lock_file_test(&contexts);
-
- var was_error = false;
- for (contexts) |context, idx| {
- if (context.err) |err| {
- was_error = true;
- std.debug.warn("\nError in context {}: {}\n", .{ idx, err });
- }
- }
- if (was_error) builtin.panic("There was an error in contexts", null);
-
- std.debug.assert(contexts[0].overlaps(&contexts[1]));
- std.debug.assert(!contexts[2].overlaps(&contexts[0]));
- std.debug.assert(!contexts[2].overlaps(&contexts[1]));
- if (contexts[0].bytes_read.? != filedata.len) {
- std.debug.warn("\n bytes_read: {}, expected: {} \n", .{ contexts[0].bytes_read, filedata.len });
- }
- std.debug.assert(contexts[0].bytes_read.? == filedata.len);
- std.debug.assert(contexts[1].bytes_read.? == filedata.len);
-
- cwd().deleteFile(filename) catch |err| switch (err) {
- error.FileNotFound => {},
- else => return err,
- };
-}
-
-const FileLockTestContext = struct {
- filename: []const u8,
- pid: if (builtin.os.tag == .windows) ?void else ?std.os.pid_t = null,
-
- // use file.createFile
- create: bool,
- // the type of lock to use
- lock: File.Lock,
-
- // Output variables
- err: ?(File.OpenError || std.os.ReadError) = null,
- start_time: u64 = 0,
- end_time: u64 = 0,
- bytes_read: ?usize = null,
-
- fn overlaps(self: *const @This(), other: *const @This()) bool {
- return (self.start_time < other.end_time) and (self.end_time > other.start_time);
- }
-
- fn run(ctx: *@This()) void {
- var file: File = undefined;
- if (ctx.create) {
- file = cwd().createFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| {
- ctx.err = err;
- return;
- };
- } else {
- file = cwd().openFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| {
- ctx.err = err;
- return;
- };
- }
- defer file.close();
-
- ctx.start_time = std.time.milliTimestamp();
-
- if (!ctx.create) {
- var buffer: [100]u8 = undefined;
- ctx.bytes_read = 0;
- while (true) {
- const amt = file.read(buffer[0..]) catch |err| {
- ctx.err = err;
- return;
- };
- if (amt == 0) break;
- ctx.bytes_read.? += amt;
- }
- }
-
- std.time.sleep(FILE_LOCK_TEST_SLEEP_TIME);
-
- ctx.end_time = std.time.milliTimestamp();
- }
-};
-
-fn run_lock_file_test(contexts: []FileLockTestContext) !void {
- var threads = std.ArrayList(*std.Thread).init(std.testing.allocator);
- defer {
- for (threads.toSlice()) |thread| {
- thread.wait();
- }
- threads.deinit();
- }
- for (contexts) |*ctx, idx| {
- try threads.append(try std.Thread.spawn(ctx, FileLockTestContext.run));
- }
-}