Files
picoclaw/pkg/tools/hardware/serial_unix_test.go
T

141 lines
3.4 KiB
Go

//go:build linux || darwin
package hardwaretools
import (
"context"
"errors"
"testing"
"time"
)
func stubUnixSerialIO(t *testing.T, now *time.Time) {
t.Helper()
prevNow := unixSerialNow
prevOpen := unixSerialOpenPort
prevClose := unixSerialClosePort
prevPollRead := unixSerialPollRead
prevPollWrite := unixSerialPollWrite
unixSerialNow = func() time.Time {
return *now
}
unixSerialOpenPort = func(cfg serialConfig) (int, error) {
return 42, nil
}
unixSerialClosePort = func(fd int) error {
return nil
}
unixSerialPollRead = prevPollRead
unixSerialPollWrite = prevPollWrite
t.Cleanup(func() {
unixSerialNow = prevNow
unixSerialOpenPort = prevOpen
unixSerialClosePort = prevClose
unixSerialPollRead = prevPollRead
unixSerialPollWrite = prevPollWrite
})
}
func TestSerialReadWaitsPastEmptyPollsUntilDeadline(t *testing.T) {
now := time.Unix(0, 0)
stubUnixSerialIO(t, &now)
pollCalls := 0
unixSerialPollRead = func(fd int, dst []byte, timeout time.Duration) (int, error) {
pollCalls++
if timeout > serialPollInterval {
t.Fatalf("poll timeout = %v, want <= %v", timeout, serialPollInterval)
}
now = now.Add(timeout)
if pollCalls < 4 {
return 0, nil
}
return copy(dst, []byte("OK")), nil
}
got, err := serialRead(context.Background(), serialConfig{}, 2, 500*time.Millisecond)
if err != nil {
t.Fatalf("serialRead() error = %v", err)
}
if string(got) != "OK" {
t.Fatalf("serialRead() = %q, want %q", got, "OK")
}
if pollCalls != 4 {
t.Fatalf("poll calls = %d, want 4", pollCalls)
}
}
func TestSerialReadReturnsPromptlyOnContextCancelBetweenPolls(t *testing.T) {
now := time.Unix(0, 0)
stubUnixSerialIO(t, &now)
ctx, cancel := context.WithCancel(context.Background())
pollCalls := 0
unixSerialPollRead = func(fd int, dst []byte, timeout time.Duration) (int, error) {
pollCalls++
now = now.Add(timeout)
cancel()
return 0, nil
}
_, err := serialRead(ctx, serialConfig{}, 1, time.Second)
if !errors.Is(err, context.Canceled) {
t.Fatalf("serialRead() error = %v, want context canceled", err)
}
if pollCalls != 1 {
t.Fatalf("poll calls = %d, want 1", pollCalls)
}
}
func TestSerialWriteWaitsPastEmptyPollsUntilReady(t *testing.T) {
now := time.Unix(0, 0)
stubUnixSerialIO(t, &now)
pollCalls := 0
unixSerialPollWrite = func(fd int, src []byte, timeout time.Duration) (int, error) {
pollCalls++
if timeout > serialPollInterval {
t.Fatalf("poll timeout = %v, want <= %v", timeout, serialPollInterval)
}
now = now.Add(timeout)
switch pollCalls {
case 1, 2:
return 0, nil
default:
return 1, nil
}
}
written, err := serialWrite(context.Background(), serialConfig{}, []byte("OK"), 500*time.Millisecond)
if err != nil {
t.Fatalf("serialWrite() error = %v", err)
}
if written != 2 {
t.Fatalf("serialWrite() wrote %d bytes, want 2", written)
}
if pollCalls != 4 {
t.Fatalf("poll calls = %d, want 4", pollCalls)
}
}
func TestSerialWriteTimesOutAfterRepeatedEmptyPolls(t *testing.T) {
now := time.Unix(0, 0)
stubUnixSerialIO(t, &now)
unixSerialPollWrite = func(fd int, src []byte, timeout time.Duration) (int, error) {
now = now.Add(timeout)
return 0, nil
}
written, err := serialWrite(context.Background(), serialConfig{}, []byte("A"), 250*time.Millisecond)
if err == nil || err.Error() != "timeout while writing serial data" {
t.Fatalf("serialWrite() error = %v, want timeout", err)
}
if written != 0 {
t.Fatalf("serialWrite() wrote %d bytes, want 0", written)
}
}