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;...@@ -5,6 +5,7 @@ const Io = std.Io;
5const net = std.Io.net;5const net = std.Io.net;
6const mem = std.mem;6const mem = std.mem;
7const testing = std.testing;7const testing = std.testing;
8const Allocator = std.mem.Allocator;
89
9test "parse and render IP addresses at comptime" {10test "parse and render IP addresses at comptime" {
10 comptime {11 comptime {
...@@ -84,28 +85,19 @@ test "IPv6 address parse failures" {...@@ -84,28 +85,19 @@ test "IPv6 address parse failures" {
84}85}
8586
86test "invalid but parseable IPv6 scope ids" {87test "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 const io = testing.io;
90 return error.SkipZigTest; // TODO
91 }
9291
93 try testing.expectError(error.InterfaceNotFound, net.IpAddress.resolveIp6(io, "ff01::fb%123s45678901234", 0));92 try testing.expectError(error.InterfaceNotFound, net.IpAddress.resolveIp6(io, "ff01::fb%123s45678901234", 0));
94}93}
9594
96test "parse and render IPv4 addresses" {95test "parse and render IPv4 addresses" {
97 var buffer: [18]u8 = undefined;96 try testIp4ParseAndRender("0.0.0.0");
98 for ([_][]const u8{97 try testIp4ParseAndRender("255.255.255.255");
99 "0.0.0.0",98 try testIp4ParseAndRender("1.2.3.4");
100 "255.255.255.255",99 try testIp4ParseAndRender("123.255.0.91");
101 "1.2.3.4",100 try testIp4ParseAndRender("127.0.0.1");
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 }
109101
110 try testing.expectError(error.Overflow, net.IpAddress.parseIp4("256.0.0.1", 0));102 try testing.expectError(error.Overflow, net.IpAddress.parseIp4("256.0.0.1", 0));
111 try testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp4("x.0.0.1", 0));103 try testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp4("x.0.0.1", 0));
...@@ -115,9 +107,15 @@ test "parse and render IPv4 addresses" {...@@ -115,9 +107,15 @@ test "parse and render IPv4 addresses" {
115 try testing.expectError(error.NonCanonical, net.IpAddress.parseIp4("127.01.0.1", 0));107 try testing.expectError(error.NonCanonical, net.IpAddress.parseIp4("127.01.0.1", 0));
116}108}
117109
118test "resolve DNS" {110fn testIp4ParseAndRender(text: []const u8) !void {
119 if (builtin.os.tag == .wasi) return error.SkipZigTest;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" {
121 const io = testing.io;119 const io = testing.io;
122120
123 // Resolve localhost, this should not fail.121 // Resolve localhost, this should not fail.
...@@ -129,10 +127,13 @@ test "resolve DNS" {...@@ -129,10 +127,13 @@ test "resolve DNS" {
129 var results_buffer: [32]net.HostName.LookupResult = undefined;127 var results_buffer: [32]net.HostName.LookupResult = undefined;
130 var results: Io.Queue(net.HostName.LookupResult) = .init(&results_buffer);128 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, .{
133 .port = 80,131 .port = 80,
134 .canonical_name_buffer = &canonical_name_buffer,132 .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
137 var addresses_found: usize = 0;138 var addresses_found: usize = 0;
138139
...@@ -177,17 +178,16 @@ test "resolve DNS" {...@@ -177,17 +178,16 @@ test "resolve DNS" {
177}178}
178179
179test "listen on a port, send bytes, receive bytes" {180test "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
184 const io = testing.io;181 const io = testing.io;
185182
186 // Try only the IPv4 variant as some CI builders have no IPv6 localhost183 // Try only the IPv4 variant as some CI builders have no IPv6 localhost
187 // configured.184 // configured.
188 const localhost: net.IpAddress = .{ .ip4 = .loopback(0) };185 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 };
191 defer server.deinit(io);191 defer server.deinit(io);
192192
193 const S = struct {193 const S = struct {
...@@ -200,89 +200,54 @@ test "listen on a port, send bytes, receive bytes" {...@@ -200,89 +200,54 @@ test "listen on a port, send bytes, receive bytes" {
200 }200 }
201 };201 };
202202
203 const t = try std.Thread.spawn(.{}, S.clientFn, .{server.socket.address});203 var client_task = io.concurrent(S.clientFn, .{server.socket.address}) catch |err| switch (err) {
204 defer t.join();204 error.ConcurrencyUnavailable => return error.SkipZigTest,
205 };
206 defer client_task.cancel(io) catch {};
205207
206 var stream = try server.accept(io);208 var stream = try server.accept(io);
207 defer stream.close(io);209 defer stream.close(io);
210
208 var buf: [16]u8 = undefined;211 var buf: [16]u8 = undefined;
209 var stream_reader = stream.reader(io, &.{});212 var stream_reader = stream.reader(io, &.{});
210 const n = try stream_reader.interface.readSliceShort(&buf);213 const n = try stream_reader.interface.readSliceShort(&buf);
211214
212 try testing.expectEqual(@as(usize, 12), n);215 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);
214}219}
215220
216test "listen on an in use port" {221test "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
222 const io = testing.io;222 const io = testing.io;
223223
224 const localhost: net.IpAddress = .{ .ip4 = .loopback(0) };224 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 };
227 defer server1.deinit(io);230 defer server1.deinit(io);
228231
229 var server2 = try server1.socket.address.listen(io, .{ .reuse_address = true });232 var server2 = try server1.socket.address.listen(io, .{ .reuse_address = true });
230 defer server2.deinit(io);233 defer server2.deinit(io);
231}234}
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
271test "listen on a unix socket, send bytes, receive bytes" {236test "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
277 const io = testing.io;237 const io = testing.io;
238 const gpa = testing.allocator;
278239
279 const socket_path = try generateFileName(io, "socket.unix");240 const socket_path = try generateFileName(gpa, io, "socket.unix");
280 defer testing.allocator.free(socket_path);241 defer gpa.free(socket_path);
281242
282 const socket_addr = try net.UnixAddress.init(socket_path);243 const socket_addr = try net.UnixAddress.init(socket_path);
283 defer Io.Dir.cwd().deleteFile(io, socket_path) catch {};244 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 };
286 defer server.socket.close(io);251 defer server.socket.close(io);
287252
288 const S = struct {253 const S = struct {
...@@ -296,59 +261,32 @@ test "listen on a unix socket, send bytes, receive bytes" {...@@ -296,59 +261,32 @@ test "listen on a unix socket, send bytes, receive bytes" {
296 }261 }
297 };262 };
298263
299 const t = try std.Thread.spawn(.{}, S.clientFn, .{socket_path});264 var client_task = io.concurrent(S.clientFn, .{socket_path}) catch |err| switch (err) {
300 defer t.join();265 error.ConcurrencyUnavailable => return error.SkipZigTest,
266 };
267 defer client_task.cancel(io) catch {};
301268
302 var stream = try server.accept(io);269 var stream = try server.accept(io);
303 defer stream.close(io);270 defer stream.close(io);
271
304 var buf: [16]u8 = undefined;272 var buf: [16]u8 = undefined;
305 var stream_reader = stream.reader(io, &.{});273 var stream_reader = stream.reader(io, &.{});
306 const n = try stream_reader.interface.readSliceShort(&buf);274 const n = try stream_reader.interface.readSliceShort(&buf);
307275
308 try testing.expectEqual(@as(usize, 12), n);276 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);
310}280}
311281
312fn generateFileName(io: Io, base_name: []const u8) ![]const u8 {282fn generateFileName(gpa: Allocator, io: Io, base_name: []const u8) ![]const u8 {
313 const random_bytes_count = 12;283 const random_bytes_count = 12;
314 const sub_path_len = comptime std.base64.url_safe.Encoder.calcSize(random_bytes_count);284 const sub_path_len = comptime std.base64.url_safe.Encoder.calcSize(random_bytes_count);
315 var random_bytes: [12]u8 = undefined;285 var random_bytes: [12]u8 = undefined;
316 io.random(&random_bytes);286 io.random(&random_bytes);
317 var sub_path: [sub_path_len]u8 = undefined;287 var sub_path: [sub_path_len]u8 = undefined;
318 _ = std.base64.url_safe.Encoder.encode(&sub_path, &random_bytes);288 _ = std.base64.url_safe.Encoder.encode(&sub_path, &random_bytes);
319 return std.fmt.allocPrint(testing.allocator, "{s}-{s}", .{ sub_path[0..], base_name });289 return std.fmt.allocPrint(gpa, "{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);
352}290}
353291
354test "decompress compressed DNS name" {292test "decompress compressed DNS name" {
...@@ -400,3 +338,21 @@ test "decompress compressed DNS name" {...@@ -400,3 +338,21 @@ test "decompress compressed DNS name" {
400 try testing.expectEqual(packet.len - cname_data_index, n_consumed);338 try testing.expectEqual(packet.len - cname_data_index, n_consumed);
401 try testing.expectEqualStrings("target.ziglang.org", result.bytes);339 try testing.expectEqualStrings("target.ziglang.org", result.bytes);
402}340}
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}