blob: 7152ceb74cbcdae33fa0fa368b7fd20722dd8982 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vom
import (
"bytes"
"fmt"
"testing"
"v.io/v23/vdl"
"v.io/v23/vom/testdata/types"
)
func TestInterleavedMessageReader(t *testing.T) {
type chunkHeader struct {
msgID int64
finalChunk bool
}
tests := []struct {
name string
hex string
typeMap map[typeId]*vdl.Type
expectedChunks []chunkHeader
}{
// Type message split into chunks.
{
name: "type chunks",
hex: "81" +
crHex(WireCtrlTypeFirstChunk) + "5103060029" +
crHex(WireCtrlTypeChunk) + "06762e696f2f76" +
crHex(WireCtrlTypeLastChunk) + "2e32332f766f6d2f74657374646174612f7465737474797065732e537472756374416e7901010003416e79010fe1e1" +
"52012a08000000010000e1e1",
typeMap: map[typeId]*vdl.Type{
41: vdl.TypeOf(types.StructAny{}),
},
expectedChunks: []chunkHeader{
{
msgID: -41,
},
{},
{
finalChunk: true,
},
{
msgID: 41,
finalChunk: true,
},
},
},
// Value message with types interleaved.
{
name: "interleaved chunks",
hex: "81" +
"5137060029762e696f2f7632332f766f6d2f74657374646174612f7465737474797065732e537472756374416e7901010003416e79010fe1e1" +
crHex(WireCtrlValueFirstChunk) + "520000" +
"5337060029762e696f2f7632332f766f6d2f74657374646174612f7465737474797065732e537472756374416e7901010003416e79010fe1e1" +
crHex(WireCtrlValueChunk) + "012a0400000001" +
crHex(WireCtrlTypeFirstChunk) + "5503060029" +
crHex(WireCtrlTypeChunk) + "06762e696f2f76" +
crHex(WireCtrlTypeLastChunk) + "2e32332f766f6d2f74657374646174612f7465737474797065732e537472756374416e7901010003416e79010fe1e1" +
crHex(WireCtrlValueLastChunk) + "00040000e1e1",
typeMap: map[typeId]*vdl.Type{
41: vdl.TypeOf(types.StructAny{}),
},
expectedChunks: []chunkHeader{
{
msgID: -41,
finalChunk: true,
},
{
msgID: 41,
},
{
msgID: -42,
finalChunk: true,
},
{},
{
msgID: -43,
},
{},
{
finalChunk: true,
},
{
finalChunk: true,
},
},
},
}
for _, test := range tests {
// Helpers to iterate over expected chunks.
var expectedChunkIndex int
chunkRead := func() bool {
expectedChunkIndex++
return expectedChunkIndex < len(test.expectedChunks)
}
expectedChunk := func() *chunkHeader {
if expectedChunkIndex >= len(test.expectedChunks) {
t.Fatalf("%s: chunk index %d out of bounds of slice of length %d", test.name, expectedChunkIndex, test.expectedChunks)
}
return &test.expectedChunks[expectedChunkIndex]
}
bin := hex2Bin(t, test.hex)
mr := newMessageReader(newDecbufFromBytes(bin))
// Fake type decoding by looking up types from a predefined map.
lookupType := func(tid typeId) (*vdl.Type, error) {
t := test.typeMap[tid]
if t == nil {
return nil, fmt.Errorf("type not found")
}
return t, nil
}
// Callback to read a type in the middle of a value read.
readSingleType := func() error {
if expectedChunk().msgID >= 0 {
t.Errorf("%s: expected new type, but got %#v", test.name, expectedChunk())
return nil
}
tid, err := mr.StartTypeMessage()
if err != nil {
t.Fatalf("%s: error in StartTypeMessage: %v", test.name, err)
}
for {
if expectedChunk().msgID != -int64(tid) {
t.Errorf("%s: got chunk %v but expected %v", test.name, -int64(tid), expectedChunk().msgID)
break
}
if expectedChunk().finalChunk != mr.finalChunk {
t.Errorf("%s: final chunk was %v, but expected %v", test.name, mr.finalChunk, expectedChunk().finalChunk)
}
if err := mr.Skip(mr.buf.lim); err != nil {
t.Fatalf("%s: error in Skip(): %v", test.name, err)
}
if expectedChunk().finalChunk {
break
}
if !chunkRead() {
break
}
consumed, err := mr.startChunk()
if err != nil {
t.Fatalf("%s: error in startChunk: %v", test.name, err)
}
if consumed {
t.Fatalf("%s: chunk unexpectedly consumed while reading types", test.name)
}
if mr.curMsgKind != typeMessage {
t.Fatalf("%s: got value message when expected type message at index %d", test.name, expectedChunkIndex)
}
tid = 0
}
if err := mr.EndMessage(); err != nil {
t.Fatalf("%s: error in end message: %v", test.name, err)
}
chunkRead() // expectedChunk() now refers to the message after the type message
return nil
}
mr.SetCallbacks(lookupType, readSingleType)
// Read the value messages until the end of the expected chunk list is reached.
for expectedChunkIndex < len(test.expectedChunks) {
tid, err := mr.StartValueMessage()
if err != nil {
t.Fatalf("%s: error in StartValueMessage: %v", test.name, err)
}
mid := int64(tid)
for {
if expectedChunk().msgID != mid {
t.Errorf("%s: got chunk mid %v but expected %v", test.name, mid, expectedChunk().msgID)
break
}
if err := mr.Skip(mr.buf.lim); err != nil {
t.Fatalf("%s: error in Skip(): %v", test.name, err)
}
if expectedChunk().finalChunk {
break
}
if !chunkRead() {
break
}
err := mr.nextChunk()
if err != nil {
t.Fatalf("%s: error in nextChunk: %v", test.name, err)
}
mid = 0
}
if err := mr.EndMessage(); err != nil {
t.Fatalf("%s: error in end message: %v", test.name, err)
}
chunkRead() // Point to the next message
}
if mr.buf.beg != len(bin) {
// NOTE: This check is invalid if the length of the test bytes exceeds the buffer length and will
// need to be updated if this is ever changed.
t.Errorf("%s: input hex was not fully read. read length was: %d, but full length was %d", test.name, mr.buf.beg, len(bin))
}
}
}
func TestTypeMessageReader(t *testing.T) {
lookupType := func(tid typeId) (*vdl.Type, error) {
switch tid {
case 1:
return vdl.BoolType, nil
case 41:
return vdl.TypeOf(types.NBool(true)), nil
default:
return nil, fmt.Errorf("invalid type id: %d", tid)
}
}
successMr := newMessageReader(newDecbufFromBytes(hex2Bin(t, "81"+
crHex(WireCtrlTypeFirstChunk)+"5120000025762e696f2f7632332f766f6d2f74657374646174612f74657374747970"+
crHex(WireCtrlTypeLastChunk)+"0b65732e4e426f6f6c0101e1")))
successMr.SetCallbacks(lookupType, nil)
mid, err := successMr.StartTypeMessage()
if err != nil {
t.Fatalf("error starting type message: %v", err)
}
if mid != 41 {
t.Errorf("got invalid type id: %v, expected %v", mid, 41)
}
if err := successMr.Skip(43); err != nil {
t.Fatalf("error skipping bytes: %v", err)
}
if err := successMr.EndMessage(); err != nil {
t.Fatalf("error ending message: %v", err)
}
failingMr := newMessageReader(newDecbufFromBytes(hex2Bin(t, "810201")))
failingMr.SetCallbacks(lookupType, nil)
if _, err := failingMr.StartTypeMessage(); err == nil {
t.Fatalf("expected error when reading value message on type stream")
}
}
func TestValueMessageReader(t *testing.T) {
lookupType := func(tid typeId) (*vdl.Type, error) {
switch tid {
case 1:
return vdl.BoolType, nil
case 41:
return vdl.TypeOf(types.NStruct{}), nil
default:
return nil, fmt.Errorf("invalid type id: %d", tid)
}
}
successMr := newMessageReader(newDecbufFromBytes(hex2Bin(t, "81"+
crHex(WireCtrlValueFirstChunk)+"520400010103"+
crHex(WireCtrlValueLastChunk)+"0761626302fff6e1")))
successMr.SetCallbacks(lookupType, nil)
mid, err := successMr.StartValueMessage()
if err != nil {
t.Fatalf("error starting value message: %v", err)
}
if mid != 41 {
t.Errorf("got invalid message id: %v, expected %v", mid, 41)
}
if err := successMr.Skip(11); err != nil {
t.Fatalf("error skipping bytes: %v", err)
}
if err := successMr.EndMessage(); err != nil {
t.Fatalf("error ending message: %v", err)
}
failingMr := newMessageReader(newDecbufFromBytes(hex2Bin(t, "81513f060027762e696f2f7632332f766f6d2f74657374646174612f7465737474797065732e4e53747275637401030001410101e10001420103e10001430109e1e1")))
failingMr.SetCallbacks(lookupType, nil)
if _, err := failingMr.StartValueMessage(); err == nil {
t.Fatalf("expected error when reading type message on value stream")
}
}
func TestReadAllValueBytes(t *testing.T) {
type testCase struct {
Name string
InputTypeHex string
InputValueHex string
ExpectedHex string
NumMessages int
}
// NOTE: More types are tested in raw_value_test.go -- this just tests broad classes.
tests := []testCase{
{
Name: "Bool",
InputValueHex: "0201",
ExpectedHex: "01",
NumMessages: 1,
},
{
Name: "uint16(65534)",
InputValueHex: "08fefffe",
ExpectedHex: "fefffe",
NumMessages: 1,
},
{
Name: "\"abc\"",
InputValueHex: "0603616263",
ExpectedHex: "03616263",
NumMessages: 1,
},
{
Name: "[]byte(\"abc\")",
InputValueHex: "4e03616263",
ExpectedHex: "03616263",
NumMessages: 1,
},
{
Name: "typeobject(bool)",
InputValueHex: "1c010100",
ExpectedHex: "00",
NumMessages: 1,
},
{
Name: "testtypes.NStruct{A: true, B: \"abc\", C: 123}",
InputTypeHex: "513f060027762e696f2f7632332f766f6d2f74657374646174612f7465737474797065732e4e53747275637401030001410101e10001420103e10001430109e1e1",
InputValueHex: "520b0001010361626302fff6e1",
ExpectedHex: "0001010361626302fff6e1",
NumMessages: 1,
},
{
Name: "testtypes.StructAny{Any: false}",
InputTypeHex: "5137060029762e696f2f7632332f766f6d2f74657374646174612f7465737474797065732e537472756374416e7901010003416e79010fe1e1",
InputValueHex: "52010104000000e1",
ExpectedHex: "000000e1",
NumMessages: 1,
},
{
Name: "chunked testtypes.NStruct{A: true, B: \"abc\", C: 123}",
InputTypeHex: crHex(WireCtrlTypeFirstChunk) + "513b060027762e696f2f7632332f766f6d2f74657374646174612f7465737474797065732e4e53747275637401030001410101e10001420103e1000143" +
crHex(WireCtrlTypeLastChunk) + "040109e1e1",
InputValueHex: crHex(WireCtrlValueFirstChunk) + "52050001010361" +
crHex(WireCtrlValueLastChunk) + "06626302fff6e1",
ExpectedHex: "0001010361626302fff6e1",
NumMessages: 1,
},
{
Name: "chunked testtypes.StructAny{Any: false}",
InputTypeHex: "5137060029762e696f2f7632332f766f6d2f74657374646174612f7465737474797065732e537472756374416e7901010003416e79010fe1e1",
InputValueHex: crHex(WireCtrlValueFirstChunk) + "5201000100" +
crHex(WireCtrlValueLastChunk) + "0101030000e1",
ExpectedHex: "000000e1",
NumMessages: 1,
},
{
Name: "two messages testtypes.NStruct{A: true, B: \"abc\", C: 123}",
InputTypeHex: "513f060027762e696f2f7632332f766f6d2f74657374646174612f7465737474797065732e4e53747275637401030001410101e10001420103e10001430109e1e1",
InputValueHex: "520b0001010361626302fff6e1" + // first message
"520b0001010361626302fff6e1", // second message
ExpectedHex: "0001010361626302fff6e1",
NumMessages: 2,
},
}
nextTest:
for _, test := range tests {
// Interleaved
mr := newMessageReader(newDecbufFromBytes(hex2Bin(t, "81"+test.InputTypeHex+test.InputValueHex)))
typeDec := newTypeDecoderInternal(mr)
mr.SetCallbacks(typeDec.lookupType, typeDec.readSingleType)
for i := 0; i < test.NumMessages; i++ {
_, err := mr.StartValueMessage()
if err != nil {
t.Errorf("%s (interleaved): error in start value message: %v", test.Name, err)
continue nextTest
}
b, err := mr.ReadAllValueBytes()
if err != nil {
t.Errorf("%s (interleaved): error in ReadAllBytes(): %v", test.Name, err)
continue nextTest
}
if !bytes.Equal(b, hex2Bin(t, test.ExpectedHex)) {
t.Errorf("%s (interleaved): expected: %s but got %x", test.Name, test.ExpectedHex, b)
continue nextTest
}
if err := mr.EndMessage(); err != nil {
t.Errorf("%s (interleaved): error ending message: %v", test.Name, err)
continue nextTest
}
}
// Split type and value streams
typeMr := newMessageReader(newDecbufFromBytes(hex2Bin(t, "81"+test.InputTypeHex)))
valueMr := newMessageReader(newDecbufFromBytes(hex2Bin(t, "81"+test.InputValueHex)))
typeDec = newTypeDecoderInternal(typeMr)
typeMr.SetCallbacks(typeDec.lookupType, nil)
valueMr.SetCallbacks(typeDec.lookupType, nil)
typeDec.Start()
defer typeDec.Stop()
for i := 0; i < test.NumMessages; i++ {
_, err := valueMr.StartValueMessage()
if err != nil {
t.Errorf("%s (split): error in start value message: %v", test.Name, err)
continue nextTest
}
b, err := valueMr.ReadAllValueBytes()
if err != nil {
t.Errorf("%s (split): error in ReadAllBytes(): %v", test.Name, err)
continue nextTest
}
if !bytes.Equal(b, hex2Bin(t, test.ExpectedHex)) {
t.Errorf("%s (split): expected: %s but got %x", test.Name, test.ExpectedHex, b)
continue nextTest
}
if err := valueMr.EndMessage(); err != nil {
t.Errorf("%s (split): error ending message: %v", test.Name, err)
continue nextTest
}
}
}
}
func crHex(b byte) string {
return fmt.Sprintf("%x", b)
}