Testing
This commit is contained in:
+68
-9
@@ -29,13 +29,21 @@ type Server struct {
|
||||
tcpLn net.Listener
|
||||
udpCn *net.UDPConn
|
||||
|
||||
mu sync.RWMutex
|
||||
pvs map[string]*serverPV
|
||||
subs map[uint32]*serverSub // subID → sub
|
||||
mu sync.RWMutex
|
||||
pvs map[string]*serverPV
|
||||
subs map[uint32]*serverSub // subID → sub
|
||||
getFault int // fault to inject on the next READ_NOTIFY
|
||||
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// Fault modes for SetGetFault, used to exercise the client's error paths.
|
||||
const (
|
||||
GetFaultNone = iota // serve replies normally
|
||||
GetFaultDisconnect // drop the connection mid-request
|
||||
GetFaultCorrupt // reply with an undecodable (too-short) payload
|
||||
)
|
||||
|
||||
type serverPV struct {
|
||||
spec PVSpec
|
||||
mu sync.RWMutex
|
||||
@@ -43,11 +51,11 @@ type serverPV struct {
|
||||
}
|
||||
|
||||
type serverSub struct {
|
||||
pvName string
|
||||
subID uint32
|
||||
cid uint32
|
||||
sid uint32
|
||||
conn net.Conn
|
||||
pvName string
|
||||
subID uint32
|
||||
cid uint32
|
||||
sid uint32
|
||||
conn net.Conn
|
||||
dbrType uint16
|
||||
count uint32
|
||||
}
|
||||
@@ -93,6 +101,29 @@ func (s *Server) Close() {
|
||||
s.udpCn.Close()
|
||||
}
|
||||
|
||||
// SetGetFault arms a fault to be injected on the next READ_NOTIFY request.
|
||||
// It is reset to GetFaultNone after a single request is faulted.
|
||||
func (s *Server) SetGetFault(mode int) {
|
||||
s.mu.Lock()
|
||||
s.getFault = mode
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
// Disconnect pushes a SERVER_DISCONN message to every connected subscriber,
|
||||
// simulating an orderly server shutdown that forces clients to reconnect.
|
||||
func (s *Server) Disconnect() {
|
||||
msg := proto.BuildMessage(proto.Header{Command: proto.CmdServerDisc}, nil)
|
||||
seen := make(map[net.Conn]bool)
|
||||
s.mu.RLock()
|
||||
for _, sub := range s.subs {
|
||||
if sub.conn != nil && !seen[sub.conn] {
|
||||
seen[sub.conn] = true
|
||||
_, _ = sub.conn.Write(msg)
|
||||
}
|
||||
}
|
||||
s.mu.RUnlock()
|
||||
}
|
||||
|
||||
// SetValue updates a PV's value and pushes a monitor event to all subscribers.
|
||||
func (s *Server) SetValue(pvName string, val any) error {
|
||||
s.mu.RLock()
|
||||
@@ -350,6 +381,31 @@ func (s *Server) handleConn(conn net.Conn) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Fault injection: consume a one-shot armed fault, if any.
|
||||
s.mu.Lock()
|
||||
fault := s.getFault
|
||||
s.getFault = GetFaultNone
|
||||
s.mu.Unlock()
|
||||
switch fault {
|
||||
case GetFaultDisconnect:
|
||||
// Drop the connection mid-request: the client's in-flight GET
|
||||
// unblocks with ok=false ("circuit disconnected during GET").
|
||||
conn.Close()
|
||||
return
|
||||
case GetFaultCorrupt:
|
||||
// Reply with a too-short payload so DecodeTimeValue fails
|
||||
// ("failed to decode GET reply").
|
||||
reply := proto.BuildMessage(proto.Header{
|
||||
Command: proto.CmdReadNotify,
|
||||
DataType: hdr.DataType,
|
||||
DataCount: hdr.DataCount,
|
||||
Parameter1: cid,
|
||||
Parameter2: ioid,
|
||||
}, []byte{0, 0, 0, 0})
|
||||
conn.Write(reply)
|
||||
continue
|
||||
}
|
||||
|
||||
s.mu.RLock()
|
||||
pv := s.pvs[pvName]
|
||||
s.mu.RUnlock()
|
||||
@@ -591,7 +647,10 @@ func nullStr(b []byte) string {
|
||||
// Minimal io.Reader for proto.DecodeHeader //
|
||||
// -------------------------------------------------------------------------- //
|
||||
|
||||
type bytesReader struct{ b []byte; i int }
|
||||
type bytesReader struct {
|
||||
b []byte
|
||||
i int
|
||||
}
|
||||
|
||||
func newBytesReader(b []byte) *bytesReader { return &bytesReader{b: b} }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user