176 lines
4.9 KiB
Go
176 lines
4.9 KiB
Go
package pva
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"testing"
|
|
)
|
|
|
|
// ---- Header codec -----------------------------------------------------
|
|
|
|
func TestHeaderRoundTrip(t *testing.T) {
|
|
h := PVAHeader{Version: 1, Flags: flagApp, Command: CmdGet, Size: 1234}
|
|
var buf bytes.Buffer
|
|
if err := WriteHeader(&buf, h); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := ReadHeader(&buf)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got.Version != h.Version || got.Command != h.Command || got.Size != h.Size {
|
|
t.Errorf("round-trip header: got %+v want %+v", got, h)
|
|
}
|
|
}
|
|
|
|
func TestReadHeaderBadMagic(t *testing.T) {
|
|
raw := []byte{0xAB, 0x01, 0x00, CmdGet, 0, 0, 0, 0}
|
|
if _, err := ReadHeader(bytes.NewReader(raw)); err == nil {
|
|
t.Error("bad magic: want error")
|
|
}
|
|
}
|
|
|
|
func TestReadHeaderTruncated(t *testing.T) {
|
|
if _, err := ReadHeader(bytes.NewReader([]byte{0xCA, 0x01})); err == nil {
|
|
t.Error("truncated header: want error")
|
|
}
|
|
}
|
|
|
|
func TestHeaderBigEndian(t *testing.T) {
|
|
// Hand-assemble a big-endian server header with size 0x00010203.
|
|
raw := []byte{magic, 0x01, flagBigEndian | flagFromServer, CmdMonitor, 0x00, 0x01, 0x02, 0x03}
|
|
h, err := ReadHeader(bytes.NewReader(raw))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if h.isLittleEndian() {
|
|
t.Error("expected big-endian header")
|
|
}
|
|
if !h.isFromServer() {
|
|
t.Error("expected from-server bit")
|
|
}
|
|
if h.byteOrder() != binary.BigEndian {
|
|
t.Error("byteOrder should be big-endian")
|
|
}
|
|
if h.Size != 0x00010203 {
|
|
t.Errorf("size = %#x, want 0x00010203", h.Size)
|
|
}
|
|
}
|
|
|
|
func TestHeaderPredicates(t *testing.T) {
|
|
le := PVAHeader{Flags: flagApp}
|
|
if !le.isLittleEndian() || le.isFromServer() || le.isControl() {
|
|
t.Errorf("app/LE header predicates wrong: %+v", le)
|
|
}
|
|
ctrl := PVAHeader{Flags: flagControl}
|
|
if !ctrl.isControl() {
|
|
t.Error("control header should report isControl")
|
|
}
|
|
}
|
|
|
|
// ---- BuildMessage -----------------------------------------------------
|
|
|
|
func TestBuildMessage(t *testing.T) {
|
|
payload := []byte{1, 2, 3, 4, 5}
|
|
// Pass the server flag too; BuildMessage must clear it (client direction).
|
|
msg := BuildMessage(CmdCreateChannel, flagFromServer, payload)
|
|
h, err := ReadHeader(bytes.NewReader(msg))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if h.Command != CmdCreateChannel {
|
|
t.Errorf("command = %#x, want %#x", h.Command, CmdCreateChannel)
|
|
}
|
|
if h.isFromServer() {
|
|
t.Error("BuildMessage must clear the from-server flag")
|
|
}
|
|
if int(h.Size) != len(payload) {
|
|
t.Errorf("size = %d, want %d", h.Size, len(payload))
|
|
}
|
|
if got := msg[headerSize:]; !bytes.Equal(got, payload) {
|
|
t.Errorf("payload = %v, want %v", got, payload)
|
|
}
|
|
}
|
|
|
|
// ---- Status decoding --------------------------------------------------
|
|
|
|
func TestReadStatusOKShort(t *testing.T) {
|
|
s, err := ReadStatus(bytes.NewReader([]byte{StatusOK}), binary.LittleEndian)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !s.OK() || s.Error() != "" {
|
|
t.Errorf("short OK status: got %+v", s)
|
|
}
|
|
}
|
|
|
|
func TestReadStatusError(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
buf.WriteByte(StatusError)
|
|
buf.WriteByte(0x04) // compact size 4
|
|
buf.WriteString("boom")
|
|
buf.WriteByte(0x00) // empty stack
|
|
s, err := ReadStatus(&buf, binary.LittleEndian)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if s.OK() {
|
|
t.Error("error status should not be OK")
|
|
}
|
|
if s.Message != "boom" {
|
|
t.Errorf("message = %q, want boom", s.Message)
|
|
}
|
|
if s.Error() == "" {
|
|
t.Error("Error() should be non-empty for an error status")
|
|
}
|
|
}
|
|
|
|
func TestReadStatusTruncated(t *testing.T) {
|
|
if _, err := ReadStatus(bytes.NewReader(nil), binary.LittleEndian); err == nil {
|
|
t.Error("empty status: want error")
|
|
}
|
|
// type byte present but the message string is missing.
|
|
if _, err := ReadStatus(bytes.NewReader([]byte{StatusError}), binary.LittleEndian); err == nil {
|
|
t.Error("status missing message: want error")
|
|
}
|
|
}
|
|
|
|
// ---- readPVAString ----------------------------------------------------
|
|
|
|
func TestReadPVAString(t *testing.T) {
|
|
// Short form.
|
|
short := append([]byte{0x03}, []byte("abc")...)
|
|
s, err := readPVAString(bytes.NewReader(short), binary.LittleEndian)
|
|
if err != nil || s != "abc" {
|
|
t.Errorf("short string: got %q err %v", s, err)
|
|
}
|
|
// Empty.
|
|
s, err = readPVAString(bytes.NewReader([]byte{0x00}), binary.LittleEndian)
|
|
if err != nil || s != "" {
|
|
t.Errorf("empty string: got %q err %v", s, err)
|
|
}
|
|
// Extended form: 0xFF + int32 length + bytes.
|
|
var ext bytes.Buffer
|
|
ext.WriteByte(0xFF)
|
|
_ = binary.Write(&ext, binary.LittleEndian, int32(5))
|
|
ext.WriteString("hello")
|
|
s, err = readPVAString(&ext, binary.LittleEndian)
|
|
if err != nil || s != "hello" {
|
|
t.Errorf("extended string: got %q err %v", s, err)
|
|
}
|
|
}
|
|
|
|
// ---- env --------------------------------------------------------------
|
|
|
|
func TestAddrsFromEnv(t *testing.T) {
|
|
t.Setenv("EPICS_PVA_ADDR_LIST", "")
|
|
if got := addrsFromEnv(); got != nil {
|
|
t.Errorf("empty env: got %v, want nil", got)
|
|
}
|
|
t.Setenv("EPICS_PVA_ADDR_LIST", " 1.2.3.4 5.6.7.8 ")
|
|
got := addrsFromEnv()
|
|
if len(got) != 2 || got[0] != "1.2.3.4" || got[1] != "5.6.7.8" {
|
|
t.Errorf("addr list: got %v", got)
|
|
}
|
|
}
|