package proto import ( "encoding/binary" "fmt" "io/ioutil" "math" "reflect" "testing" ) func TestEncodeDecodeVarint(t *testing.T) { b := [8]byte{} n, err := encodeVarint(b[:], 42) if err != nil { t.Fatal(err) } v, n2, err := decodeVarint(b[:n]) if err != nil { t.Fatal(err) } if v != 42 { t.Errorf("decoded value mismatch: want %d, got %d", 42, v) } if n2 != n { t.Errorf("decoded byte count mismatch: want %d, got %d", n, n2) } } func TestEncodeDecodeVarintZigZag(t *testing.T) { b := [8]byte{} n, err := encodeVarintZigZag(b[:], -42) if err != nil { t.Fatal(err) } v, n2, err := decodeVarintZigZag(b[:n]) if err != nil { t.Fatal(err) } if v != -42 { t.Errorf("decoded value mismatch: want %d, got %d", -42, v) } if n2 != n { t.Errorf("decoded byte count mismatch: want %d, got %d", n, n2) } } func TestEncodeDecodeTag(t *testing.T) { b := [8]byte{} n, err := encodeTag(b[:], 1, varint) if err != nil { t.Fatal(err) } num, typ, n2, err := decodeTag(b[:n]) if err != nil { t.Fatal(err) } if num != 1 { t.Errorf("decoded field number mismatch: want %d, got %d", 1, num) } if typ != varint { t.Errorf("decoded wire type mismatch: want %d, got %d", varint, typ) } if n2 != n { t.Errorf("decoded byte count mismatch: want %d, got %d", n, n2) } } type key struct { Hi uint64 Lo uint64 } type message struct { A int B int C int S submessage } type submessage struct { X string Y string } type structWithMap struct { M map[int]string } type custom [16]byte func (c *custom) Size() int { return len(c) } func (c *custom) MarshalTo(b []byte) (int, error) { return copy(b, c[:]), nil } func (c *custom) Unmarshal(b []byte) error { copy(c[:], b) return nil } type messageWithRawMessage struct { Raw RawMessage } type messageWithCustomField struct { Custom custom } func TestMarshalUnmarshal(t *testing.T) { intVal := 42 values := []interface{}{ // bool true, false, // zig-zag varint 0, 1, 1234567890, -1, -1234567890, // sfixed32 int32(0), int32(math.MinInt32), int32(math.MaxInt32), // sfixed64 int64(0), int64(math.MinInt64), int64(math.MaxInt64), // varint uint(0), uint(1), uint(1234567890), // fixed32 uint32(0), uint32(1234567890), // fixed64 uint64(0), uint64(1234567890), // float float32(0), float32(math.Copysign(0, -1)), float32(0.1234), // double float64(0), float64(math.Copysign(0, -1)), float64(0.1234), // string "", "A", "Hello World!", // bytes ([]byte)(nil), []byte(""), []byte("A"), []byte("Hello World!"), // messages struct{ B bool }{B: false}, struct{ B bool }{B: true}, struct{ I int }{I: 0}, struct{ I int }{I: 1}, struct{ I32 int32 }{I32: 0}, struct{ I32 int32 }{I32: -1234567890}, struct{ I64 int64 }{I64: 0}, struct{ I64 int64 }{I64: -1234567890}, struct{ U int }{U: 0}, struct{ U int }{U: 1}, struct{ U32 uint32 }{U32: 0}, struct{ U32 uint32 }{U32: 1234567890}, struct{ U64 uint64 }{U64: 0}, struct{ U64 uint64 }{U64: 1234567890}, struct{ F32 float32 }{F32: 0}, struct{ F32 float32 }{F32: 0.1234}, struct{ F64 float64 }{F64: 0}, struct{ F64 float64 }{F64: 0.1234}, struct{ S string }{S: ""}, struct{ S string }{S: "E"}, struct{ B []byte }{B: nil}, struct{ B []byte }{B: []byte{}}, struct{ B []byte }{B: []byte{1, 2, 3}}, &message{ A: 1, B: 2, C: 3, S: submessage{ X: "hello", Y: "world", }, }, struct { Min int64 `protobuf:"zigzag64,1,opt,name=min,proto3"` Max int64 `protobuf:"zigzag64,2,opt,name=min,proto3"` }{Min: math.MinInt64, Max: math.MaxInt64}, // pointers struct{ M *message }{M: nil}, struct { M1 *message M2 *message M3 *message }{ M1: &message{A: 10, B: 100, C: 1000}, M2: &message{S: submessage{X: "42"}}, }, // byte arrays [0]byte{}, [8]byte{}, [16]byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}, &[...]byte{}, &[...]byte{3, 2, 1}, // slices (repeated) struct{ S []int }{S: nil}, struct{ S []int }{S: []int{0}}, struct{ S []int }{S: []int{0, 0, 0}}, struct{ S []int }{S: []int{1, 2, 3}}, struct{ S []string }{S: nil}, struct{ S []string }{S: []string{""}}, struct{ S []string }{S: []string{"A", "B", "C"}}, struct{ K []key }{ K: []key{ {Hi: 0, Lo: 0}, {Hi: 0, Lo: 1}, {Hi: 0, Lo: 2}, {Hi: 0, Lo: 3}, {Hi: 0, Lo: 4}, }, }, // maps (repeated) struct{ M map[int]string }{}, struct{ M map[int]string }{ M: map[int]string{0: ""}, }, struct{ M map[int]string }{ M: map[int]string{0: "A", 1: "B", 2: "C"}, }, &struct{ M map[int]string }{ M: map[int]string{0: "A", 1: "B", 2: "C"}, }, struct { M1 map[int]int M2 map[string]string M3 map[string]message M4 map[string]*message M5 map[key]uint }{ M1: map[int]int{0: 1}, M2: map[string]string{"": "A"}, M3: map[string]message{ "m0": message{}, "m1": message{A: 42}, "m3": message{S: submessage{X: "X", Y: "Y"}}, }, M4: map[string]*message{ "m0": &message{}, "m1": &message{A: 42}, "m3": &message{S: submessage{X: "X", Y: "Y"}}, }, M5: map[key]uint{ key{Hi: 0, Lo: 0}: 0, key{Hi: 1, Lo: 0}: 1, key{Hi: 0, Lo: 1}: 2, key{Hi: math.MaxUint64, Lo: math.MaxUint64}: 3, }, }, // more complex inlined types use cases struct{ I *int }{}, struct{ I *int }{I: new(int)}, struct{ I *int }{I: &intVal}, struct{ M *message }{}, struct{ M *message }{M: new(message)}, struct{ M map[int]int }{}, struct{ M map[int]int }{M: map[int]int{}}, struct{ S structWithMap }{ S: structWithMap{ M: map[int]string{0: "A", 1: "B", 2: "C"}, }, }, &struct{ S structWithMap }{ S: structWithMap{ M: map[int]string{0: "A", 1: "B", 2: "C"}, }, }, // raw messages RawMessage(nil), RawMessage{0x08, 0x96, 0x01}, messageWithRawMessage{ Raw: RawMessage{1, 2, 3, 4}, }, struct { A int B string C RawMessage }{A: 42, B: "Hello World!", C: RawMessage{1, 2, 3, 4}}, // custom messages custom{}, custom{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, messageWithCustomField{ Custom: custom{1: 42}, }, struct { A int B string C custom }{A: 42, B: "Hello World!", C: custom{1: 42}}, } for _, v := range values { t.Run(fmt.Sprintf("%T/%+v", v, v), func(t *testing.T) { n := Size(v) b, err := Marshal(v) if err != nil { t.Fatal(err) } if n != len(b) { t.Fatalf("value size and buffer length mismatch (%d != %d)", n, len(b)) } p := reflect.New(reflect.TypeOf(v)) if err := Unmarshal(b, p.Interface()); err != nil { t.Fatal(err) } x := p.Elem().Interface() if !reflect.DeepEqual(v, x) { t.Errorf("values mismatch:\nexpected: %#v\nfound: %#v", v, x) } }) } } func loadProtobuf(t *testing.T, fileName string) RawMessage { b, err := ioutil.ReadFile("fixtures/protobuf/" + fileName) if err != nil { t.Fatal(err) } return RawMessage(b) } func makeVarint(v uint64) []byte { b := [12]byte{} n := binary.PutUvarint(b[:], v) return b[:n] } func makeFixed32(v uint32) []byte { b := [4]byte{} binary.LittleEndian.PutUint32(b[:], v) return b[:] } func makeFixed64(v uint64) []byte { b := [8]byte{} binary.LittleEndian.PutUint64(b[:], v) return b[:] }