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) } }