master
1const builtin = @import("builtin");
2const native_endian = builtin.cpu.arch.endian();
3
4const std = @import("std");
5const Io = std.Io;
6const testing = std.testing;
7const expect = std.testing.expect;
8const expectEqual = std.testing.expectEqual;
9const expectError = std.testing.expectError;
10const DefaultPrng = std.Random.DefaultPrng;
11const mem = std.mem;
12const fs = std.fs;
13const File = std.fs.File;
14const assert = std.debug.assert;
15
16const tmpDir = std.testing.tmpDir;
17
18test "write a file, read it, then delete it" {
19 const io = testing.io;
20
21 var tmp = tmpDir(.{});
22 defer tmp.cleanup();
23
24 var data: [1024]u8 = undefined;
25 var prng = DefaultPrng.init(testing.random_seed);
26 const random = prng.random();
27 random.bytes(data[0..]);
28 const tmp_file_name = "temp_test_file.txt";
29 {
30 var file = try tmp.dir.createFile(tmp_file_name, .{});
31 defer file.close();
32
33 var file_writer = file.writer(&.{});
34 const st = &file_writer.interface;
35 try st.print("begin", .{});
36 try st.writeAll(&data);
37 try st.print("end", .{});
38 try st.flush();
39 }
40
41 {
42 // Make sure the exclusive flag is honored.
43 try expectError(File.OpenError.PathAlreadyExists, tmp.dir.createFile(tmp_file_name, .{ .exclusive = true }));
44 }
45
46 {
47 var file = try tmp.dir.openFile(tmp_file_name, .{});
48 defer file.close();
49
50 const file_size = try file.getEndPos();
51 const expected_file_size: u64 = "begin".len + data.len + "end".len;
52 try expectEqual(expected_file_size, file_size);
53
54 var file_buffer: [1024]u8 = undefined;
55 var file_reader = file.reader(io, &file_buffer);
56 const contents = try file_reader.interface.allocRemaining(testing.allocator, .limited(2 * 1024));
57 defer testing.allocator.free(contents);
58
59 try expect(mem.eql(u8, contents[0.."begin".len], "begin"));
60 try expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], &data));
61 try expect(mem.eql(u8, contents[contents.len - "end".len ..], "end"));
62 }
63 try tmp.dir.deleteFile(tmp_file_name);
64}
65
66test "File seek ops" {
67 var tmp = tmpDir(.{});
68 defer tmp.cleanup();
69
70 const tmp_file_name = "temp_test_file.txt";
71 var file = try tmp.dir.createFile(tmp_file_name, .{});
72 defer file.close();
73
74 try file.writeAll(&([_]u8{0x55} ** 8192));
75
76 // Seek to the end
77 try file.seekFromEnd(0);
78 try expect((try file.getPos()) == try file.getEndPos());
79 // Negative delta
80 try file.seekBy(-4096);
81 try expect((try file.getPos()) == 4096);
82 // Positive delta
83 try file.seekBy(10);
84 try expect((try file.getPos()) == 4106);
85 // Absolute position
86 try file.seekTo(1234);
87 try expect((try file.getPos()) == 1234);
88}
89
90test "setEndPos" {
91 var tmp = tmpDir(.{});
92 defer tmp.cleanup();
93
94 const tmp_file_name = "temp_test_file.txt";
95 var file = try tmp.dir.createFile(tmp_file_name, .{});
96 defer file.close();
97
98 // Verify that the file size changes and the file offset is not moved
99 try expect((try file.getEndPos()) == 0);
100 try expect((try file.getPos()) == 0);
101 try file.setEndPos(8192);
102 try expect((try file.getEndPos()) == 8192);
103 try expect((try file.getPos()) == 0);
104 try file.seekTo(100);
105 try file.setEndPos(4096);
106 try expect((try file.getEndPos()) == 4096);
107 try expect((try file.getPos()) == 100);
108 try file.setEndPos(0);
109 try expect((try file.getEndPos()) == 0);
110 try expect((try file.getPos()) == 100);
111}
112
113test "updateTimes" {
114 var tmp = tmpDir(.{});
115 defer tmp.cleanup();
116
117 const tmp_file_name = "just_a_temporary_file.txt";
118 var file = try tmp.dir.createFile(tmp_file_name, .{ .read = true });
119 defer file.close();
120
121 const stat_old = try file.stat();
122 // Set atime and mtime to 5s before
123 try file.updateTimes(
124 stat_old.atime.subDuration(.fromSeconds(5)),
125 stat_old.mtime.subDuration(.fromSeconds(5)),
126 );
127 const stat_new = try file.stat();
128 try expect(stat_new.atime.nanoseconds < stat_old.atime.nanoseconds);
129 try expect(stat_new.mtime.nanoseconds < stat_old.mtime.nanoseconds);
130}
131
132test "Group" {
133 const io = testing.io;
134
135 var group: Io.Group = .init;
136 var results: [2]usize = undefined;
137
138 group.async(io, count, .{ 1, 10, &results[0] });
139 group.async(io, count, .{ 20, 30, &results[1] });
140
141 group.wait(io);
142
143 try testing.expectEqualSlices(usize, &.{ 45, 245 }, &results);
144}
145
146fn count(a: usize, b: usize, result: *usize) void {
147 var sum: usize = 0;
148 for (a..b) |i| {
149 sum += i;
150 }
151 result.* = sum;
152}
153
154test "Group cancellation" {
155 const io = testing.io;
156
157 var group: Io.Group = .init;
158 var results: [2]usize = undefined;
159
160 group.async(io, sleep, .{ io, &results[0] });
161 group.async(io, sleep, .{ io, &results[1] });
162
163 group.cancel(io);
164
165 try testing.expectEqualSlices(usize, &.{ 1, 1 }, &results);
166}
167
168fn sleep(io: Io, result: *usize) void {
169 // TODO when cancellation race bug is fixed, make this timeout much longer so that
170 // it causes the unit test to be failed if not canceled.
171 io.sleep(.fromMilliseconds(1), .awake) catch {};
172 result.* = 1;
173}
174
175test "Group concurrent" {
176 const io = testing.io;
177
178 var group: Io.Group = .init;
179 defer group.cancel(io);
180 var results: [2]usize = undefined;
181
182 group.concurrent(io, count, .{ 1, 10, &results[0] }) catch |err| switch (err) {
183 error.ConcurrencyUnavailable => {
184 try testing.expect(builtin.single_threaded);
185 return;
186 },
187 };
188
189 group.concurrent(io, count, .{ 20, 30, &results[1] }) catch |err| switch (err) {
190 error.ConcurrencyUnavailable => {
191 try testing.expect(builtin.single_threaded);
192 return;
193 },
194 };
195
196 group.wait(io);
197
198 try testing.expectEqualSlices(usize, &.{ 45, 245 }, &results);
199}
200
201test "select" {
202 const io = testing.io;
203
204 var queue: Io.Queue(u8) = .init(&.{});
205
206 var get_a = io.concurrent(Io.Queue(u8).getOne, .{ &queue, io }) catch |err| switch (err) {
207 error.ConcurrencyUnavailable => {
208 try testing.expect(builtin.single_threaded);
209 return;
210 },
211 };
212 defer if (get_a.cancel(io)) |_| {} else |_| @panic("fail");
213
214 var get_b = try io.concurrent(Io.Queue(u8).getOne, .{ &queue, io });
215 defer if (get_b.cancel(io)) |_| {} else |_| @panic("fail");
216
217 var timeout = io.async(Io.sleep, .{ io, .fromMilliseconds(1), .awake });
218 defer timeout.cancel(io) catch {};
219
220 switch (try io.select(.{
221 .get_a = &get_a,
222 .get_b = &get_b,
223 .timeout = &timeout,
224 })) {
225 .get_a => return error.TestFailure,
226 .get_b => return error.TestFailure,
227 .timeout => {
228 // Unblock the queues to avoid making this unit test depend on
229 // cancellation.
230 queue.putOneUncancelable(io, 1);
231 queue.putOneUncancelable(io, 1);
232 try testing.expectEqual(1, try get_a.await(io));
233 try testing.expectEqual(1, try get_b.await(io));
234 },
235 }
236}
237
238fn testQueue(comptime len: usize) !void {
239 const io = testing.io;
240 var buf: [len]usize = undefined;
241 var queue: Io.Queue(usize) = .init(&buf);
242 var begin: usize = 0;
243 for (1..len + 1) |n| {
244 const end = begin + n;
245 for (begin..end) |i| try queue.putOne(io, i);
246 for (begin..end) |i| try expect(try queue.getOne(io) == i);
247 begin = end;
248 }
249}
250
251test "Queue" {
252 try testQueue(1);
253 try testQueue(2);
254 try testQueue(3);
255 try testQueue(4);
256 try testQueue(5);
257}