Initial go port of epics
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
package ca
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/uopi/goca/proto"
|
||||
)
|
||||
|
||||
// buildOldSearchReply builds a CA_PROTO_SEARCH UDP reply using the old
|
||||
// payload format (CA < 4.9): [minor_version(2)][pad(2)][IP(4)].
|
||||
// When ip is nil, the IP field is 0xFFFFFFFF (use-sender convention).
|
||||
func buildOldSearchReply(searchID uint32, serverIP net.IP, port int) []byte {
|
||||
payload := make([]byte, 8)
|
||||
payload[0] = 0x00
|
||||
payload[1] = proto.MinorVersion // e.g. 0x0D = 13
|
||||
// payload[2:4] = 0x00 0x00 (pad)
|
||||
if serverIP == nil {
|
||||
payload[4] = 0xFF
|
||||
payload[5] = 0xFF
|
||||
payload[6] = 0xFF
|
||||
payload[7] = 0xFF
|
||||
} else {
|
||||
copy(payload[4:], serverIP.To4())
|
||||
}
|
||||
h := proto.Header{
|
||||
Command: proto.CmdSearch,
|
||||
DataType: uint16(port),
|
||||
Parameter1: proto.MinorVersion,
|
||||
Parameter2: searchID,
|
||||
}
|
||||
return proto.BuildMessage(h, payload)
|
||||
}
|
||||
|
||||
// TestParseReplyOldFormatAllFF verifies the old format with 0xFFFFFFFF IP.
|
||||
func TestParseReplyOldFormatAllFF(t *testing.T) {
|
||||
se := &searchEngine{waiters: make(map[uint32]*searchWaiter)}
|
||||
const searchID = uint32(42)
|
||||
result := make(chan searchResult, 1)
|
||||
se.mu.Lock()
|
||||
se.waiters[searchID] = &searchWaiter{pvName: "TEST:PV", ch: result}
|
||||
se.mu.Unlock()
|
||||
|
||||
pkt := buildOldSearchReply(searchID, nil, 5064) // nil → 0xFFFFFFFF
|
||||
src := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 5064}
|
||||
se.parseReply(pkt, src)
|
||||
|
||||
select {
|
||||
case r := <-result:
|
||||
if r.addr != "127.0.0.1:5064" {
|
||||
t.Errorf("addr = %q, want 127.0.0.1:5064", r.addr)
|
||||
}
|
||||
default:
|
||||
t.Fatal("parseReply did not deliver a result")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseReplyOldFormatAllZero verifies the old format with 0.0.0.0 IP
|
||||
// (used by some EPICS Base versions to also mean "use sender IP").
|
||||
func TestParseReplyOldFormatAllZero(t *testing.T) {
|
||||
se := &searchEngine{waiters: make(map[uint32]*searchWaiter)}
|
||||
const searchID = uint32(43)
|
||||
result := make(chan searchResult, 1)
|
||||
se.mu.Lock()
|
||||
se.waiters[searchID] = &searchWaiter{pvName: "TEST:PV", ch: result}
|
||||
se.mu.Unlock()
|
||||
|
||||
pkt := buildOldSearchReply(searchID, net.IPv4(0, 0, 0, 0).To4(), 5064)
|
||||
src := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 5064}
|
||||
se.parseReply(pkt, src)
|
||||
|
||||
select {
|
||||
case r := <-result:
|
||||
if r.addr != "127.0.0.1:5064" {
|
||||
t.Errorf("addr = %q, want 127.0.0.1:5064", r.addr)
|
||||
}
|
||||
default:
|
||||
t.Fatal("parseReply did not deliver a result")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseReplyOldFormatExplicitIP verifies that an explicit server IP in
|
||||
// the old payload format is read from the correct offset (bytes [4:8]).
|
||||
func TestParseReplyOldFormatExplicitIP(t *testing.T) {
|
||||
se := &searchEngine{
|
||||
waiters: make(map[uint32]*searchWaiter),
|
||||
}
|
||||
|
||||
const searchID = uint32(7)
|
||||
result := make(chan searchResult, 1)
|
||||
se.mu.Lock()
|
||||
se.waiters[searchID] = &searchWaiter{pvName: "TEST:PV2", ch: result}
|
||||
se.mu.Unlock()
|
||||
|
||||
explicitIP := net.ParseIP("10.0.0.5").To4()
|
||||
pkt := buildOldSearchReply(searchID, explicitIP, 5064)
|
||||
src := &net.UDPAddr{IP: net.ParseIP("10.0.0.5"), Port: 5064}
|
||||
|
||||
se.parseReply(pkt, src)
|
||||
|
||||
select {
|
||||
case r := <-result:
|
||||
if r.addr != "10.0.0.5:5064" {
|
||||
t.Errorf("addr = %q, want 10.0.0.5:5064", r.addr)
|
||||
}
|
||||
default:
|
||||
t.Fatal("parseReply did not deliver a result")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseReplyNewFormat verifies the new payload format (IP at [0:4]).
|
||||
func TestParseReplyNewFormat(t *testing.T) {
|
||||
se := &searchEngine{
|
||||
waiters: make(map[uint32]*searchWaiter),
|
||||
}
|
||||
|
||||
const searchID = uint32(99)
|
||||
result := make(chan searchResult, 1)
|
||||
se.mu.Lock()
|
||||
se.waiters[searchID] = &searchWaiter{pvName: "TEST:PV3", ch: result}
|
||||
se.mu.Unlock()
|
||||
|
||||
// New format: IP = 0xFFFFFFFF at [0:4].
|
||||
pkt := EncodeSearchReply(searchID, nil, 5064)
|
||||
src := &net.UDPAddr{IP: net.ParseIP("192.168.1.10"), Port: 5064}
|
||||
|
||||
se.parseReply(pkt, src)
|
||||
|
||||
select {
|
||||
case r := <-result:
|
||||
if r.addr != "192.168.1.10:5064" {
|
||||
t.Errorf("addr = %q, want 192.168.1.10:5064", r.addr)
|
||||
}
|
||||
default:
|
||||
t.Fatal("parseReply did not deliver a result")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user