You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

251 lines
7.7 KiB

package serial
import (
"context"
"encoding/json"
"errors"
"net"
"path/filepath"
"testing"
"time"
)
type socketProbeController struct {
UnavailableController
result ProbeResult
}
type socketLinkController struct{ UnavailableController }
type socketCaptureController struct{ configs []CaptureConfig }
type socketCRProbeController struct{ states []CRProbeState }
type socketBusyController struct{ UnavailableController }
func (socketBusyController) Apply(context.Context, []ChannelConfig) ([]State, error) {
return nil, ErrStreamLeaseBusy
}
func (c socketCRProbeController) CRProbeStates(context.Context) ([]CRProbeState, error) {
return append([]CRProbeState(nil), c.states...), nil
}
func (c *socketCaptureController) ApplyCapture(_ context.Context, configs []CaptureConfig) error {
c.configs = append([]CaptureConfig(nil), configs...)
return nil
}
func (socketLinkController) LinkStates(context.Context) ([]LinkState, error) {
return []LinkState{{Channel: "CH01", State: "up", Signal: "dcd", Available: true}}, nil
}
func (c socketProbeController) Probe(context.Context, ChannelConfig) (ProbeResult, error) {
return c.result, nil
}
func TestSocketClientPreservesFailClosedSerialUnavailableResult(t *testing.T) {
socketPath := filepath.Join(t.TempDir(), "serial-ports.sock")
listener, err := net.Listen("unix", socketPath)
if err != nil {
t.Skipf("unix sockets unavailable: %v", err)
}
defer listener.Close()
server := SocketServer{Controller: UnavailableController{Reason: "validated mapping is not installed"}}
done := make(chan error, 1)
go func() { done <- server.Serve(listener) }()
client := NewSocketClient(socketPath)
states, err := client.States(context.Background())
if err != nil || len(states) != ChannelCount || states[0].Available {
t.Fatalf("states = %#v, %v", states, err)
}
_, err = client.Apply(context.Background(), []ChannelConfig{{Channel: "CH01", BaudRate: 115200}})
if !IsUnavailable(err) {
t.Fatalf("apply error = %v, want unavailable", err)
}
if err := listener.Close(); err != nil {
t.Fatal(err)
}
select {
case err := <-done:
if err != nil {
t.Fatal(err)
}
case <-time.After(time.Second):
t.Fatal("socket server did not stop")
}
}
func TestSocketClientPreservesStreamLeaseBusyResult(t *testing.T) {
socketPath := filepath.Join(t.TempDir(), "serial-busy.sock")
listener, err := net.Listen("unix", socketPath)
if err != nil {
t.Skipf("unix sockets unavailable: %v", err)
}
done := make(chan error, 1)
go func() { done <- (SocketServer{Controller: socketBusyController{}}).Serve(listener) }()
client := NewSocketClient(socketPath)
_, err = client.Apply(context.Background(), []ChannelConfig{{Channel: "CH16", BaudRate: 9600}})
if !errors.Is(err, ErrStreamLeaseBusy) {
t.Fatalf("apply error = %v, want stream lease busy", err)
}
_ = listener.Close()
<-done
}
func TestSocketStatesRequestDoesNotWaitForClientEOF(t *testing.T) {
socketPath := filepath.Join(t.TempDir(), "serial-ports.sock")
listener, err := net.Listen("unix", socketPath)
if err != nil {
t.Skipf("unix sockets unavailable: %v", err)
}
defer listener.Close()
server := SocketServer{Controller: UnavailableController{Reason: "read-only test"}}
done := make(chan error, 1)
go func() { done <- server.Serve(listener) }()
connection, err := net.Dial("unix", socketPath)
if err != nil {
t.Fatal(err)
}
defer connection.Close()
if err := connection.SetDeadline(time.Now().Add(time.Second)); err != nil {
t.Fatal(err)
}
if err := json.NewEncoder(connection).Encode(socketRequest{Action: "states"}); err != nil {
t.Fatal(err)
}
var response socketResponse
if err := json.NewDecoder(connection).Decode(&response); err != nil {
t.Fatalf("decode response without closing request side: %v", err)
}
if len(response.States) != ChannelCount {
t.Fatalf("states = %d, want %d", len(response.States), ChannelCount)
}
if err := listener.Close(); err != nil {
t.Fatal(err)
}
select {
case err := <-done:
if err != nil {
t.Fatal(err)
}
case <-time.After(time.Second):
t.Fatal("socket server did not stop")
}
}
func TestSocketClientReadsBoundedModemLinkState(t *testing.T) {
socketPath := filepath.Join(t.TempDir(), "serial-links.sock")
listener, err := net.Listen("unix", socketPath)
if err != nil {
t.Skipf("unix sockets unavailable: %v", err)
}
done := make(chan error, 1)
go func() { done <- (SocketServer{Controller: socketLinkController{}}).Serve(listener) }()
client := NewSocketClient(socketPath)
states, err := client.LinkStates(context.Background())
if err != nil || len(states) != 1 || !states[0].Available || states[0].Signal != "dcd" {
t.Fatalf("LinkStates = %+v, %v", states, err)
}
_ = listener.Close()
<-done
}
func TestSocketClientReadsCachedCRProbeState(t *testing.T) {
socketPath := filepath.Join(t.TempDir(), "serial-cr-probe.sock")
listener, err := net.Listen("unix", socketPath)
if err != nil {
t.Skipf("unix sockets unavailable: %v", err)
}
want := []CRProbeState{{Channel: "CH08", Enabled: true, State: CRProbeStateResponded, Method: "cr", ResponseBytes: 7}}
done := make(chan error, 1)
go func() {
done <- (SocketServer{
Controller: UnavailableController{Reason: "line control unused"},
CRProbe: socketCRProbeController{states: want},
}).Serve(listener)
}()
client := NewSocketClient(socketPath)
got, err := client.CRProbeStates(context.Background())
if err != nil || len(got) != 1 || got[0].Channel != "CH08" || got[0].State != CRProbeStateResponded {
t.Fatalf("CRProbeStates = %+v, %v", got, err)
}
_ = listener.Close()
<-done
}
func TestSocketClientProbeRoundTrip(t *testing.T) {
socketPath := filepath.Join(t.TempDir(), "serial-probe.sock")
listener, err := net.Listen("unix", socketPath)
if err != nil {
t.Skipf("unix sockets unavailable: %v", err)
}
defer listener.Close()
want := ProbeResult{
Channel: "CH01", Device: "/dev/ttyWCH4",
Previous: LineSettings{BaudRate: 9600, DataBits: 8, Parity: "none", StopBits: 1, FlowControl: "none"},
Applied: LineSettings{BaudRate: 115200, DataBits: 8, Parity: "none", StopBits: 1, FlowControl: "none"},
Restored: true,
}
server := SocketServer{Controller: socketProbeController{
UnavailableController: UnavailableController{Reason: "states unused"},
result: want,
}}
done := make(chan error, 1)
go func() { done <- server.Serve(listener) }()
client := NewSocketClient(socketPath)
got, err := client.Probe(context.Background(), ChannelConfig{
Channel: "CH01", BaudRate: 115200, DataBits: 8, Parity: "none", StopBits: 1, FlowControl: "none",
})
if err != nil {
t.Fatalf("probe: %v", err)
}
if got != want {
t.Fatalf("probe = %#v, want %#v", got, want)
}
if err := listener.Close(); err != nil {
t.Fatal(err)
}
select {
case err := <-done:
if err != nil {
t.Fatal(err)
}
case <-time.After(time.Second):
t.Fatal("socket server did not stop")
}
}
func TestSocketClientAppliesPayloadCaptureWithoutTermios(t *testing.T) {
socketPath := filepath.Join(t.TempDir(), "serial-capture.sock")
listener, err := net.Listen("unix", socketPath)
if err != nil {
t.Skipf("unix sockets unavailable: %v", err)
}
capture := &socketCaptureController{}
done := make(chan error, 1)
go func() {
done <- (SocketServer{Controller: UnavailableController{Reason: "line control unused"}, Capture: capture}).Serve(listener)
}()
client := NewSocketClient(socketPath)
want := []CaptureConfig{{Channel: "CH01", Mode: CaptureModeRXTX}, {Channel: "CH02", Mode: CaptureModeOff}}
if err := client.ApplyCapture(context.Background(), want); err != nil {
t.Fatal(err)
}
if len(capture.configs) != len(want) || capture.configs[0] != want[0] || capture.configs[1] != want[1] {
t.Fatalf("capture configs = %#v", capture.configs)
}
_ = listener.Close()
<-done
}