add tests

This commit is contained in:
Patricio Whittingslow
2026-07-04 11:56:36 -03:00
parent ffb222f1ad
commit be8ef2c5b4
5 changed files with 444 additions and 0 deletions
+42
View File
@@ -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
View File
@@ -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")
}
}
+85
View File
@@ -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 })
}
+70
View File
@@ -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
View File
@@ -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)
}
}