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.

172 lines
5.1 KiB

package serial
import (
"context"
"errors"
"fmt"
"sync/atomic"
"testing"
"time"
)
func TestMappingControllerRequiresValidatedCompleteMapping(t *testing.T) {
mapping := testMapping()
mapping.Validated = false
if _, err := NewMappingController(mapping); err == nil {
t.Fatal("expected unvalidated mapping to be rejected")
}
mapping = testMapping()
mapping.Channels = mapping.Channels[:47]
if _, err := NewMappingController(mapping); err == nil {
t.Fatal("expected incomplete mapping to be rejected")
}
}
func TestMappingControllerRejectsConnectedChannelsMappedToNCUARTs(t *testing.T) {
mapping := testMapping()
mapping.Channels[0].Device = "/dev/ttyWCH0"
if _, err := NewMappingController(mapping); err == nil {
t.Fatal("expected CH01 mapped to NC ttyWCH0 to be rejected")
}
mapping = testMapping()
mapping.Channels[24].Device = "/dev/ttyWCH28"
if _, err := NewMappingController(mapping); err == nil {
t.Fatal("expected CH25 mapped to NC ttyWCH28 to be rejected")
}
}
func TestMappingControllerFailsClosedBeforeOpeningUnavailableDevice(t *testing.T) {
controller, err := NewMappingController(testMapping())
if err != nil {
t.Fatalf("new mapping controller: %v", err)
}
states, err := controller.States(context.Background())
if err != nil {
t.Fatalf("states: %v", err)
}
if len(states) != ChannelCount || states[0].Channel != "CH01" || states[0].Available {
t.Fatalf("states = %#v", states)
}
_, err = controller.Apply(context.Background(), []ChannelConfig{{Channel: "CH01", BaudRate: 115200}})
if !IsUnavailable(err) {
t.Fatalf("apply error = %v, want unavailable error", err)
}
if !errors.Is(err, ErrSerialUnavailable) {
t.Fatalf("apply error = %v, want ErrSerialUnavailable", err)
}
}
func TestMappingControllerReportsUnknownUntilModemSignalIsValidated(t *testing.T) {
controller, err := NewMappingController(testMapping())
if err != nil {
t.Fatal(err)
}
states, err := controller.LinkStates(context.Background())
if err != nil || len(states) != ChannelCount {
t.Fatalf("LinkStates = %+v, %v", states, err)
}
if states[0].Channel != "CH01" || states[0].State != "unknown" || states[0].Signal != "unknown" || states[0].Available {
t.Fatalf("CH01 modem state = %+v", states[0])
}
mapping := testMapping()
mapping.Channels[0].LinkSignal = "ri"
if _, err := NewMappingController(mapping); err == nil {
t.Fatal("NewMappingController accepted an unapproved link signal")
}
}
func TestMappingControllerCanObserveDCDDSRAndCTSIndependently(t *testing.T) {
mapping := testMapping()
mapping.Channels[0].LinkSignals = []string{"dcd", "dsr", "cts"}
controller, err := NewMappingController(mapping)
if err != nil {
t.Fatal(err)
}
up := true
controller.modemProbe = func(entry MappingChannel) LinkState {
state := "down"
if up {
state = "up"
}
return LinkState{Channel: entry.Channel, State: state, Signal: entry.LinkSignal, Available: true}
}
states, err := controller.LinkStates(context.Background())
if err != nil {
t.Fatal(err)
}
if len(states) != ChannelCount+2 || states[0].Signal != "dcd" || states[1].Signal != "dsr" || states[2].Signal != "cts" ||
states[0].Channel != "CH01" || states[1].Channel != "CH01" || states[2].Channel != "CH01" || !states[2].EverUp {
t.Fatalf("multi-signal link states = %+v", states[:min(4, len(states))])
}
up = false
states, err = controller.LinkStates(context.Background())
if err != nil || states[2].State != "down" || !states[2].EverUp {
t.Fatalf("CTS history after link loss = %+v, %v", states[2], err)
}
mapping.Channels[0].LinkSignals = []string{"dcd", "dcd"}
if _, err := NewMappingController(mapping); err == nil {
t.Fatal("duplicate modem signals were accepted")
}
}
func TestMappingControllerBoundsParallelLinkProbes(t *testing.T) {
controller, err := NewMappingController(testMapping())
if err != nil {
t.Fatal(err)
}
controller.SetLinkWorkerCount(8)
var active atomic.Int32
var maximum atomic.Int32
controller.modemProbe = func(entry MappingChannel) LinkState {
current := active.Add(1)
for {
seen := maximum.Load()
if current <= seen || maximum.CompareAndSwap(seen, current) {
break
}
}
time.Sleep(5 * time.Millisecond)
active.Add(-1)
return LinkState{Channel: entry.Channel, State: "down", Signal: entry.LinkSignal, Available: true}
}
states, err := controller.LinkStates(context.Background())
if err != nil || len(states) != ChannelCount {
t.Fatalf("LinkStates = %d, %v", len(states), err)
}
if maximum.Load() < 2 || maximum.Load() > 8 {
t.Fatalf("parallel link workers = %d, want 2..8", maximum.Load())
}
}
func testMapping() Mapping {
channels := make([]MappingChannel, 0, ChannelCount)
for index := 1; index <= ChannelCount; index++ {
bdf := "0000:01:00.0"
ttyNumber := index + 3
if index > 24 {
bdf = "0002:21:00.0"
ttyNumber = index + 7
}
channels = append(channels, MappingChannel{
Channel: fmt.Sprintf("CH%02d", index),
Device: fmt.Sprintf("/dev/ttyWCH%d", ttyNumber),
PCIBDF: bdf,
})
}
return Mapping{
FormatVersion: 1,
HardwareProfile: "fet3568j-c",
MappingVersion: "validated-test",
Evidence: "isolated-loopback-test",
Validated: true,
Channels: channels,
}
}