mirror of
https://github.com/tinygo-org/net.git
synced 2026-07-26 08:18:39 +00:00
add tests
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
// TINYGO test for DNSError.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDNSErrorError(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
e *DNSError
|
||||
want string
|
||||
}{
|
||||
{"nil", nil, "<nil>"},
|
||||
{"no server", &DNSError{Err: "no such host", Name: "example.com"},
|
||||
"lookup example.com: no such host"},
|
||||
{"with server", &DNSError{Err: "timeout", Name: "example.com", Server: "8.8.8.8:53"},
|
||||
"lookup example.com on 8.8.8.8:53: timeout"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
if got := tt.e.Error(); got != tt.want {
|
||||
t.Errorf("%s: Error() = %q, want %q", tt.name, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDNSErrorUnwrap(t *testing.T) {
|
||||
inner := errors.New("boom")
|
||||
e := &DNSError{Err: "x", UnwrapErr: inner}
|
||||
if got := e.Unwrap(); got != inner {
|
||||
t.Errorf("Unwrap() = %v, want %v", got, inner)
|
||||
}
|
||||
if !errors.Is(e, inner) {
|
||||
t.Errorf("errors.Is(e, inner) = false, want true")
|
||||
}
|
||||
|
||||
if (&DNSError{Err: "x"}).Unwrap() != nil {
|
||||
t.Errorf("Unwrap() with no UnwrapErr should be nil")
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
// TINYGO tests for the netdev-backed Resolver lookups.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/netip"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestResolverLookupHost(t *testing.T) {
|
||||
ip := netip.MustParseAddr("1.2.3.4")
|
||||
withNetdev(t, &fakeNetdev{hostIP: ip})
|
||||
|
||||
addrs, err := DefaultResolver.LookupHost(context.Background(), "example.com")
|
||||
if err != nil {
|
||||
t.Fatalf("LookupHost: unexpected error %v", err)
|
||||
}
|
||||
if len(addrs) != 1 || addrs[0] != "1.2.3.4" {
|
||||
t.Fatalf("LookupHost = %v, want [1.2.3.4]", addrs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolverLookupHostEmpty(t *testing.T) {
|
||||
withNetdev(t, &fakeNetdev{})
|
||||
|
||||
_, err := DefaultResolver.LookupHost(context.Background(), "")
|
||||
var de *DNSError
|
||||
if !errors.As(err, &de) || !de.IsNotFound {
|
||||
t.Fatalf("LookupHost(\"\") = %v, want *DNSError with IsNotFound", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolverLookupHostError(t *testing.T) {
|
||||
withNetdev(t, &fakeNetdev{hostErr: errors.New("dns down")})
|
||||
|
||||
_, err := DefaultResolver.LookupHost(context.Background(), "example.com")
|
||||
var de *DNSError
|
||||
if !errors.As(err, &de) {
|
||||
t.Fatalf("LookupHost error = %T, want *DNSError", err)
|
||||
}
|
||||
if de.Name != "example.com" {
|
||||
t.Errorf("DNSError.Name = %q, want example.com", de.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolverLookupIPAddr(t *testing.T) {
|
||||
ip := netip.MustParseAddr("10.0.0.5")
|
||||
withNetdev(t, &fakeNetdev{hostIP: ip})
|
||||
|
||||
addrs, err := DefaultResolver.LookupIPAddr(context.Background(), "host")
|
||||
if err != nil {
|
||||
t.Fatalf("LookupIPAddr: %v", err)
|
||||
}
|
||||
if len(addrs) != 1 || !addrs[0].IP.Equal(IPv4(10, 0, 0, 5)) {
|
||||
t.Fatalf("LookupIPAddr = %v, want [10.0.0.5]", addrs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolverLookupNetIP(t *testing.T) {
|
||||
ip := netip.MustParseAddr("10.0.0.6")
|
||||
withNetdev(t, &fakeNetdev{hostIP: ip})
|
||||
|
||||
addrs, err := DefaultResolver.LookupNetIP(context.Background(), "ip", "host")
|
||||
if err != nil {
|
||||
t.Fatalf("LookupNetIP: %v", err)
|
||||
}
|
||||
if len(addrs) != 1 || addrs[0] != ip {
|
||||
t.Fatalf("LookupNetIP = %v, want [%v]", addrs, ip)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupIP(t *testing.T) {
|
||||
ip := netip.MustParseAddr("192.168.1.1")
|
||||
withNetdev(t, &fakeNetdev{hostIP: ip})
|
||||
|
||||
ips, err := LookupIP("host")
|
||||
if err != nil {
|
||||
t.Fatalf("LookupIP: %v", err)
|
||||
}
|
||||
if len(ips) != 1 || !ips[0].Equal(IPv4(192, 168, 1, 1)) {
|
||||
t.Fatalf("LookupIP = %v, want [192.168.1.1]", ips)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupHostPackage(t *testing.T) {
|
||||
ip := netip.MustParseAddr("172.16.0.9")
|
||||
withNetdev(t, &fakeNetdev{hostIP: ip})
|
||||
|
||||
addrs, err := LookupHost("host")
|
||||
if err != nil {
|
||||
t.Fatalf("LookupHost: %v", err)
|
||||
}
|
||||
if len(addrs) != 1 || addrs[0] != "172.16.0.9" {
|
||||
t.Fatalf("LookupHost = %v, want [172.16.0.9]", addrs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultResolverNonNil(t *testing.T) {
|
||||
if DefaultResolver == nil {
|
||||
t.Fatal("DefaultResolver is nil")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
// TINYGO test helper: an in-memory fake netdever for exercising the
|
||||
// netdev-backed net APIs without real hardware/OS sockets.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// fakeNetdev embeds nopNetdev (whose methods all error) and overrides only
|
||||
// the calls a given test needs. Recorded fields let tests assert on how the
|
||||
// net package drove the device.
|
||||
type fakeNetdev struct {
|
||||
nopNetdev
|
||||
|
||||
// GetHostByName control.
|
||||
hostIP netip.Addr
|
||||
hostErr error
|
||||
|
||||
// Socket/Bind/Connect control + capture.
|
||||
socketFD int
|
||||
socketErr error
|
||||
socketArgs [3]int
|
||||
bindErr error
|
||||
bindAddr netip.AddrPort
|
||||
connectErr error
|
||||
|
||||
// Send/Recv control + capture.
|
||||
sent []byte // accumulates everything written via Send
|
||||
sendErr error
|
||||
recvData []byte // returned (once) by Recv
|
||||
recvErr error
|
||||
|
||||
closedFDs []int
|
||||
}
|
||||
|
||||
func (f *fakeNetdev) GetHostByName(name string) (netip.Addr, error) {
|
||||
return f.hostIP, f.hostErr
|
||||
}
|
||||
|
||||
func (f *fakeNetdev) Socket(domain, stype, protocol int) (int, error) {
|
||||
f.socketArgs = [3]int{domain, stype, protocol}
|
||||
return f.socketFD, f.socketErr
|
||||
}
|
||||
|
||||
func (f *fakeNetdev) Bind(sockfd int, ip netip.AddrPort) error {
|
||||
f.bindAddr = ip
|
||||
return f.bindErr
|
||||
}
|
||||
|
||||
func (f *fakeNetdev) Connect(sockfd int, host string, ip netip.AddrPort) error {
|
||||
return f.connectErr
|
||||
}
|
||||
|
||||
func (f *fakeNetdev) Send(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) {
|
||||
if f.sendErr != nil {
|
||||
return -1, f.sendErr
|
||||
}
|
||||
f.sent = append(f.sent, buf...)
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (f *fakeNetdev) Recv(sockfd int, buf []byte, flags int, deadline time.Time) (int, error) {
|
||||
if f.recvErr != nil {
|
||||
return -1, f.recvErr
|
||||
}
|
||||
n := copy(buf, f.recvData)
|
||||
f.recvData = f.recvData[n:]
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (f *fakeNetdev) Close(sockfd int) error {
|
||||
f.closedFDs = append(f.closedFDs, sockfd)
|
||||
return nil
|
||||
}
|
||||
|
||||
// withNetdev installs d as the package netdev for the duration of the test.
|
||||
func withNetdev(t *testing.T, d netdever) {
|
||||
t.Helper()
|
||||
old := netdev
|
||||
netdev = d
|
||||
t.Cleanup(func() { netdev = old })
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// TINYGO tests for TCPConn.ReadFrom and its generic copy helper.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGenericReadFrom(t *testing.T) {
|
||||
var dst bytes.Buffer
|
||||
src := strings.NewReader("payload bytes")
|
||||
|
||||
n, err := genericReadFrom(&dst, src)
|
||||
if err != nil {
|
||||
t.Fatalf("genericReadFrom: %v", err)
|
||||
}
|
||||
if n != int64(len("payload bytes")) {
|
||||
t.Errorf("n = %d, want %d", n, len("payload bytes"))
|
||||
}
|
||||
if dst.String() != "payload bytes" {
|
||||
t.Errorf("copied %q, want %q", dst.String(), "payload bytes")
|
||||
}
|
||||
}
|
||||
|
||||
// readerFromSpy is an io.Writer that also implements io.ReaderFrom. If
|
||||
// io.Copy ever routes through ReadFrom, the spy records it — genericReadFrom
|
||||
// must hide it (via onlyWriter) to avoid recursing back into TCPConn.ReadFrom.
|
||||
type readerFromSpy struct {
|
||||
bytes.Buffer
|
||||
readFromCalled bool
|
||||
}
|
||||
|
||||
func (s *readerFromSpy) ReadFrom(r io.Reader) (int64, error) {
|
||||
s.readFromCalled = true
|
||||
return s.Buffer.ReadFrom(r)
|
||||
}
|
||||
|
||||
func TestGenericReadFromHidesReaderFrom(t *testing.T) {
|
||||
spy := &readerFromSpy{}
|
||||
if _, err := genericReadFrom(spy, strings.NewReader("abc")); err != nil {
|
||||
t.Fatalf("genericReadFrom: %v", err)
|
||||
}
|
||||
if spy.readFromCalled {
|
||||
t.Error("onlyWriter failed to hide ReadFrom; io.Copy recursed via ReaderFrom")
|
||||
}
|
||||
if spy.String() != "abc" {
|
||||
t.Errorf("copied %q, want abc", spy.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTCPConnReadFrom(t *testing.T) {
|
||||
f := &fakeNetdev{}
|
||||
withNetdev(t, f)
|
||||
|
||||
c := &TCPConn{fd: 1, net: "tcp"}
|
||||
src := strings.NewReader("streamed over tcp")
|
||||
n, err := c.ReadFrom(src)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFrom: %v", err)
|
||||
}
|
||||
if n != int64(len("streamed over tcp")) {
|
||||
t.Errorf("n = %d, want %d", n, len("streamed over tcp"))
|
||||
}
|
||||
if string(f.sent) != "streamed over tcp" {
|
||||
t.Errorf("netdev received %q, want %q", f.sent, "streamed over tcp")
|
||||
}
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
// TINYGO tests for ListenUDP and the UDPConn addr-carrying I/O methods.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestListenUDP(t *testing.T) {
|
||||
f := &fakeNetdev{socketFD: 7}
|
||||
withNetdev(t, f)
|
||||
|
||||
uc, err := ListenUDP("udp", &UDPAddr{IP: IPv4(0, 0, 0, 0), Port: 1234})
|
||||
if err != nil {
|
||||
t.Fatalf("ListenUDP: %v", err)
|
||||
}
|
||||
if uc.fd != 7 {
|
||||
t.Errorf("fd = %d, want 7", uc.fd)
|
||||
}
|
||||
if uc.net != "udp" {
|
||||
t.Errorf("net = %q, want udp", uc.net)
|
||||
}
|
||||
if uc.laddr.Port != 1234 {
|
||||
t.Errorf("laddr.Port = %d, want 1234", uc.laddr.Port)
|
||||
}
|
||||
if uc.raddr != nil {
|
||||
t.Errorf("raddr = %v, want nil (no connect)", uc.raddr)
|
||||
}
|
||||
if f.socketArgs != [3]int{_AF_INET, _SOCK_DGRAM, _IPPROTO_UDP} {
|
||||
t.Errorf("Socket args = %v, want AF_INET/SOCK_DGRAM/IPPROTO_UDP", f.socketArgs)
|
||||
}
|
||||
if f.bindAddr.Port() != 1234 {
|
||||
t.Errorf("Bind port = %d, want 1234", f.bindAddr.Port())
|
||||
}
|
||||
}
|
||||
|
||||
func TestListenUDPBadNetwork(t *testing.T) {
|
||||
withNetdev(t, &fakeNetdev{})
|
||||
if _, err := ListenUDP("tcp", nil); err == nil {
|
||||
t.Fatal("ListenUDP(\"tcp\") = nil error, want error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListenUDPEphemeralPort(t *testing.T) {
|
||||
withNetdev(t, &fakeNetdev{socketFD: 3})
|
||||
uc, err := ListenUDP("udp", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("ListenUDP: %v", err)
|
||||
}
|
||||
if uc.laddr.Port == 0 {
|
||||
t.Error("expected an ephemeral port to be assigned, got 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListenUDPBindErrorClosesSocket(t *testing.T) {
|
||||
f := &fakeNetdev{socketFD: 9, bindErr: errBind}
|
||||
withNetdev(t, f)
|
||||
|
||||
if _, err := ListenUDP("udp", &UDPAddr{Port: 5}); err != errBind {
|
||||
t.Fatalf("ListenUDP err = %v, want %v", err, errBind)
|
||||
}
|
||||
if len(f.closedFDs) != 1 || f.closedFDs[0] != 9 {
|
||||
t.Errorf("closed fds = %v, want [9]", f.closedFDs)
|
||||
}
|
||||
}
|
||||
|
||||
var errBind = &DNSError{Err: "bind failed"} // any sentinel error
|
||||
|
||||
func TestUDPConnReadFromUDP(t *testing.T) {
|
||||
raddr := &UDPAddr{IP: IPv4(5, 6, 7, 8), Port: 99}
|
||||
f := &fakeNetdev{recvData: []byte("hello")}
|
||||
withNetdev(t, f)
|
||||
|
||||
c := &UDPConn{fd: 1, net: "udp", raddr: raddr}
|
||||
buf := make([]byte, 8)
|
||||
n, addr, err := c.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFromUDP: %v", err)
|
||||
}
|
||||
if string(buf[:n]) != "hello" {
|
||||
t.Errorf("read %q, want hello", buf[:n])
|
||||
}
|
||||
if addr != raddr {
|
||||
t.Errorf("source addr = %v, want %v (connected remote)", addr, raddr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUDPConnReadFromUDPAddrPort(t *testing.T) {
|
||||
raddr := &UDPAddr{IP: IPv4(5, 6, 7, 8), Port: 99}
|
||||
withNetdev(t, &fakeNetdev{recvData: []byte("hi")})
|
||||
|
||||
c := &UDPConn{fd: 1, net: "udp", raddr: raddr}
|
||||
n, ap, err := c.ReadFromUDPAddrPort(make([]byte, 8))
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFromUDPAddrPort: %v", err)
|
||||
}
|
||||
if n != 2 {
|
||||
t.Errorf("n = %d, want 2", n)
|
||||
}
|
||||
if ap != raddr.AddrPort() {
|
||||
t.Errorf("addrport = %v, want %v", ap, raddr.AddrPort())
|
||||
}
|
||||
}
|
||||
|
||||
func TestUDPConnWriteToUDP(t *testing.T) {
|
||||
f := &fakeNetdev{}
|
||||
withNetdev(t, f)
|
||||
|
||||
c := &UDPConn{fd: 1, net: "udp"}
|
||||
n, err := c.WriteToUDP([]byte("data"), &UDPAddr{IP: IPv4(1, 1, 1, 1), Port: 53})
|
||||
if err != nil {
|
||||
t.Fatalf("WriteToUDP: %v", err)
|
||||
}
|
||||
if n != 4 || string(f.sent) != "data" {
|
||||
t.Errorf("sent %q (n=%d), want data (n=4)", f.sent, n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUDPConnWriteToUDPAddrPort(t *testing.T) {
|
||||
f := &fakeNetdev{}
|
||||
withNetdev(t, f)
|
||||
|
||||
c := &UDPConn{fd: 1, net: "udp"}
|
||||
ap := netip.AddrPortFrom(netip.MustParseAddr("2.2.2.2"), 53)
|
||||
n, err := c.WriteToUDPAddrPort([]byte("xyz"), ap)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteToUDPAddrPort: %v", err)
|
||||
}
|
||||
if n != 3 || string(f.sent) != "xyz" {
|
||||
t.Errorf("sent %q (n=%d), want xyz (n=3)", f.sent, n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUDPConnSetBuffersNoop(t *testing.T) {
|
||||
c := &UDPConn{}
|
||||
if err := c.SetReadBuffer(4096); err != nil {
|
||||
t.Errorf("SetReadBuffer: %v", err)
|
||||
}
|
||||
if err := c.SetWriteBuffer(4096); err != nil {
|
||||
t.Errorf("SetWriteBuffer: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user