2020-08-20 10:40:15 +08:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// Copyright (c) 2015-2020 Zig Contributors
|
|
|
|
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
|
|
|
|
// The MIT license requires this copyright notice to be included in all copies
|
|
|
|
// and substantial portions of the software.
|
2020-04-12 05:50:38 +08:00
|
|
|
const std = @import("../std.zig");
|
2020-06-21 21:18:50 +08:00
|
|
|
const testing = std.testing;
|
2020-04-12 05:50:38 +08:00
|
|
|
const builtin = std.builtin;
|
|
|
|
const fs = std.fs;
|
2020-06-21 21:18:50 +08:00
|
|
|
const mem = std.mem;
|
2020-06-30 23:43:00 +08:00
|
|
|
const wasi = std.os.wasi;
|
2020-06-21 21:18:50 +08:00
|
|
|
|
2020-07-01 20:15:58 +08:00
|
|
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
2020-06-30 23:43:00 +08:00
|
|
|
const Dir = std.fs.Dir;
|
2020-04-12 05:50:38 +08:00
|
|
|
const File = std.fs.File;
|
2020-06-21 21:18:50 +08:00
|
|
|
const tmpDir = testing.tmpDir;
|
|
|
|
|
2020-07-22 02:40:34 +08:00
|
|
|
test "Dir.readLink" {
|
|
|
|
var tmp = tmpDir(.{});
|
|
|
|
defer tmp.cleanup();
|
|
|
|
|
|
|
|
// Create some targets
|
|
|
|
try tmp.dir.writeFile("file.txt", "nonsense");
|
|
|
|
try tmp.dir.makeDir("subdir");
|
|
|
|
|
|
|
|
{
|
|
|
|
// Create symbolic link by path
|
2020-08-28 02:55:47 +08:00
|
|
|
tmp.dir.symLink("file.txt", "symlink1", .{}) catch |err| switch (err) {
|
|
|
|
// Symlink requires admin privileges on windows, so this test can legitimately fail.
|
|
|
|
error.AccessDenied => return error.SkipZigTest,
|
|
|
|
else => return err,
|
|
|
|
};
|
2020-07-22 02:40:34 +08:00
|
|
|
try testReadLink(tmp.dir, "file.txt", "symlink1");
|
|
|
|
}
|
|
|
|
{
|
|
|
|
// Create symbolic link by path
|
2020-08-28 02:55:47 +08:00
|
|
|
tmp.dir.symLink("subdir", "symlink2", .{ .is_directory = true }) catch |err| switch (err) {
|
|
|
|
// Symlink requires admin privileges on windows, so this test can legitimately fail.
|
|
|
|
error.AccessDenied => return error.SkipZigTest,
|
|
|
|
else => return err,
|
|
|
|
};
|
2020-07-22 02:40:34 +08:00
|
|
|
try testReadLink(tmp.dir, "subdir", "symlink2");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn testReadLink(dir: Dir, target_path: []const u8, symlink_path: []const u8) !void {
|
|
|
|
var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
|
|
|
|
const given = try dir.readLink(symlink_path, buffer[0..]);
|
|
|
|
testing.expect(mem.eql(u8, target_path, given));
|
|
|
|
}
|
|
|
|
|
2020-07-19 17:47:00 +08:00
|
|
|
test "readLinkAbsolute" {
|
|
|
|
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
|
|
|
|
|
|
|
var tmp = tmpDir(.{});
|
|
|
|
defer tmp.cleanup();
|
|
|
|
|
|
|
|
// Create some targets
|
|
|
|
try tmp.dir.writeFile("file.txt", "nonsense");
|
|
|
|
try tmp.dir.makeDir("subdir");
|
|
|
|
|
|
|
|
// Get base abs path
|
|
|
|
var arena = ArenaAllocator.init(testing.allocator);
|
|
|
|
defer arena.deinit();
|
|
|
|
|
|
|
|
const base_path = blk: {
|
|
|
|
const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
|
|
|
|
break :blk try fs.realpathAlloc(&arena.allocator, relative_path);
|
|
|
|
};
|
|
|
|
const allocator = &arena.allocator;
|
|
|
|
|
|
|
|
{
|
|
|
|
const target_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "file.txt" });
|
|
|
|
const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink1" });
|
|
|
|
|
|
|
|
// Create symbolic link by path
|
2020-08-28 02:55:47 +08:00
|
|
|
fs.symLinkAbsolute(target_path, symlink_path, .{}) catch |err| switch (err) {
|
|
|
|
// Symlink requires admin privileges on windows, so this test can legitimately fail.
|
|
|
|
error.AccessDenied => return error.SkipZigTest,
|
|
|
|
else => return err,
|
|
|
|
};
|
2020-07-20 04:25:00 +08:00
|
|
|
try testReadLinkAbsolute(target_path, symlink_path);
|
2020-07-19 17:47:00 +08:00
|
|
|
}
|
|
|
|
{
|
|
|
|
const target_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "subdir" });
|
|
|
|
const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink2" });
|
|
|
|
|
|
|
|
// Create symbolic link by path
|
2020-08-28 02:55:47 +08:00
|
|
|
fs.symLinkAbsolute(target_path, symlink_path, .{ .is_directory = true }) catch |err| switch (err) {
|
|
|
|
// Symlink requires admin privileges on windows, so this test can legitimately fail.
|
|
|
|
error.AccessDenied => return error.SkipZigTest,
|
|
|
|
else => return err,
|
|
|
|
};
|
2020-07-20 04:25:00 +08:00
|
|
|
try testReadLinkAbsolute(target_path, symlink_path);
|
2020-07-19 17:47:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-20 04:25:00 +08:00
|
|
|
fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void {
|
2020-07-19 17:47:00 +08:00
|
|
|
var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
|
|
|
|
const given = try fs.readLinkAbsolute(symlink_path, buffer[0..]);
|
|
|
|
testing.expect(mem.eql(u8, target_path, given));
|
|
|
|
}
|
|
|
|
|
2020-06-30 23:43:00 +08:00
|
|
|
test "Dir.Iterator" {
|
|
|
|
var tmp_dir = tmpDir(.{ .iterate = true });
|
|
|
|
defer tmp_dir.cleanup();
|
|
|
|
|
|
|
|
// First, create a couple of entries to iterate over.
|
|
|
|
const file = try tmp_dir.dir.createFile("some_file", .{});
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
try tmp_dir.dir.makeDir("some_dir");
|
|
|
|
|
2020-07-01 20:15:58 +08:00
|
|
|
var arena = ArenaAllocator.init(testing.allocator);
|
|
|
|
defer arena.deinit();
|
|
|
|
|
|
|
|
var entries = std.ArrayList(Dir.Entry).init(&arena.allocator);
|
|
|
|
|
2020-06-30 23:43:00 +08:00
|
|
|
// Create iterator.
|
|
|
|
var iter = tmp_dir.dir.iterate();
|
|
|
|
while (try iter.next()) |entry| {
|
2020-07-01 20:15:58 +08:00
|
|
|
// We cannot just store `entry` as on Windows, we're re-using the name buffer
|
|
|
|
// which means we'll actually share the `name` pointer between entries!
|
2020-07-03 02:54:57 +08:00
|
|
|
const name = try arena.allocator.dupe(u8, entry.name);
|
2020-07-01 20:15:58 +08:00
|
|
|
try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });
|
2020-06-30 23:43:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
testing.expect(entries.items.len == 2); // note that the Iterator skips '.' and '..'
|
|
|
|
testing.expect(contains(&entries, Dir.Entry{ .name = "some_file", .kind = Dir.Entry.Kind.File }));
|
|
|
|
testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));
|
|
|
|
}
|
|
|
|
|
2020-08-13 05:50:00 +08:00
|
|
|
fn entryEql(lhs: Dir.Entry, rhs: Dir.Entry) bool {
|
2020-06-30 23:43:00 +08:00
|
|
|
return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool {
|
|
|
|
for (entries.items) |entry| {
|
2020-08-13 05:50:00 +08:00
|
|
|
if (entryEql(entry, el)) return true;
|
2020-06-30 23:43:00 +08:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-08-13 05:50:00 +08:00
|
|
|
test "Dir.realpath smoke test" {
|
|
|
|
switch (builtin.os.tag) {
|
2020-10-12 16:59:43 +08:00
|
|
|
.linux, .windows, .macos, .ios, .watchos, .tvos => {},
|
2020-08-13 05:50:00 +08:00
|
|
|
else => return error.SkipZigTest,
|
|
|
|
}
|
|
|
|
|
|
|
|
var tmp_dir = tmpDir(.{});
|
|
|
|
defer tmp_dir.cleanup();
|
|
|
|
|
|
|
|
var file = try tmp_dir.dir.createFile("test_file", .{ .lock = File.Lock.Shared });
|
|
|
|
// We need to close the file immediately as otherwise on Windows we'll end up
|
|
|
|
// with a sharing violation.
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
var arena = ArenaAllocator.init(testing.allocator);
|
|
|
|
defer arena.deinit();
|
|
|
|
|
|
|
|
const base_path = blk: {
|
|
|
|
const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp_dir.sub_path[0..] });
|
|
|
|
break :blk try fs.realpathAlloc(&arena.allocator, relative_path);
|
|
|
|
};
|
|
|
|
|
|
|
|
// First, test non-alloc version
|
|
|
|
{
|
|
|
|
var buf1: [fs.MAX_PATH_BYTES]u8 = undefined;
|
|
|
|
const file_path = try tmp_dir.dir.realpath("test_file", buf1[0..]);
|
|
|
|
const expected_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "test_file" });
|
|
|
|
|
|
|
|
testing.expect(mem.eql(u8, file_path, expected_path));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next, test alloc version
|
|
|
|
{
|
|
|
|
const file_path = try tmp_dir.dir.realpathAlloc(&arena.allocator, "test_file");
|
|
|
|
const expected_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "test_file" });
|
|
|
|
|
|
|
|
testing.expect(mem.eql(u8, file_path, expected_path));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-21 21:18:50 +08:00
|
|
|
test "readAllAlloc" {
|
|
|
|
var tmp_dir = tmpDir(.{});
|
|
|
|
defer tmp_dir.cleanup();
|
|
|
|
|
|
|
|
var file = try tmp_dir.dir.createFile("test_file", .{ .read = true });
|
|
|
|
defer file.close();
|
|
|
|
|
2020-09-04 15:28:43 +08:00
|
|
|
const buf1 = try file.readToEndAlloc(testing.allocator, 1024);
|
2020-06-21 21:18:50 +08:00
|
|
|
defer testing.allocator.free(buf1);
|
|
|
|
testing.expect(buf1.len == 0);
|
|
|
|
|
|
|
|
const write_buf: []const u8 = "this is a test.\nthis is a test.\nthis is a test.\nthis is a test.\n";
|
|
|
|
try file.writeAll(write_buf);
|
|
|
|
try file.seekTo(0);
|
|
|
|
|
|
|
|
// max_bytes > file_size
|
2020-09-04 15:28:43 +08:00
|
|
|
const buf2 = try file.readToEndAlloc(testing.allocator, 1024);
|
2020-06-21 21:18:50 +08:00
|
|
|
defer testing.allocator.free(buf2);
|
|
|
|
testing.expectEqual(write_buf.len, buf2.len);
|
|
|
|
testing.expect(std.mem.eql(u8, write_buf, buf2));
|
|
|
|
try file.seekTo(0);
|
|
|
|
|
|
|
|
// max_bytes == file_size
|
2020-09-04 15:28:43 +08:00
|
|
|
const buf3 = try file.readToEndAlloc(testing.allocator, write_buf.len);
|
2020-06-21 21:18:50 +08:00
|
|
|
defer testing.allocator.free(buf3);
|
|
|
|
testing.expectEqual(write_buf.len, buf3.len);
|
|
|
|
testing.expect(std.mem.eql(u8, write_buf, buf3));
|
2020-09-04 15:28:43 +08:00
|
|
|
try file.seekTo(0);
|
2020-06-21 21:18:50 +08:00
|
|
|
|
|
|
|
// max_bytes < file_size
|
2020-09-04 15:28:43 +08:00
|
|
|
testing.expectError(error.FileTooBig, file.readToEndAlloc(testing.allocator, write_buf.len - 1));
|
2020-06-21 21:18:50 +08:00
|
|
|
}
|
2020-04-12 05:50:38 +08:00
|
|
|
|
2020-06-24 08:41:41 +08:00
|
|
|
test "directory operations on files" {
|
|
|
|
var tmp_dir = tmpDir(.{});
|
|
|
|
defer tmp_dir.cleanup();
|
|
|
|
|
|
|
|
const test_file_name = "test_file";
|
|
|
|
|
|
|
|
var file = try tmp_dir.dir.createFile(test_file_name, .{ .read = true });
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
testing.expectError(error.PathAlreadyExists, tmp_dir.dir.makeDir(test_file_name));
|
|
|
|
testing.expectError(error.NotDir, tmp_dir.dir.openDir(test_file_name, .{}));
|
|
|
|
testing.expectError(error.NotDir, tmp_dir.dir.deleteDir(test_file_name));
|
|
|
|
|
2020-10-13 11:05:34 +08:00
|
|
|
if (builtin.os.tag != .wasi and builtin.os.tag != .freebsd) {
|
2020-08-13 05:50:00 +08:00
|
|
|
const absolute_path = try tmp_dir.dir.realpathAlloc(testing.allocator, test_file_name);
|
2020-06-24 08:41:41 +08:00
|
|
|
defer testing.allocator.free(absolute_path);
|
|
|
|
|
|
|
|
testing.expectError(error.PathAlreadyExists, fs.makeDirAbsolute(absolute_path));
|
|
|
|
testing.expectError(error.NotDir, fs.deleteDirAbsolute(absolute_path));
|
|
|
|
}
|
|
|
|
|
|
|
|
// ensure the file still exists and is a file as a sanity check
|
|
|
|
file = try tmp_dir.dir.openFile(test_file_name, .{});
|
|
|
|
const stat = try file.stat();
|
|
|
|
testing.expect(stat.kind == .File);
|
|
|
|
file.close();
|
|
|
|
}
|
|
|
|
|
2020-06-26 15:06:23 +08:00
|
|
|
test "file operations on directories" {
|
2020-10-13 11:05:34 +08:00
|
|
|
// TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759
|
|
|
|
if (builtin.os.tag == .freebsd) return error.SkipZigTest;
|
|
|
|
|
2020-06-26 15:06:23 +08:00
|
|
|
var tmp_dir = tmpDir(.{});
|
|
|
|
defer tmp_dir.cleanup();
|
|
|
|
|
|
|
|
const test_dir_name = "test_dir";
|
|
|
|
|
|
|
|
try tmp_dir.dir.makeDir(test_dir_name);
|
|
|
|
|
|
|
|
testing.expectError(error.IsDir, tmp_dir.dir.createFile(test_dir_name, .{}));
|
|
|
|
testing.expectError(error.IsDir, tmp_dir.dir.deleteFile(test_dir_name));
|
2020-06-29 04:56:00 +08:00
|
|
|
// Currently, WASI will return error.Unexpected (via ENOTCAPABLE) when attempting fd_read on a directory handle.
|
|
|
|
// TODO: Re-enable on WASI once https://github.com/bytecodealliance/wasmtime/issues/1935 is resolved.
|
|
|
|
if (builtin.os.tag != .wasi) {
|
|
|
|
testing.expectError(error.IsDir, tmp_dir.dir.readFileAlloc(testing.allocator, test_dir_name, std.math.maxInt(usize)));
|
|
|
|
}
|
|
|
|
// Note: The `.write = true` is necessary to ensure the error occurs on all platforms.
|
|
|
|
// TODO: Add a read-only test as well, see https://github.com/ziglang/zig/issues/5732
|
2020-06-26 15:06:23 +08:00
|
|
|
testing.expectError(error.IsDir, tmp_dir.dir.openFile(test_dir_name, .{ .write = true }));
|
|
|
|
|
2020-10-13 11:05:34 +08:00
|
|
|
if (builtin.os.tag != .wasi and builtin.os.tag != .freebsd) {
|
2020-08-13 05:50:00 +08:00
|
|
|
const absolute_path = try tmp_dir.dir.realpathAlloc(testing.allocator, test_dir_name);
|
2020-06-26 15:06:23 +08:00
|
|
|
defer testing.allocator.free(absolute_path);
|
|
|
|
|
|
|
|
testing.expectError(error.IsDir, fs.createFileAbsolute(absolute_path, .{}));
|
|
|
|
testing.expectError(error.IsDir, fs.deleteFileAbsolute(absolute_path));
|
|
|
|
}
|
|
|
|
|
|
|
|
// ensure the directory still exists as a sanity check
|
|
|
|
var dir = try tmp_dir.dir.openDir(test_dir_name, .{});
|
|
|
|
dir.close();
|
|
|
|
}
|
|
|
|
|
2020-09-30 08:47:24 +08:00
|
|
|
test "deleteDir" {
|
|
|
|
var tmp_dir = tmpDir(.{});
|
|
|
|
defer tmp_dir.cleanup();
|
|
|
|
|
|
|
|
// deleting a non-existent directory
|
|
|
|
testing.expectError(error.FileNotFound, tmp_dir.dir.deleteDir("test_dir"));
|
|
|
|
|
|
|
|
var dir = try tmp_dir.dir.makeOpenPath("test_dir", .{});
|
|
|
|
var file = try dir.createFile("test_file", .{});
|
|
|
|
file.close();
|
|
|
|
dir.close();
|
|
|
|
|
|
|
|
// deleting a non-empty directory
|
|
|
|
// TODO: Re-enable this check on Windows, see https://github.com/ziglang/zig/issues/5537
|
|
|
|
if (builtin.os.tag != .windows) {
|
|
|
|
testing.expectError(error.DirNotEmpty, tmp_dir.dir.deleteDir("test_dir"));
|
|
|
|
}
|
|
|
|
|
|
|
|
dir = try tmp_dir.dir.openDir("test_dir", .{});
|
|
|
|
try dir.deleteFile("test_file");
|
|
|
|
dir.close();
|
|
|
|
|
|
|
|
// deleting an empty directory
|
|
|
|
try tmp_dir.dir.deleteDir("test_dir");
|
|
|
|
}
|
|
|
|
|
2020-09-17 11:59:45 +08:00
|
|
|
test "Dir.rename files" {
|
|
|
|
var tmp_dir = tmpDir(.{});
|
|
|
|
defer tmp_dir.cleanup();
|
|
|
|
|
|
|
|
testing.expectError(error.FileNotFound, tmp_dir.dir.rename("missing_file_name", "something_else"));
|
|
|
|
|
|
|
|
// Renaming files
|
|
|
|
const test_file_name = "test_file";
|
|
|
|
const renamed_test_file_name = "test_file_renamed";
|
|
|
|
var file = try tmp_dir.dir.createFile(test_file_name, .{ .read = true });
|
|
|
|
file.close();
|
|
|
|
try tmp_dir.dir.rename(test_file_name, renamed_test_file_name);
|
|
|
|
|
|
|
|
// Ensure the file was renamed
|
|
|
|
testing.expectError(error.FileNotFound, tmp_dir.dir.openFile(test_file_name, .{}));
|
|
|
|
file = try tmp_dir.dir.openFile(renamed_test_file_name, .{});
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
// Rename to self succeeds
|
|
|
|
try tmp_dir.dir.rename(renamed_test_file_name, renamed_test_file_name);
|
|
|
|
|
|
|
|
// Rename to existing file succeeds
|
|
|
|
var existing_file = try tmp_dir.dir.createFile("existing_file", .{ .read = true });
|
|
|
|
existing_file.close();
|
|
|
|
try tmp_dir.dir.rename(renamed_test_file_name, "existing_file");
|
|
|
|
|
|
|
|
testing.expectError(error.FileNotFound, tmp_dir.dir.openFile(renamed_test_file_name, .{}));
|
|
|
|
file = try tmp_dir.dir.openFile("existing_file", .{});
|
|
|
|
file.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
test "Dir.rename directories" {
|
|
|
|
// TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364
|
|
|
|
if (builtin.os.tag == .windows) return error.SkipZigTest;
|
|
|
|
|
|
|
|
var tmp_dir = tmpDir(.{});
|
|
|
|
defer tmp_dir.cleanup();
|
|
|
|
|
|
|
|
// Renaming directories
|
|
|
|
try tmp_dir.dir.makeDir("test_dir");
|
|
|
|
try tmp_dir.dir.rename("test_dir", "test_dir_renamed");
|
|
|
|
|
|
|
|
// Ensure the directory was renamed
|
|
|
|
testing.expectError(error.FileNotFound, tmp_dir.dir.openDir("test_dir", .{}));
|
|
|
|
var dir = try tmp_dir.dir.openDir("test_dir_renamed", .{});
|
|
|
|
|
|
|
|
// Put a file in the directory
|
|
|
|
var file = try dir.createFile("test_file", .{ .read = true });
|
|
|
|
file.close();
|
|
|
|
dir.close();
|
|
|
|
|
|
|
|
try tmp_dir.dir.rename("test_dir_renamed", "test_dir_renamed_again");
|
|
|
|
|
|
|
|
// Ensure the directory was renamed and the file still exists in it
|
|
|
|
testing.expectError(error.FileNotFound, tmp_dir.dir.openDir("test_dir_renamed", .{}));
|
|
|
|
dir = try tmp_dir.dir.openDir("test_dir_renamed_again", .{});
|
|
|
|
file = try dir.openFile("test_file", .{});
|
|
|
|
file.close();
|
|
|
|
dir.close();
|
|
|
|
|
|
|
|
// Try to rename to a non-empty directory now
|
|
|
|
var target_dir = try tmp_dir.dir.makeOpenPath("non_empty_target_dir", .{});
|
|
|
|
file = try target_dir.createFile("filler", .{ .read = true });
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
testing.expectError(error.PathAlreadyExists, tmp_dir.dir.rename("test_dir_renamed_again", "non_empty_target_dir"));
|
|
|
|
|
|
|
|
// Ensure the directory was not renamed
|
|
|
|
dir = try tmp_dir.dir.openDir("test_dir_renamed_again", .{});
|
|
|
|
file = try dir.openFile("test_file", .{});
|
|
|
|
file.close();
|
|
|
|
dir.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
test "Dir.rename file <-> dir" {
|
|
|
|
// TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364
|
|
|
|
if (builtin.os.tag == .windows) return error.SkipZigTest;
|
|
|
|
|
|
|
|
var tmp_dir = tmpDir(.{});
|
|
|
|
defer tmp_dir.cleanup();
|
|
|
|
|
|
|
|
var file = try tmp_dir.dir.createFile("test_file", .{ .read = true });
|
|
|
|
file.close();
|
|
|
|
try tmp_dir.dir.makeDir("test_dir");
|
|
|
|
testing.expectError(error.IsDir, tmp_dir.dir.rename("test_file", "test_dir"));
|
|
|
|
testing.expectError(error.NotDir, tmp_dir.dir.rename("test_dir", "test_file"));
|
|
|
|
}
|
|
|
|
|
|
|
|
test "rename" {
|
|
|
|
var tmp_dir1 = tmpDir(.{});
|
|
|
|
defer tmp_dir1.cleanup();
|
|
|
|
|
|
|
|
var tmp_dir2 = tmpDir(.{});
|
|
|
|
defer tmp_dir2.cleanup();
|
|
|
|
|
|
|
|
// Renaming files
|
|
|
|
const test_file_name = "test_file";
|
|
|
|
const renamed_test_file_name = "test_file_renamed";
|
|
|
|
var file = try tmp_dir1.dir.createFile(test_file_name, .{ .read = true });
|
|
|
|
file.close();
|
|
|
|
try fs.rename(tmp_dir1.dir, test_file_name, tmp_dir2.dir, renamed_test_file_name);
|
|
|
|
|
|
|
|
// ensure the file was renamed
|
|
|
|
testing.expectError(error.FileNotFound, tmp_dir1.dir.openFile(test_file_name, .{}));
|
|
|
|
file = try tmp_dir2.dir.openFile(renamed_test_file_name, .{});
|
|
|
|
file.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
test "renameAbsolute" {
|
|
|
|
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
|
|
|
|
|
|
|
var tmp_dir = tmpDir(.{});
|
|
|
|
defer tmp_dir.cleanup();
|
|
|
|
|
|
|
|
// Get base abs path
|
|
|
|
var arena = ArenaAllocator.init(testing.allocator);
|
|
|
|
defer arena.deinit();
|
|
|
|
const allocator = &arena.allocator;
|
|
|
|
|
|
|
|
const base_path = blk: {
|
|
|
|
const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp_dir.sub_path[0..] });
|
|
|
|
break :blk try fs.realpathAlloc(&arena.allocator, relative_path);
|
|
|
|
};
|
|
|
|
|
|
|
|
testing.expectError(error.FileNotFound, fs.renameAbsolute(
|
|
|
|
try fs.path.join(allocator, &[_][]const u8{ base_path, "missing_file_name" }),
|
|
|
|
try fs.path.join(allocator, &[_][]const u8{ base_path, "something_else" }),
|
|
|
|
));
|
|
|
|
|
|
|
|
// Renaming files
|
|
|
|
const test_file_name = "test_file";
|
|
|
|
const renamed_test_file_name = "test_file_renamed";
|
|
|
|
var file = try tmp_dir.dir.createFile(test_file_name, .{ .read = true });
|
|
|
|
file.close();
|
|
|
|
try fs.renameAbsolute(
|
|
|
|
try fs.path.join(allocator, &[_][]const u8{ base_path, test_file_name }),
|
|
|
|
try fs.path.join(allocator, &[_][]const u8{ base_path, renamed_test_file_name }),
|
|
|
|
);
|
|
|
|
|
|
|
|
// ensure the file was renamed
|
|
|
|
testing.expectError(error.FileNotFound, tmp_dir.dir.openFile(test_file_name, .{}));
|
|
|
|
file = try tmp_dir.dir.openFile(renamed_test_file_name, .{});
|
|
|
|
const stat = try file.stat();
|
|
|
|
testing.expect(stat.kind == .File);
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
// Renaming directories
|
|
|
|
const test_dir_name = "test_dir";
|
|
|
|
const renamed_test_dir_name = "test_dir_renamed";
|
|
|
|
try tmp_dir.dir.makeDir(test_dir_name);
|
|
|
|
try fs.renameAbsolute(
|
|
|
|
try fs.path.join(allocator, &[_][]const u8{ base_path, test_dir_name }),
|
|
|
|
try fs.path.join(allocator, &[_][]const u8{ base_path, renamed_test_dir_name }),
|
|
|
|
);
|
|
|
|
|
|
|
|
// ensure the directory was renamed
|
|
|
|
testing.expectError(error.FileNotFound, tmp_dir.dir.openDir(test_dir_name, .{}));
|
|
|
|
var dir = try tmp_dir.dir.openDir(renamed_test_dir_name, .{});
|
|
|
|
dir.close();
|
|
|
|
}
|
|
|
|
|
2020-04-12 05:50:38 +08:00
|
|
|
test "openSelfExe" {
|
Add/fix missing WASI functionality to pass libstd tests
This rather large commit adds/fixes missing WASI functionality
in `libstd` needed to pass the `libstd` tests. As such, now by
default tests targeting `wasm32-wasi` target are enabled in
`test/tests.zig` module. However, they can be disabled by passing
the `-Dskip-wasi=true` flag when invoking the `zig build test`
command. When the flag is set to `false`, i.e., when WASI tests are
included, `wasmtime` with `--dir=.` is used as the default testing
command.
Since the majority of `libstd` tests were relying on `fs.cwd()`
call to get current working directory handle wrapped in `Dir`
struct, in order to make the tests WASI-friendly, `fs.cwd()`
call was replaced with `testing.getTestDir()` function which
resolved to either `fs.cwd()` for non-WASI targets, or tries to
fetch the preopen list from the WASI runtime and extract a
preopen for '.' path.
The summary of changes introduced by this commit:
* implement `Dir.makeDir` and `Dir.openDir` targeting WASI
* implement `Dir.deleteFile` and `Dir.deleteDir` targeting WASI
* fix `os.close` and map errors in `unlinkat`
* move WASI-specific `mkdirat` and `unlinkat` from `std.fs.wasi`
to `std.os` module
* implement `lseek_{SET, CUR, END}` targeting WASI
* implement `futimens` targeting WASI
* implement `ftruncate` targeting WASI
* implement `readv`, `writev`, `pread{v}`, `pwrite{v}` targeting WASI
* make sure ANSI escape codes are _not_ used in stderr or stdout
in WASI, as WASI always sanitizes stderr, and sanitizes stdout if
fd is a TTY
* fix specifying WASI rights when opening/creating files/dirs
* tweak `AtomicFile` to be WASI-compatible
* implement `os.renameatWasi` for WASI-compliant `os.renameat` function
* implement sleep() targeting WASI
* fix `process.getEnvMap` targeting WASI
2020-05-05 23:23:49 +08:00
|
|
|
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
|
|
|
|
2020-03-28 04:13:26 +08:00
|
|
|
const self_exe_file = try std.fs.openSelfExe(.{});
|
2020-04-12 05:50:38 +08:00
|
|
|
self_exe_file.close();
|
|
|
|
}
|
|
|
|
|
2020-06-27 14:17:40 +08:00
|
|
|
test "makePath, put some files in it, deleteTree" {
|
|
|
|
var tmp = tmpDir(.{});
|
|
|
|
defer tmp.cleanup();
|
|
|
|
|
|
|
|
try tmp.dir.makePath("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");
|
|
|
|
try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");
|
|
|
|
try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");
|
|
|
|
try tmp.dir.deleteTree("os_test_tmp");
|
|
|
|
if (tmp.dir.openDir("os_test_tmp", .{})) |dir| {
|
|
|
|
@panic("expected error");
|
|
|
|
} else |err| {
|
|
|
|
testing.expect(err == error.FileNotFound);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
test "access file" {
|
|
|
|
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
|
|
|
|
|
|
|
var tmp = tmpDir(.{});
|
|
|
|
defer tmp.cleanup();
|
|
|
|
|
|
|
|
try tmp.dir.makePath("os_test_tmp");
|
|
|
|
if (tmp.dir.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{})) |ok| {
|
|
|
|
@panic("expected error");
|
|
|
|
} else |err| {
|
|
|
|
testing.expect(err == error.FileNotFound);
|
|
|
|
}
|
|
|
|
|
|
|
|
try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", "");
|
|
|
|
try tmp.dir.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{});
|
|
|
|
try tmp.dir.deleteTree("os_test_tmp");
|
|
|
|
}
|
|
|
|
|
|
|
|
test "sendfile" {
|
|
|
|
var tmp = tmpDir(.{});
|
|
|
|
defer tmp.cleanup();
|
|
|
|
|
|
|
|
try tmp.dir.makePath("os_test_tmp");
|
|
|
|
defer tmp.dir.deleteTree("os_test_tmp") catch {};
|
|
|
|
|
|
|
|
var dir = try tmp.dir.openDir("os_test_tmp", .{});
|
|
|
|
defer dir.close();
|
|
|
|
|
|
|
|
const line1 = "line1\n";
|
|
|
|
const line2 = "second line\n";
|
|
|
|
var vecs = [_]std.os.iovec_const{
|
|
|
|
.{
|
|
|
|
.iov_base = line1,
|
|
|
|
.iov_len = line1.len,
|
|
|
|
},
|
|
|
|
.{
|
|
|
|
.iov_base = line2,
|
|
|
|
.iov_len = line2.len,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
var src_file = try dir.createFile("sendfile1.txt", .{ .read = true });
|
|
|
|
defer src_file.close();
|
|
|
|
|
|
|
|
try src_file.writevAll(&vecs);
|
|
|
|
|
|
|
|
var dest_file = try dir.createFile("sendfile2.txt", .{ .read = true });
|
|
|
|
defer dest_file.close();
|
|
|
|
|
|
|
|
const header1 = "header1\n";
|
|
|
|
const header2 = "second header\n";
|
|
|
|
const trailer1 = "trailer1\n";
|
|
|
|
const trailer2 = "second trailer\n";
|
|
|
|
var hdtr = [_]std.os.iovec_const{
|
|
|
|
.{
|
|
|
|
.iov_base = header1,
|
|
|
|
.iov_len = header1.len,
|
|
|
|
},
|
|
|
|
.{
|
|
|
|
.iov_base = header2,
|
|
|
|
.iov_len = header2.len,
|
|
|
|
},
|
|
|
|
.{
|
|
|
|
.iov_base = trailer1,
|
|
|
|
.iov_len = trailer1.len,
|
|
|
|
},
|
|
|
|
.{
|
|
|
|
.iov_base = trailer2,
|
|
|
|
.iov_len = trailer2.len,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
var written_buf: [100]u8 = undefined;
|
|
|
|
try dest_file.writeFileAll(src_file, .{
|
|
|
|
.in_offset = 1,
|
|
|
|
.in_len = 10,
|
|
|
|
.headers_and_trailers = &hdtr,
|
|
|
|
.header_count = 2,
|
|
|
|
});
|
|
|
|
const amt = try dest_file.preadAll(&written_buf, 0);
|
|
|
|
testing.expect(mem.eql(u8, written_buf[0..amt], "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
|
|
|
|
}
|
|
|
|
|
2020-08-12 03:49:43 +08:00
|
|
|
test "copyRangeAll" {
|
|
|
|
var tmp = tmpDir(.{});
|
|
|
|
defer tmp.cleanup();
|
|
|
|
|
|
|
|
try tmp.dir.makePath("os_test_tmp");
|
|
|
|
defer tmp.dir.deleteTree("os_test_tmp") catch {};
|
|
|
|
|
|
|
|
var dir = try tmp.dir.openDir("os_test_tmp", .{});
|
|
|
|
defer dir.close();
|
|
|
|
|
|
|
|
var src_file = try dir.createFile("file1.txt", .{ .read = true });
|
|
|
|
defer src_file.close();
|
|
|
|
|
|
|
|
const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";
|
|
|
|
try src_file.writeAll(data);
|
|
|
|
|
|
|
|
var dest_file = try dir.createFile("file2.txt", .{ .read = true });
|
|
|
|
defer dest_file.close();
|
|
|
|
|
|
|
|
var written_buf: [100]u8 = undefined;
|
|
|
|
_ = try src_file.copyRangeAll(0, dest_file, 0, data.len);
|
|
|
|
|
|
|
|
const amt = try dest_file.preadAll(&written_buf, 0);
|
|
|
|
testing.expect(mem.eql(u8, written_buf[0..amt], data));
|
|
|
|
}
|
|
|
|
|
2020-06-27 14:17:40 +08:00
|
|
|
test "fs.copyFile" {
|
|
|
|
const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";
|
|
|
|
const src_file = "tmp_test_copy_file.txt";
|
|
|
|
const dest_file = "tmp_test_copy_file2.txt";
|
|
|
|
const dest_file2 = "tmp_test_copy_file3.txt";
|
|
|
|
|
|
|
|
var tmp = tmpDir(.{});
|
|
|
|
defer tmp.cleanup();
|
|
|
|
|
|
|
|
try tmp.dir.writeFile(src_file, data);
|
|
|
|
defer tmp.dir.deleteFile(src_file) catch {};
|
|
|
|
|
|
|
|
try tmp.dir.copyFile(src_file, tmp.dir, dest_file, .{});
|
|
|
|
defer tmp.dir.deleteFile(dest_file) catch {};
|
|
|
|
|
|
|
|
try tmp.dir.copyFile(src_file, tmp.dir, dest_file2, .{ .override_mode = File.default_mode });
|
|
|
|
defer tmp.dir.deleteFile(dest_file2) catch {};
|
|
|
|
|
|
|
|
try expectFileContents(tmp.dir, dest_file, data);
|
|
|
|
try expectFileContents(tmp.dir, dest_file2, data);
|
|
|
|
}
|
|
|
|
|
2020-06-30 23:43:00 +08:00
|
|
|
fn expectFileContents(dir: Dir, file_path: []const u8, data: []const u8) !void {
|
2020-06-27 14:17:40 +08:00
|
|
|
const contents = try dir.readFileAlloc(testing.allocator, file_path, 1000);
|
|
|
|
defer testing.allocator.free(contents);
|
|
|
|
|
|
|
|
testing.expectEqualSlices(u8, data, contents);
|
|
|
|
}
|
|
|
|
|
|
|
|
test "AtomicFile" {
|
|
|
|
const test_out_file = "tmp_atomic_file_test_dest.txt";
|
|
|
|
const test_content =
|
|
|
|
\\ hello!
|
|
|
|
\\ this is a test file
|
|
|
|
;
|
|
|
|
|
|
|
|
var tmp = tmpDir(.{});
|
|
|
|
defer tmp.cleanup();
|
|
|
|
|
|
|
|
{
|
|
|
|
var af = try tmp.dir.atomicFile(test_out_file, .{});
|
|
|
|
defer af.deinit();
|
|
|
|
try af.file.writeAll(test_content);
|
|
|
|
try af.finish();
|
|
|
|
}
|
|
|
|
const content = try tmp.dir.readFileAlloc(testing.allocator, test_out_file, 9999);
|
|
|
|
defer testing.allocator.free(content);
|
|
|
|
testing.expect(mem.eql(u8, content, test_content));
|
|
|
|
|
|
|
|
try tmp.dir.deleteFile(test_out_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
test "realpath" {
|
|
|
|
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
|
|
|
|
|
|
|
var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
|
|
|
testing.expectError(error.FileNotFound, fs.realpath("definitely_bogus_does_not_exist1234", &buf));
|
|
|
|
}
|
|
|
|
|
2020-05-25 08:06:56 +08:00
|
|
|
const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.ns_per_ms;
|
2020-04-12 05:50:38 +08:00
|
|
|
|
|
|
|
test "open file with exclusive nonblocking lock twice" {
|
Add/fix missing WASI functionality to pass libstd tests
This rather large commit adds/fixes missing WASI functionality
in `libstd` needed to pass the `libstd` tests. As such, now by
default tests targeting `wasm32-wasi` target are enabled in
`test/tests.zig` module. However, they can be disabled by passing
the `-Dskip-wasi=true` flag when invoking the `zig build test`
command. When the flag is set to `false`, i.e., when WASI tests are
included, `wasmtime` with `--dir=.` is used as the default testing
command.
Since the majority of `libstd` tests were relying on `fs.cwd()`
call to get current working directory handle wrapped in `Dir`
struct, in order to make the tests WASI-friendly, `fs.cwd()`
call was replaced with `testing.getTestDir()` function which
resolved to either `fs.cwd()` for non-WASI targets, or tries to
fetch the preopen list from the WASI runtime and extract a
preopen for '.' path.
The summary of changes introduced by this commit:
* implement `Dir.makeDir` and `Dir.openDir` targeting WASI
* implement `Dir.deleteFile` and `Dir.deleteDir` targeting WASI
* fix `os.close` and map errors in `unlinkat`
* move WASI-specific `mkdirat` and `unlinkat` from `std.fs.wasi`
to `std.os` module
* implement `lseek_{SET, CUR, END}` targeting WASI
* implement `futimens` targeting WASI
* implement `ftruncate` targeting WASI
* implement `readv`, `writev`, `pread{v}`, `pwrite{v}` targeting WASI
* make sure ANSI escape codes are _not_ used in stderr or stdout
in WASI, as WASI always sanitizes stderr, and sanitizes stdout if
fd is a TTY
* fix specifying WASI rights when opening/creating files/dirs
* tweak `AtomicFile` to be WASI-compatible
* implement `os.renameatWasi` for WASI-compliant `os.renameat` function
* implement sleep() targeting WASI
* fix `process.getEnvMap` targeting WASI
2020-05-05 23:23:49 +08:00
|
|
|
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
|
|
|
|
2020-10-13 11:05:34 +08:00
|
|
|
// TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759
|
|
|
|
if (builtin.os.tag == .freebsd) return error.SkipZigTest;
|
|
|
|
|
2020-05-19 04:22:27 +08:00
|
|
|
const dir = fs.cwd();
|
2020-04-12 05:50:38 +08:00
|
|
|
const filename = "file_nonblocking_lock_test.txt";
|
|
|
|
|
2020-05-19 04:22:27 +08:00
|
|
|
const file1 = try dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
|
2020-04-12 05:50:38 +08:00
|
|
|
defer file1.close();
|
|
|
|
|
2020-05-19 04:22:27 +08:00
|
|
|
const file2 = dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
|
2020-04-12 05:50:38 +08:00
|
|
|
std.debug.assert(std.meta.eql(file2, error.WouldBlock));
|
|
|
|
|
2020-05-19 04:22:27 +08:00
|
|
|
dir.deleteFile(filename) catch |err| switch (err) {
|
2020-04-12 05:50:38 +08:00
|
|
|
error.FileNotFound => {},
|
|
|
|
else => return err,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
test "open file with lock twice, make sure it wasn't open at the same time" {
|
2020-05-02 11:17:15 +08:00
|
|
|
if (builtin.single_threaded) return error.SkipZigTest;
|
|
|
|
|
|
|
|
if (std.io.is_async) {
|
|
|
|
// This test starts its own threads and is not compatible with async I/O.
|
|
|
|
return error.SkipZigTest;
|
|
|
|
}
|
2020-04-12 05:50:38 +08:00
|
|
|
|
|
|
|
const filename = "file_lock_test.txt";
|
|
|
|
var contexts = [_]FileLockTestContext{
|
2020-05-19 04:22:27 +08:00
|
|
|
.{ .filename = filename, .create = true, .lock = .Exclusive },
|
|
|
|
.{ .filename = filename, .create = true, .lock = .Exclusive },
|
2020-04-12 05:50:38 +08:00
|
|
|
};
|
|
|
|
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]));
|
|
|
|
|
2020-05-19 04:22:27 +08:00
|
|
|
fs.cwd().deleteFile(filename) catch |err| switch (err) {
|
2020-04-12 05:50:38 +08:00
|
|
|
error.FileNotFound => {},
|
|
|
|
else => return err,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
test "create file, lock and read from multiple process at once" {
|
|
|
|
if (builtin.single_threaded) return error.SkipZigTest;
|
|
|
|
|
2020-05-02 11:17:15 +08:00
|
|
|
if (std.io.is_async) {
|
|
|
|
// This test starts its own threads and is not compatible with async I/O.
|
|
|
|
return error.SkipZigTest;
|
|
|
|
}
|
|
|
|
|
2020-04-12 05:50:38 +08:00
|
|
|
if (true) {
|
|
|
|
// https://github.com/ziglang/zig/issues/5006
|
|
|
|
return error.SkipZigTest;
|
|
|
|
}
|
|
|
|
|
|
|
|
const filename = "file_read_lock_test.txt";
|
|
|
|
const filedata = "Hello, world!\n";
|
|
|
|
|
2020-05-19 04:22:27 +08:00
|
|
|
try fs.cwd().writeFile(filename, filedata);
|
2020-04-12 05:50:38 +08:00
|
|
|
|
|
|
|
var contexts = [_]FileLockTestContext{
|
2020-05-19 04:22:27 +08:00
|
|
|
.{ .filename = filename, .create = false, .lock = .Shared },
|
|
|
|
.{ .filename = filename, .create = false, .lock = .Shared },
|
|
|
|
.{ .filename = filename, .create = false, .lock = .Exclusive },
|
2020-04-12 05:50:38 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2020-05-19 04:22:27 +08:00
|
|
|
fs.cwd().deleteFile(filename) catch |err| switch (err) {
|
2020-04-12 05:50:38 +08:00
|
|
|
error.FileNotFound => {},
|
|
|
|
else => return err,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-05-02 16:19:07 +08:00
|
|
|
test "open file with exclusive nonblocking lock twice (absolute paths)" {
|
Add/fix missing WASI functionality to pass libstd tests
This rather large commit adds/fixes missing WASI functionality
in `libstd` needed to pass the `libstd` tests. As such, now by
default tests targeting `wasm32-wasi` target are enabled in
`test/tests.zig` module. However, they can be disabled by passing
the `-Dskip-wasi=true` flag when invoking the `zig build test`
command. When the flag is set to `false`, i.e., when WASI tests are
included, `wasmtime` with `--dir=.` is used as the default testing
command.
Since the majority of `libstd` tests were relying on `fs.cwd()`
call to get current working directory handle wrapped in `Dir`
struct, in order to make the tests WASI-friendly, `fs.cwd()`
call was replaced with `testing.getTestDir()` function which
resolved to either `fs.cwd()` for non-WASI targets, or tries to
fetch the preopen list from the WASI runtime and extract a
preopen for '.' path.
The summary of changes introduced by this commit:
* implement `Dir.makeDir` and `Dir.openDir` targeting WASI
* implement `Dir.deleteFile` and `Dir.deleteDir` targeting WASI
* fix `os.close` and map errors in `unlinkat`
* move WASI-specific `mkdirat` and `unlinkat` from `std.fs.wasi`
to `std.os` module
* implement `lseek_{SET, CUR, END}` targeting WASI
* implement `futimens` targeting WASI
* implement `ftruncate` targeting WASI
* implement `readv`, `writev`, `pread{v}`, `pwrite{v}` targeting WASI
* make sure ANSI escape codes are _not_ used in stderr or stdout
in WASI, as WASI always sanitizes stderr, and sanitizes stdout if
fd is a TTY
* fix specifying WASI rights when opening/creating files/dirs
* tweak `AtomicFile` to be WASI-compatible
* implement `os.renameatWasi` for WASI-compliant `os.renameat` function
* implement sleep() targeting WASI
* fix `process.getEnvMap` targeting WASI
2020-05-05 23:23:49 +08:00
|
|
|
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
|
|
|
|
2020-10-13 11:05:34 +08:00
|
|
|
// TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759
|
|
|
|
if (builtin.os.tag == .freebsd) return error.SkipZigTest;
|
|
|
|
|
2020-06-21 21:18:50 +08:00
|
|
|
const allocator = testing.allocator;
|
2020-05-02 16:19:07 +08:00
|
|
|
|
|
|
|
const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"};
|
|
|
|
const filename = try fs.path.resolve(allocator, &file_paths);
|
|
|
|
defer allocator.free(filename);
|
|
|
|
|
|
|
|
const file1 = try fs.createFileAbsolute(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
|
|
|
|
|
|
|
|
const file2 = fs.createFileAbsolute(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
|
|
|
|
file1.close();
|
2020-06-21 21:18:50 +08:00
|
|
|
testing.expectError(error.WouldBlock, file2);
|
2020-05-02 16:19:07 +08:00
|
|
|
|
|
|
|
try fs.deleteFileAbsolute(filename);
|
|
|
|
}
|
|
|
|
|
2020-04-12 05:50:38 +08:00
|
|
|
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,
|
2020-05-25 08:06:56 +08:00
|
|
|
start_time: i64 = 0,
|
|
|
|
end_time: i64 = 0,
|
2020-04-12 05:50:38 +08:00
|
|
|
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) {
|
2020-05-19 04:22:27 +08:00
|
|
|
file = fs.cwd().createFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| {
|
2020-04-12 05:50:38 +08:00
|
|
|
ctx.err = err;
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
} else {
|
2020-05-19 04:22:27 +08:00
|
|
|
file = fs.cwd().openFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| {
|
2020-04-12 05:50:38 +08:00
|
|
|
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 {
|
2020-06-21 21:18:50 +08:00
|
|
|
var threads = std.ArrayList(*std.Thread).init(testing.allocator);
|
2020-04-12 05:50:38 +08:00
|
|
|
defer {
|
2020-04-12 03:06:56 +08:00
|
|
|
for (threads.items) |thread| {
|
2020-04-12 05:50:38 +08:00
|
|
|
thread.wait();
|
|
|
|
}
|
|
|
|
threads.deinit();
|
|
|
|
}
|
|
|
|
for (contexts) |*ctx, idx| {
|
|
|
|
try threads.append(try std.Thread.spawn(ctx, FileLockTestContext.run));
|
|
|
|
}
|
|
|
|
}
|