authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-11 15:25:35-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-12 15:40:59-07:00
log1c9bd1d74c357384bdd8bbf59c71190641a41a9d
treeadac86ff26e48dd0e8ed8a354ade4ae55dab749f
parent627f03af9d90e0ac33834b4dd04a3a0adc9bbcef

std.Io.net: enable all the tests

closes #18315 closes #25983 closes #31388

1 files changed, 73 insertions(+), 117 deletions(-)

lib/std/Io/net/test.zig+73-117
......@@ -5,6 +5,7 @@ const Io = std.Io;
55const net = std.Io.net;
66const mem = std.mem;
77const testing = std.testing;
8const Allocator = std.mem.Allocator;
89
910test "parse and render IP addresses at comptime" {
1011 comptime {
......@@ -84,28 +85,19 @@ test "IPv6 address parse failures" {
8485}
8586
8687test "invalid but parseable IPv6 scope ids" {
87 const io = testing.io;
88 if (builtin.os.tag != .linux and comptime !builtin.os.tag.isDarwin()) return error.SkipZigTest;
8889
89 if (builtin.os.tag != .linux and comptime !builtin.os.tag.isDarwin()) {
90 return error.SkipZigTest; // TODO
91 }
90 const io = testing.io;
9291
9392 try testing.expectError(error.InterfaceNotFound, net.IpAddress.resolveIp6(io, "ff01::fb%123s45678901234", 0));
9493}
9594
9695test "parse and render IPv4 addresses" {
97 var buffer: [18]u8 = undefined;
98 for ([_][]const u8{
99 "0.0.0.0",
100 "255.255.255.255",
101 "1.2.3.4",
102 "123.255.0.91",
103 "127.0.0.1",
104 }) |ip| {
105 const addr = net.IpAddress.parseIp4(ip, 0) catch unreachable;
106 var newIp = std.fmt.bufPrint(buffer[0..], "{f}", .{addr}) catch unreachable;
107 try testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2]));
108 }
96 try testIp4ParseAndRender("0.0.0.0");
97 try testIp4ParseAndRender("255.255.255.255");
98 try testIp4ParseAndRender("1.2.3.4");
99 try testIp4ParseAndRender("123.255.0.91");
100 try testIp4ParseAndRender("127.0.0.1");
109101
110102 try testing.expectError(error.Overflow, net.IpAddress.parseIp4("256.0.0.1", 0));
111103 try testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp4("x.0.0.1", 0));
......@@ -115,9 +107,15 @@ test "parse and render IPv4 addresses" {
115107 try testing.expectError(error.NonCanonical, net.IpAddress.parseIp4("127.01.0.1", 0));
116108}
117109
118test "resolve DNS" {
119 if (builtin.os.tag == .wasi) return error.SkipZigTest;
110fn testIp4ParseAndRender(text: []const u8) !void {
111 var buffer: [18]u8 = undefined;
112 const addr = try net.IpAddress.parseIp4(text, 0);
113 const rendered = try std.fmt.bufPrint(&buffer, "{f}", .{addr});
114 const without_port = rendered[0 .. rendered.len - 2];
115 try testing.expectEqualStrings(text, without_port);
116}
120117
118test "resolve DNS" {
121119 const io = testing.io;
122120
123121 // Resolve localhost, this should not fail.
......@@ -129,10 +127,13 @@ test "resolve DNS" {
129127 var results_buffer: [32]net.HostName.LookupResult = undefined;
130128 var results: Io.Queue(net.HostName.LookupResult) = .init(&results_buffer);
131129
132 try net.HostName.lookup(try .init("localhost"), io, &results, .{
130 net.HostName.lookup(try .init("localhost"), io, &results, .{
133131 .port = 80,
134132 .canonical_name_buffer = &canonical_name_buffer,
135 });
133 }) catch |err| switch (err) {
134 error.NetworkDown => return error.SkipZigTest,
135 else => |e| return e,
136 };
136137
137138 var addresses_found: usize = 0;
138139
......@@ -177,17 +178,16 @@ test "resolve DNS" {
177178}
178179
179180test "listen on a port, send bytes, receive bytes" {
180 if (true) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/31388
181 if (builtin.single_threaded) return error.SkipZigTest;
182 if (builtin.os.tag == .wasi) return error.SkipZigTest;
183
184181 const io = testing.io;
185182
186183 // Try only the IPv4 variant as some CI builders have no IPv6 localhost
187184 // configured.
188185 const localhost: net.IpAddress = .{ .ip4 = .loopback(0) };
189186
190 var server = try localhost.listen(io, .{});
187 var server = localhost.listen(io, .{}) catch |err| switch (err) {
188 error.NetworkDown => return error.SkipZigTest,
189 else => |e| return e,
190 };
191191 defer server.deinit(io);
192192
193193 const S = struct {
......@@ -200,89 +200,54 @@ test "listen on a port, send bytes, receive bytes" {
200200 }
201201 };
202202
203 const t = try std.Thread.spawn(.{}, S.clientFn, .{server.socket.address});
204 defer t.join();
203 var client_task = io.concurrent(S.clientFn, .{server.socket.address}) catch |err| switch (err) {
204 error.ConcurrencyUnavailable => return error.SkipZigTest,
205 };
206 defer client_task.cancel(io) catch {};
205207
206208 var stream = try server.accept(io);
207209 defer stream.close(io);
210
208211 var buf: [16]u8 = undefined;
209212 var stream_reader = stream.reader(io, &.{});
210213 const n = try stream_reader.interface.readSliceShort(&buf);
211214
212215 try testing.expectEqual(@as(usize, 12), n);
213 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);
216 try testing.expectEqualStrings("Hello world!", buf[0..n]);
217
218 try client_task.await(io);
214219}
215220
216221test "listen on an in use port" {
217 if (builtin.os.tag != .linux and comptime !builtin.os.tag.isDarwin() and builtin.os.tag != .windows) {
218 // TODO build abstractions for other operating systems
219 return error.SkipZigTest;
220 }
221
222222 const io = testing.io;
223223
224224 const localhost: net.IpAddress = .{ .ip4 = .loopback(0) };
225225
226 var server1 = try localhost.listen(io, .{ .reuse_address = true });
226 var server1 = localhost.listen(io, .{ .reuse_address = true }) catch |err| switch (err) {
227 error.NetworkDown => return error.SkipZigTest,
228 else => |e| return e,
229 };
227230 defer server1.deinit(io);
228231
229232 var server2 = try server1.socket.address.listen(io, .{ .reuse_address = true });
230233 defer server2.deinit(io);
231234}
232235
233fn testClientToHost(allocator: mem.Allocator, name: []const u8, port: u16) anyerror!void {
234 if (builtin.os.tag == .wasi) return error.SkipZigTest;
235
236 const io = testing.io;
237
238 const connection = try net.tcpConnectToHost(allocator, name, port);
239 defer connection.close(io);
240
241 var buf: [100]u8 = undefined;
242 const len = try connection.read(&buf);
243 const msg = buf[0..len];
244 try testing.expect(mem.eql(u8, msg, "hello from server\n"));
245}
246
247fn testClient(addr: net.IpAddress) anyerror!void {
248 if (builtin.os.tag == .wasi) return error.SkipZigTest;
249
250 const io = testing.io;
251
252 const socket_file = try net.tcpConnectToAddress(addr);
253 defer socket_file.close(io);
254
255 var buf: [100]u8 = undefined;
256 const len = try socket_file.read(&buf);
257 const msg = buf[0..len];
258 try testing.expect(mem.eql(u8, msg, "hello from server\n"));
259}
260
261fn testServer(server: *net.Server) anyerror!void {
262 if (builtin.os.tag == .wasi) return error.SkipZigTest;
263
264 const io = testing.io;
265
266 var stream = try server.accept(io);
267 var writer = stream.writer(io, &.{});
268 try writer.interface.print("hello from server\n", .{});
269}
270
271236test "listen on a unix socket, send bytes, receive bytes" {
272 if (builtin.single_threaded) return error.SkipZigTest;
273 if (!net.has_unix_sockets) return error.SkipZigTest;
274 if (builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25983
275 if (builtin.cpu.arch == .mipsel) return error.SkipZigTest; // TODO
276
277237 const io = testing.io;
238 const gpa = testing.allocator;
278239
279 const socket_path = try generateFileName(io, "socket.unix");
280 defer testing.allocator.free(socket_path);
240 const socket_path = try generateFileName(gpa, io, "socket.unix");
241 defer gpa.free(socket_path);
281242
282243 const socket_addr = try net.UnixAddress.init(socket_path);
283244 defer Io.Dir.cwd().deleteFile(io, socket_path) catch {};
284245
285 var server = try socket_addr.listen(io, .{});
246 var server = socket_addr.listen(io, .{}) catch |err| switch (err) {
247 error.AddressFamilyUnsupported => return error.SkipZigTest,
248 error.NetworkDown => return error.SkipZigTest,
249 else => |e| return e,
250 };
286251 defer server.socket.close(io);
287252
288253 const S = struct {
......@@ -296,59 +261,32 @@ test "listen on a unix socket, send bytes, receive bytes" {
296261 }
297262 };
298263
299 const t = try std.Thread.spawn(.{}, S.clientFn, .{socket_path});
300 defer t.join();
264 var client_task = io.concurrent(S.clientFn, .{socket_path}) catch |err| switch (err) {
265 error.ConcurrencyUnavailable => return error.SkipZigTest,
266 };
267 defer client_task.cancel(io) catch {};
301268
302269 var stream = try server.accept(io);
303270 defer stream.close(io);
271
304272 var buf: [16]u8 = undefined;
305273 var stream_reader = stream.reader(io, &.{});
306274 const n = try stream_reader.interface.readSliceShort(&buf);
307275
308276 try testing.expectEqual(@as(usize, 12), n);
309 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);
277 try testing.expectEqualStrings("Hello world!", buf[0..n]);
278
279 try client_task.await(io);
310280}
311281
312fn generateFileName(io: Io, base_name: []const u8) ![]const u8 {
282fn generateFileName(gpa: Allocator, io: Io, base_name: []const u8) ![]const u8 {
313283 const random_bytes_count = 12;
314284 const sub_path_len = comptime std.base64.url_safe.Encoder.calcSize(random_bytes_count);
315285 var random_bytes: [12]u8 = undefined;
316286 io.random(&random_bytes);
317287 var sub_path: [sub_path_len]u8 = undefined;
318288 _ = std.base64.url_safe.Encoder.encode(&sub_path, &random_bytes);
319 return std.fmt.allocPrint(testing.allocator, "{s}-{s}", .{ sub_path[0..], base_name });
320}
321
322test "non-blocking tcp server" {
323 if (builtin.os.tag == .wasi) return error.SkipZigTest;
324 if (true) {
325 // https://github.com/ziglang/zig/issues/18315
326 return error.SkipZigTest;
327 }
328
329 const io = testing.io;
330
331 const localhost: net.IpAddress = .{ .ip4 = .loopback(0) };
332 var server = localhost.listen(io, .{ .force_nonblocking = true });
333 defer server.deinit(io);
334
335 const accept_err = server.accept(io);
336 try testing.expectError(error.WouldBlock, accept_err);
337
338 const socket_file = try net.tcpConnectToAddress(server.socket.address);
339 defer socket_file.close(io);
340
341 var stream = try server.accept(io);
342 defer stream.close(io);
343 var writer = stream.writer(io, .{});
344 try writer.interface.print("hello from server\n", .{});
345
346 var buf: [100]u8 = undefined;
347 const len = try socket_file.read(&buf);
348 const msg = buf[0..len];
349 try testing.expect(mem.eql(u8, msg, "hello from server\n"));
350
351 try stream.shutdown(io, .both);
289 return std.fmt.allocPrint(gpa, "{s}-{s}", .{ sub_path[0..], base_name });
352290}
353291
354292test "decompress compressed DNS name" {
......@@ -400,3 +338,21 @@ test "decompress compressed DNS name" {
400338 try testing.expectEqual(packet.len - cname_data_index, n_consumed);
401339 try testing.expectEqualStrings("target.ziglang.org", result.bytes);
402340}
341
342test "cancel accept" {
343 const io = testing.io;
344 const localhost: net.IpAddress = .{ .ip4 = .loopback(0) };
345
346 var server = localhost.listen(io, .{}) catch |err| switch (err) {
347 error.NetworkDown => return error.SkipZigTest,
348 else => |e| return e,
349 };
350 defer server.deinit(io);
351
352 var accept = io.concurrent(std.Io.net.Server.accept, .{ &server, io }) catch |err| switch (err) {
353 error.ConcurrencyUnavailable => return error.SkipZigTest,
354 };
355 defer if (accept.cancel(io)) |stream| stream.close(io) else |_| {};
356
357 try io.sleep(.fromNanoseconds(1), .awake);
358}