1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build wasip1
6
7package net
8
9import (
10	"syscall"
11	"testing"
12)
13
14// The tests in this file intend to validate the ability for net.FileConn and
15// net.FileListener to handle both TCP and UDP sockets. Ideally we would test
16// the public interface by constructing an *os.File from a file descriptor
17// opened on a socket, but the WASI preview 1 specification is too limited to
18// support this approach for UDP sockets. Instead, we test the internals that
19// make it possible for WASI host runtimes and guest programs to integrate
20// socket extensions with the net package using net.FileConn/net.FileListener.
21//
22// Note that the creation of net.Conn and net.Listener values for TCP sockets
23// has an end-to-end test in src/runtime/internal/wasitest, here we are only
24// verifying the code paths specific to UDP, and error handling for invalid use
25// of the functions.
26
27func TestWasip1FileConnNet(t *testing.T) {
28	tests := []struct {
29		filetype syscall.Filetype
30		network  string
31		error    error
32	}{
33		{syscall.FILETYPE_SOCKET_STREAM, "tcp", nil},
34		{syscall.FILETYPE_SOCKET_DGRAM, "udp", nil},
35		{syscall.FILETYPE_BLOCK_DEVICE, "", syscall.ENOTSOCK},
36		{syscall.FILETYPE_CHARACTER_DEVICE, "", syscall.ENOTSOCK},
37		{syscall.FILETYPE_DIRECTORY, "", syscall.ENOTSOCK},
38		{syscall.FILETYPE_REGULAR_FILE, "", syscall.ENOTSOCK},
39		{syscall.FILETYPE_SYMBOLIC_LINK, "", syscall.ENOTSOCK},
40		{syscall.FILETYPE_UNKNOWN, "", syscall.ENOTSOCK},
41	}
42	for _, test := range tests {
43		net, err := fileConnNet(test.filetype)
44		if net != test.network {
45			t.Errorf("fileConnNet: network mismatch: want=%q got=%q", test.network, net)
46		}
47		if err != test.error {
48			t.Errorf("fileConnNet: error mismatch: want=%v got=%v", test.error, err)
49		}
50	}
51}
52
53func TestWasip1FileListenNet(t *testing.T) {
54	tests := []struct {
55		filetype syscall.Filetype
56		network  string
57		error    error
58	}{
59		{syscall.FILETYPE_SOCKET_STREAM, "tcp", nil},
60		{syscall.FILETYPE_SOCKET_DGRAM, "", syscall.EOPNOTSUPP},
61		{syscall.FILETYPE_BLOCK_DEVICE, "", syscall.ENOTSOCK},
62		{syscall.FILETYPE_CHARACTER_DEVICE, "", syscall.ENOTSOCK},
63		{syscall.FILETYPE_DIRECTORY, "", syscall.ENOTSOCK},
64		{syscall.FILETYPE_REGULAR_FILE, "", syscall.ENOTSOCK},
65		{syscall.FILETYPE_SYMBOLIC_LINK, "", syscall.ENOTSOCK},
66		{syscall.FILETYPE_UNKNOWN, "", syscall.ENOTSOCK},
67	}
68	for _, test := range tests {
69		net, err := fileListenNet(test.filetype)
70		if net != test.network {
71			t.Errorf("fileListenNet: network mismatch: want=%q got=%q", test.network, net)
72		}
73		if err != test.error {
74			t.Errorf("fileListenNet: error mismatch: want=%v got=%v", test.error, err)
75		}
76	}
77}
78
79func TestWasip1NewFileListener(t *testing.T) {
80	if l, ok := newFileListener(newFD("tcp", -1)).(*TCPListener); !ok {
81		t.Errorf("newFileListener: tcp listener type mismatch: %T", l)
82	} else {
83		testIsTCPAddr(t, "Addr", l.Addr())
84	}
85}
86
87func TestWasip1NewFileConn(t *testing.T) {
88	if c, ok := newFileConn(newFD("tcp", -1)).(*TCPConn); !ok {
89		t.Errorf("newFileConn: tcp conn type mismatch: %T", c)
90	} else {
91		testIsTCPAddr(t, "LocalAddr", c.LocalAddr())
92		testIsTCPAddr(t, "RemoteAddr", c.RemoteAddr())
93	}
94	if c, ok := newFileConn(newFD("udp", -1)).(*UDPConn); !ok {
95		t.Errorf("newFileConn: udp conn type mismatch: %T", c)
96	} else {
97		testIsUDPAddr(t, "LocalAddr", c.LocalAddr())
98		testIsUDPAddr(t, "RemoteAddr", c.RemoteAddr())
99	}
100}
101
102func testIsTCPAddr(t *testing.T, method string, addr Addr) {
103	if _, ok := addr.(*TCPAddr); !ok {
104		t.Errorf("%s: returned address is not a *TCPAddr: %T", method, addr)
105	}
106}
107
108func testIsUDPAddr(t *testing.T, method string, addr Addr) {
109	if _, ok := addr.(*UDPAddr); !ok {
110		t.Errorf("%s: returned address is not a *UDPAddr: %T", method, addr)
111	}
112}
113