| // Copyright 2020 The Go 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 codec |
| |
| import ( |
| "math" |
| "strings" |
| "testing" |
| |
| "github.com/google/go-cmp/cmp" |
| ) |
| |
| func TestLowLevelIO(t *testing.T) { |
| var ( |
| b byte = 15 |
| bs []byte = []byte{4, 10, 8} |
| s = "hello" |
| u32 uint32 = 999 |
| u64 uint64 = math.MaxUint32 + 1 |
| ) |
| |
| e := NewEncoder() |
| e.writeByte(b) |
| e.writeBytes(bs) |
| e.writeString(s) |
| e.writeUint32(u32) |
| e.writeUint64(u64) |
| |
| d := NewDecoder(e.Bytes()) |
| d.decodeInitial() |
| if got := d.readByte(); got != b { |
| t.Fatalf("got %d, want %d", got, b) |
| } |
| if got := d.readBytes(len(bs)); !cmp.Equal(got, bs) { |
| t.Fatalf("got %v, want %v", got, bs) |
| } |
| if got := d.readString(len(s)); got != s { |
| t.Fatalf("got %q, want %q", got, s) |
| } |
| if got := d.readUint32(); got != u32 { |
| t.Errorf("got %d, want %d", got, u32) |
| } |
| if got := d.readUint64(); got != u64 { |
| t.Errorf("got %d, want %d", got, u64) |
| } |
| } |
| |
| func TestUint(t *testing.T) { |
| e := NewEncoder() |
| uints := []uint64{99, 999, math.MaxUint32 + 1} |
| for _, u := range uints { |
| e.EncodeUint(u) |
| } |
| d := NewDecoder(e.Bytes()) |
| d.decodeInitial() |
| for _, want := range uints { |
| if got := d.DecodeUint(); got != want { |
| t.Errorf("got %d, want %d", got, want) |
| } |
| } |
| } |
| |
| func TestInt(t *testing.T) { |
| e := NewEncoder() |
| ints := []int64{99, 999, math.MaxUint32 + 1, -123} |
| for _, i := range ints { |
| e.EncodeInt(i) |
| } |
| d := NewDecoder(e.Bytes()) |
| d.decodeInitial() |
| for _, want := range ints { |
| if got := d.DecodeInt(); got != want { |
| t.Errorf("got %d, want %d", got, want) |
| } |
| } |
| } |
| |
| func TestBasicTypes(t *testing.T) { |
| e := NewEncoder() |
| var ( |
| by = []byte{1, 2, 3} |
| s = "hello" |
| b = true |
| f = 3.14 |
| ) |
| e.EncodeBytes(by) |
| e.EncodeString(s) |
| e.EncodeBool(b) |
| e.EncodeFloat(f) |
| |
| d := NewDecoder(e.Bytes()) |
| d.decodeInitial() |
| gots := []interface{}{ |
| d.DecodeBytes(), |
| d.DecodeString(), |
| d.DecodeBool(), |
| d.DecodeFloat(), |
| } |
| wants := []interface{}{by, s, b, f} |
| if !cmp.Equal(gots, wants) { |
| t.Errorf("got %v, want %v", gots, wants) |
| } |
| } |
| |
| func TestList(t *testing.T) { |
| e := NewEncoder() |
| want := []string{"Green", "eggs", "and", "ham"} |
| e.StartList(len(want)) |
| for _, s := range want { |
| e.EncodeString(s) |
| } |
| |
| d := NewDecoder(e.Bytes()) |
| d.decodeInitial() |
| n := d.StartList() |
| if n < 0 { |
| t.Fatal("got nil") |
| } |
| got := make([]string, n) |
| for i := 0; i < n; i++ { |
| got[i] = d.DecodeString() |
| } |
| if !cmp.Equal(got, want) { |
| t.Errorf("got %q, want %q", got, want) |
| } |
| } |
| |
| func TestAny(t *testing.T) { |
| want := []interface{}{"bar", nil, 1, -5, 98.6, uint64(1 << 63), "Luke Luck likes lakes", true} |
| e := NewEncoder() |
| for _, w := range want { |
| e.EncodeAny(w) |
| } |
| |
| d := NewDecoder(e.Bytes()) |
| d.decodeInitial() |
| for _, w := range want { |
| g := d.DecodeAny() |
| if g != w { |
| t.Errorf("got %v, want %v", g, w) |
| } |
| } |
| } |
| |
| func TestEncodeDecode(t *testing.T) { |
| want := []interface{}{"bar", nil, 1, -5, 98.6, uint64(1 << 63), "Luke Luck likes lakes", true} |
| e := NewEncoder() |
| for _, w := range want { |
| if err := e.Encode(w); err != nil { |
| t.Fatal(err) |
| } |
| } |
| |
| d := NewDecoder(e.Bytes()) |
| for _, w := range want { |
| g, err := d.Decode() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if g != w { |
| t.Errorf("got %v, want %v", g, w) |
| } |
| } |
| } |
| |
| func TestEncodeErrors(t *testing.T) { |
| // The only encoding error is an unregistered type. |
| e := NewEncoder() |
| type MyInt int |
| checkMessage(t, e.Encode(MyInt(0)), "unregistered") |
| } |
| |
| func TestDecodeErrors(t *testing.T) { |
| for _, test := range []struct { |
| offset int |
| change byte |
| message string |
| }{ |
| // d.buf[d.i:] should look like: nValues 2 0 nBytes 4 ... |
| // Induce errors by changing some bytes. |
| {0, startCode, "bad code"}, // mess with the initial code |
| {1, 5, "bad list length"}, // mess with the list length |
| {2, 1, "out of range"}, // mess with the type number |
| {3, nValuesCode, "bad code"}, // mess with the uint code |
| {4, 5, "bad length"}, // mess with the uint length |
| } { |
| d := NewDecoder(mustEncode(t, uint64(3000))) |
| d.decodeInitial() |
| d.buf[d.i+test.offset] = test.change |
| _, err := d.Decode() |
| checkMessage(t, err, test.message) |
| } |
| } |
| |
| func mustEncode(t *testing.T, x interface{}) []byte { |
| t.Helper() |
| e := NewEncoder() |
| if err := e.Encode(x); err != nil { |
| t.Fatal(err) |
| } |
| return e.Bytes() |
| } |
| |
| func checkMessage(t *testing.T, err error, target string) { |
| t.Helper() |
| if err == nil { |
| t.Error("want error, got nil") |
| } |
| if !strings.Contains(err.Error(), target) { |
| t.Errorf("error %q does not contain %q", err, target) |
| } |
| } |
| |
| func TestSkip(t *testing.T) { |
| e := NewEncoder() |
| values := []interface{}{ |
| 1, |
| false, |
| "yes", |
| "no", |
| 65000, |
| } |
| for _, v := range values { |
| if err := e.Encode(v); err != nil { |
| t.Fatal(err) |
| } |
| } |
| |
| d := NewDecoder(e.Bytes()) |
| // Skip odd indexes. |
| for i, want := range values { |
| if i%2 == 0 { |
| got, err := d.Decode() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if !cmp.Equal(got, want) { |
| t.Errorf("got %v, want %v", got, want) |
| } |
| } else { |
| d.skip() |
| } |
| } |
| } |
| |
| type node struct { |
| Value int |
| Next *node |
| } |
| |
| func TestSharing(t *testing.T) { |
| n := &node{Value: 1, Next: &node{Value: 2}} |
| n.Next.Next = n // create a cycle |
| d := NewDecoder(mustEncode(t, n)) |
| g, err := d.Decode() |
| if err != nil { |
| t.Fatal(err) |
| } |
| got := g.(*node) |
| if !cmp.Equal(got, n) { |
| t.Error("did not preserve cycle") |
| } |
| } |
| |
| // The following three functions were generated with |
| // Generate(os.Stdout, "p", node{}) |
| // and pasted here. They should be kept in sync |
| // with the output of Generate. |
| func encode_node(e *Encoder, x *node) { |
| if !e.StartStruct(x == nil, x) { |
| return |
| } |
| if x.Value != 0 { |
| e.EncodeUint(0) |
| e.EncodeInt(int64(x.Value)) |
| } |
| if x.Next != nil { |
| e.EncodeUint(1) |
| encode_node(e, x.Next) |
| } |
| e.EndStruct() |
| } |
| |
| func decode_node(d *Decoder, p **node) { |
| proceed, ref := d.StartStruct() |
| if !proceed { |
| return |
| } |
| if ref != nil { |
| *p = ref.(*node) |
| return |
| } |
| var x node |
| d.StoreRef(&x) |
| for { |
| n := d.NextStructField() |
| if n < 0 { |
| break |
| } |
| switch n { |
| case 0: |
| x.Value = int(d.DecodeInt()) |
| case 1: |
| decode_node(d, &x.Next) |
| |
| default: |
| d.UnknownField("node", n) |
| } |
| *p = &x |
| } |
| } |
| |
| func init() { |
| Register(&node{}, |
| func(e *Encoder, x interface{}) { encode_node(e, x.(*node)) }, |
| func(d *Decoder) interface{} { |
| var x *node |
| decode_node(d, &x) |
| return x |
| }) |
| } |