authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-11 17:50:38-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-11 17:50:38-04:00
log4ceaa0595a02b3695e37bd999bd58efb7c1e0550
tree23f180cbce9c1efdc511cd903bf3ca6256434977
parentc236cbff39de996cf862dfedbc92584ac09414d8

move fs tests to separate file; disable flaky test

See #5006

2 files changed, 170 insertions(+), 162 deletions(-)

lib/std/fs.zig+1-162
......@@ -1584,13 +1584,6 @@ pub fn openSelfExe() OpenSelfExeError!File {
15841584 return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, .{});
15851585}
15861586
1587test "openSelfExe" {
1588 switch (builtin.os.tag) {
1589 .linux, .macosx, .ios, .windows, .freebsd, .dragonfly => (try openSelfExe()).close(),
1590 else => return error.SkipZigTest, // Unsupported OS.
1591 }
1592}
1593
15941587pub const SelfExePathError = os.ReadLinkError || os.SysCtlError;
15951588
15961589/// `selfExePath` except allocates the result on the heap.
......@@ -1678,163 +1671,9 @@ test "" {
16781671 _ = copyFileAbsolute;
16791672 _ = updateFileAbsolute;
16801673 _ = Dir.copyFile;
1674 _ = @import("fs/test.zig");
16811675 _ = @import("fs/path.zig");
16821676 _ = @import("fs/file.zig");
16831677 _ = @import("fs/get_app_data_dir.zig");
16841678 _ = @import("fs/watch.zig");
16851679}
1686
1687const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.millisecond;
1688
1689test "open file with exclusive nonblocking lock twice" {
1690 const dir = cwd();
1691 const filename = "file_nonblocking_lock_test.txt";
1692
1693 const file1 = try dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
1694 defer file1.close();
1695
1696 const file2 = dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
1697 std.debug.assert(std.meta.eql(file2, error.WouldBlock));
1698
1699 dir.deleteFile(filename) catch |err| switch (err) {
1700 error.FileNotFound => {},
1701 else => return err,
1702 };
1703}
1704
1705test "open file with lock twice, make sure it wasn't open at the same time" {
1706 if (builtin.single_threaded) return;
1707
1708 const filename = "file_lock_test.txt";
1709
1710 var contexts = [_]FileLockTestContext{
1711 .{ .filename = filename, .create = true, .lock = .Exclusive },
1712 .{ .filename = filename, .create = true, .lock = .Exclusive },
1713 };
1714 try run_lock_file_test(&contexts);
1715
1716 // Check for an error
1717 var was_error = false;
1718 for (contexts) |context, idx| {
1719 if (context.err) |err| {
1720 was_error = true;
1721 std.debug.warn("\nError in context {}: {}\n", .{ idx, err });
1722 }
1723 }
1724 if (was_error) builtin.panic("There was an error in contexts", null);
1725
1726 std.debug.assert(!contexts[0].overlaps(&contexts[1]));
1727
1728 cwd().deleteFile(filename) catch |err| switch (err) {
1729 error.FileNotFound => {},
1730 else => return err,
1731 };
1732}
1733
1734test "create file, lock and read from multiple process at once" {
1735 if (builtin.single_threaded) return;
1736
1737 const filename = "file_read_lock_test.txt";
1738 const filedata = "Hello, world!\n";
1739
1740 try std.fs.cwd().writeFile(filename, filedata);
1741
1742 var contexts = [_]FileLockTestContext{
1743 .{ .filename = filename, .create = false, .lock = .Shared },
1744 .{ .filename = filename, .create = false, .lock = .Shared },
1745 .{ .filename = filename, .create = false, .lock = .Exclusive },
1746 };
1747
1748 try run_lock_file_test(&contexts);
1749
1750 var was_error = false;
1751 for (contexts) |context, idx| {
1752 if (context.err) |err| {
1753 was_error = true;
1754 std.debug.warn("\nError in context {}: {}\n", .{ idx, err });
1755 }
1756 }
1757 if (was_error) builtin.panic("There was an error in contexts", null);
1758
1759 std.debug.assert(contexts[0].overlaps(&contexts[1]));
1760 std.debug.assert(!contexts[2].overlaps(&contexts[0]));
1761 std.debug.assert(!contexts[2].overlaps(&contexts[1]));
1762 if (contexts[0].bytes_read.? != filedata.len) {
1763 std.debug.warn("\n bytes_read: {}, expected: {} \n", .{ contexts[0].bytes_read, filedata.len });
1764 }
1765 std.debug.assert(contexts[0].bytes_read.? == filedata.len);
1766 std.debug.assert(contexts[1].bytes_read.? == filedata.len);
1767
1768 cwd().deleteFile(filename) catch |err| switch (err) {
1769 error.FileNotFound => {},
1770 else => return err,
1771 };
1772}
1773
1774const FileLockTestContext = struct {
1775 filename: []const u8,
1776 pid: if (builtin.os.tag == .windows) ?void else ?std.os.pid_t = null,
1777
1778 // use file.createFile
1779 create: bool,
1780 // the type of lock to use
1781 lock: File.Lock,
1782
1783 // Output variables
1784 err: ?(File.OpenError || std.os.ReadError) = null,
1785 start_time: u64 = 0,
1786 end_time: u64 = 0,
1787 bytes_read: ?usize = null,
1788
1789 fn overlaps(self: *const @This(), other: *const @This()) bool {
1790 return (self.start_time < other.end_time) and (self.end_time > other.start_time);
1791 }
1792
1793 fn run(ctx: *@This()) void {
1794 var file: File = undefined;
1795 if (ctx.create) {
1796 file = cwd().createFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| {
1797 ctx.err = err;
1798 return;
1799 };
1800 } else {
1801 file = cwd().openFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| {
1802 ctx.err = err;
1803 return;
1804 };
1805 }
1806 defer file.close();
1807
1808 ctx.start_time = std.time.milliTimestamp();
1809
1810 if (!ctx.create) {
1811 var buffer: [100]u8 = undefined;
1812 ctx.bytes_read = 0;
1813 while (true) {
1814 const amt = file.read(buffer[0..]) catch |err| {
1815 ctx.err = err;
1816 return;
1817 };
1818 if (amt == 0) break;
1819 ctx.bytes_read.? += amt;
1820 }
1821 }
1822
1823 std.time.sleep(FILE_LOCK_TEST_SLEEP_TIME);
1824
1825 ctx.end_time = std.time.milliTimestamp();
1826 }
1827};
1828
1829fn run_lock_file_test(contexts: []FileLockTestContext) !void {
1830 var threads = std.ArrayList(*std.Thread).init(std.testing.allocator);
1831 defer {
1832 for (threads.toSlice()) |thread| {
1833 thread.wait();
1834 }
1835 threads.deinit();
1836 }
1837 for (contexts) |*ctx, idx| {
1838 try threads.append(try std.Thread.spawn(ctx, FileLockTestContext.run));
1839 }
1840}
lib/std/fs/test.zig created+169
......@@ -0,0 +1,169 @@
1const std = @import("../std.zig");
2const builtin = std.builtin;
3const fs = std.fs;
4const File = std.fs.File;
5
6test "openSelfExe" {
7 const self_exe_file = try std.fs.openSelfExe();
8 self_exe_file.close();
9}
10
11const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.millisecond;
12
13test "open file with exclusive nonblocking lock twice" {
14 const dir = fs.cwd();
15 const filename = "file_nonblocking_lock_test.txt";
16
17 const file1 = try dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
18 defer file1.close();
19
20 const file2 = dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
21 std.debug.assert(std.meta.eql(file2, error.WouldBlock));
22
23 dir.deleteFile(filename) catch |err| switch (err) {
24 error.FileNotFound => {},
25 else => return err,
26 };
27}
28
29test "open file with lock twice, make sure it wasn't open at the same time" {
30 if (builtin.single_threaded) return;
31
32 const filename = "file_lock_test.txt";
33
34 var contexts = [_]FileLockTestContext{
35 .{ .filename = filename, .create = true, .lock = .Exclusive },
36 .{ .filename = filename, .create = true, .lock = .Exclusive },
37 };
38 try run_lock_file_test(&contexts);
39
40 // Check for an error
41 var was_error = false;
42 for (contexts) |context, idx| {
43 if (context.err) |err| {
44 was_error = true;
45 std.debug.warn("\nError in context {}: {}\n", .{ idx, err });
46 }
47 }
48 if (was_error) builtin.panic("There was an error in contexts", null);
49
50 std.debug.assert(!contexts[0].overlaps(&contexts[1]));
51
52 fs.cwd().deleteFile(filename) catch |err| switch (err) {
53 error.FileNotFound => {},
54 else => return err,
55 };
56}
57
58test "create file, lock and read from multiple process at once" {
59 if (builtin.single_threaded) return error.SkipZigTest;
60
61 if (true) {
62 // https://github.com/ziglang/zig/issues/5006
63 return error.SkipZigTest;
64 }
65
66 const filename = "file_read_lock_test.txt";
67 const filedata = "Hello, world!\n";
68
69 try fs.cwd().writeFile(filename, filedata);
70
71 var contexts = [_]FileLockTestContext{
72 .{ .filename = filename, .create = false, .lock = .Shared },
73 .{ .filename = filename, .create = false, .lock = .Shared },
74 .{ .filename = filename, .create = false, .lock = .Exclusive },
75 };
76
77 try run_lock_file_test(&contexts);
78
79 var was_error = false;
80 for (contexts) |context, idx| {
81 if (context.err) |err| {
82 was_error = true;
83 std.debug.warn("\nError in context {}: {}\n", .{ idx, err });
84 }
85 }
86 if (was_error) builtin.panic("There was an error in contexts", null);
87
88 std.debug.assert(contexts[0].overlaps(&contexts[1]));
89 std.debug.assert(!contexts[2].overlaps(&contexts[0]));
90 std.debug.assert(!contexts[2].overlaps(&contexts[1]));
91 if (contexts[0].bytes_read.? != filedata.len) {
92 std.debug.warn("\n bytes_read: {}, expected: {} \n", .{ contexts[0].bytes_read, filedata.len });
93 }
94 std.debug.assert(contexts[0].bytes_read.? == filedata.len);
95 std.debug.assert(contexts[1].bytes_read.? == filedata.len);
96
97 fs.cwd().deleteFile(filename) catch |err| switch (err) {
98 error.FileNotFound => {},
99 else => return err,
100 };
101}
102
103const FileLockTestContext = struct {
104 filename: []const u8,
105 pid: if (builtin.os.tag == .windows) ?void else ?std.os.pid_t = null,
106
107 // use file.createFile
108 create: bool,
109 // the type of lock to use
110 lock: File.Lock,
111
112 // Output variables
113 err: ?(File.OpenError || std.os.ReadError) = null,
114 start_time: u64 = 0,
115 end_time: u64 = 0,
116 bytes_read: ?usize = null,
117
118 fn overlaps(self: *const @This(), other: *const @This()) bool {
119 return (self.start_time < other.end_time) and (self.end_time > other.start_time);
120 }
121
122 fn run(ctx: *@This()) void {
123 var file: File = undefined;
124 if (ctx.create) {
125 file = fs.cwd().createFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| {
126 ctx.err = err;
127 return;
128 };
129 } else {
130 file = fs.cwd().openFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| {
131 ctx.err = err;
132 return;
133 };
134 }
135 defer file.close();
136
137 ctx.start_time = std.time.milliTimestamp();
138
139 if (!ctx.create) {
140 var buffer: [100]u8 = undefined;
141 ctx.bytes_read = 0;
142 while (true) {
143 const amt = file.read(buffer[0..]) catch |err| {
144 ctx.err = err;
145 return;
146 };
147 if (amt == 0) break;
148 ctx.bytes_read.? += amt;
149 }
150 }
151
152 std.time.sleep(FILE_LOCK_TEST_SLEEP_TIME);
153
154 ctx.end_time = std.time.milliTimestamp();
155 }
156};
157
158fn run_lock_file_test(contexts: []FileLockTestContext) !void {
159 var threads = std.ArrayList(*std.Thread).init(std.testing.allocator);
160 defer {
161 for (threads.toSlice()) |thread| {
162 thread.wait();
163 }
164 threads.deinit();
165 }
166 for (contexts) |*ctx, idx| {
167 try threads.append(try std.Thread.spawn(ctx, FileLockTestContext.run));
168 }
169}