parent
c236cbff39
commit
4ceaa0595a
163
lib/std/fs.zig
163
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));
|
||||
}
|
||||
}
|
||||
|
|
169
lib/std/fs/test.zig
Normal file
169
lib/std/fs/test.zig
Normal file
|
@ -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));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user