| // Copyright 2011 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. |
| |
| // This file contains tests of the GobEncoder/GobDecoder support. |
| |
| package gob |
| |
| import ( |
| "bytes" |
| "errors" |
| "fmt" |
| "io" |
| "net" |
| "reflect" |
| "strings" |
| "testing" |
| "time" |
| ) |
| |
| // Types that implement the GobEncoder/Decoder interfaces. |
| |
| type ByteStruct struct { |
| a byte // not an exported field |
| } |
| |
| type StringStruct struct { |
| s string // not an exported field |
| } |
| |
| type ArrayStruct struct { |
| a [8192]byte // not an exported field |
| } |
| |
| type Gobber int |
| |
| type ValueGobber string // encodes with a value, decodes with a pointer. |
| |
| type BinaryGobber int |
| |
| type BinaryValueGobber string |
| |
| type TextGobber int |
| |
| type TextValueGobber string |
| |
| // The relevant methods |
| |
| func (g *ByteStruct) GobEncode() ([]byte, error) { |
| b := make([]byte, 3) |
| b[0] = g.a |
| b[1] = g.a + 1 |
| b[2] = g.a + 2 |
| return b, nil |
| } |
| |
| func (g *ByteStruct) GobDecode(data []byte) error { |
| if g == nil { |
| return errors.New("NIL RECEIVER") |
| } |
| // Expect N sequential-valued bytes. |
| if len(data) == 0 { |
| return io.EOF |
| } |
| g.a = data[0] |
| for i, c := range data { |
| if c != g.a+byte(i) { |
| return errors.New("invalid data sequence") |
| } |
| } |
| return nil |
| } |
| |
| func (g *StringStruct) GobEncode() ([]byte, error) { |
| return []byte(g.s), nil |
| } |
| |
| func (g *StringStruct) GobDecode(data []byte) error { |
| // Expect N sequential-valued bytes. |
| if len(data) == 0 { |
| return io.EOF |
| } |
| a := data[0] |
| for i, c := range data { |
| if c != a+byte(i) { |
| return errors.New("invalid data sequence") |
| } |
| } |
| g.s = string(data) |
| return nil |
| } |
| |
| func (a *ArrayStruct) GobEncode() ([]byte, error) { |
| return a.a[:], nil |
| } |
| |
| func (a *ArrayStruct) GobDecode(data []byte) error { |
| if len(data) != len(a.a) { |
| return errors.New("wrong length in array decode") |
| } |
| copy(a.a[:], data) |
| return nil |
| } |
| |
| func (g *Gobber) GobEncode() ([]byte, error) { |
| return []byte(fmt.Sprintf("VALUE=%d", *g)), nil |
| } |
| |
| func (g *Gobber) GobDecode(data []byte) error { |
| _, err := fmt.Sscanf(string(data), "VALUE=%d", (*int)(g)) |
| return err |
| } |
| |
| func (g *BinaryGobber) MarshalBinary() ([]byte, error) { |
| return []byte(fmt.Sprintf("VALUE=%d", *g)), nil |
| } |
| |
| func (g *BinaryGobber) UnmarshalBinary(data []byte) error { |
| _, err := fmt.Sscanf(string(data), "VALUE=%d", (*int)(g)) |
| return err |
| } |
| |
| func (g *TextGobber) MarshalText() ([]byte, error) { |
| return []byte(fmt.Sprintf("VALUE=%d", *g)), nil |
| } |
| |
| func (g *TextGobber) UnmarshalText(data []byte) error { |
| _, err := fmt.Sscanf(string(data), "VALUE=%d", (*int)(g)) |
| return err |
| } |
| |
| func (v ValueGobber) GobEncode() ([]byte, error) { |
| return []byte(fmt.Sprintf("VALUE=%s", v)), nil |
| } |
| |
| func (v *ValueGobber) GobDecode(data []byte) error { |
| _, err := fmt.Sscanf(string(data), "VALUE=%s", (*string)(v)) |
| return err |
| } |
| |
| func (v BinaryValueGobber) MarshalBinary() ([]byte, error) { |
| return []byte(fmt.Sprintf("VALUE=%s", v)), nil |
| } |
| |
| func (v *BinaryValueGobber) UnmarshalBinary(data []byte) error { |
| _, err := fmt.Sscanf(string(data), "VALUE=%s", (*string)(v)) |
| return err |
| } |
| |
| func (v TextValueGobber) MarshalText() ([]byte, error) { |
| return []byte(fmt.Sprintf("VALUE=%s", v)), nil |
| } |
| |
| func (v *TextValueGobber) UnmarshalText(data []byte) error { |
| _, err := fmt.Sscanf(string(data), "VALUE=%s", (*string)(v)) |
| return err |
| } |
| |
| // Structs that include GobEncodable fields. |
| |
| type GobTest0 struct { |
| X int // guarantee we have something in common with GobTest* |
| G *ByteStruct |
| } |
| |
| type GobTest1 struct { |
| X int // guarantee we have something in common with GobTest* |
| G *StringStruct |
| } |
| |
| type GobTest2 struct { |
| X int // guarantee we have something in common with GobTest* |
| G string // not a GobEncoder - should give us errors |
| } |
| |
| type GobTest3 struct { |
| X int // guarantee we have something in common with GobTest* |
| G *Gobber |
| B *BinaryGobber |
| T *TextGobber |
| } |
| |
| type GobTest4 struct { |
| X int // guarantee we have something in common with GobTest* |
| V ValueGobber |
| BV BinaryValueGobber |
| TV TextValueGobber |
| } |
| |
| type GobTest5 struct { |
| X int // guarantee we have something in common with GobTest* |
| V *ValueGobber |
| BV *BinaryValueGobber |
| TV *TextValueGobber |
| } |
| |
| type GobTest6 struct { |
| X int // guarantee we have something in common with GobTest* |
| V ValueGobber |
| W *ValueGobber |
| BV BinaryValueGobber |
| BW *BinaryValueGobber |
| TV TextValueGobber |
| TW *TextValueGobber |
| } |
| |
| type GobTest7 struct { |
| X int // guarantee we have something in common with GobTest* |
| V *ValueGobber |
| W ValueGobber |
| BV *BinaryValueGobber |
| BW BinaryValueGobber |
| TV *TextValueGobber |
| TW TextValueGobber |
| } |
| |
| type GobTestIgnoreEncoder struct { |
| X int // guarantee we have something in common with GobTest* |
| } |
| |
| type GobTestValueEncDec struct { |
| X int // guarantee we have something in common with GobTest* |
| G StringStruct // not a pointer. |
| } |
| |
| type GobTestIndirectEncDec struct { |
| X int // guarantee we have something in common with GobTest* |
| G ***StringStruct // indirections to the receiver. |
| } |
| |
| type GobTestArrayEncDec struct { |
| X int // guarantee we have something in common with GobTest* |
| A ArrayStruct // not a pointer. |
| } |
| |
| type GobTestIndirectArrayEncDec struct { |
| X int // guarantee we have something in common with GobTest* |
| A ***ArrayStruct // indirections to a large receiver. |
| } |
| |
| func TestGobEncoderField(t *testing.T) { |
| b := new(bytes.Buffer) |
| // First a field that's a structure. |
| enc := NewEncoder(b) |
| err := enc.Encode(GobTest0{17, &ByteStruct{'A'}}) |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| dec := NewDecoder(b) |
| x := new(GobTest0) |
| err = dec.Decode(x) |
| if err != nil { |
| t.Fatal("decode error:", err) |
| } |
| if x.G.a != 'A' { |
| t.Errorf("expected 'A' got %c", x.G.a) |
| } |
| // Now a field that's not a structure. |
| b.Reset() |
| gobber := Gobber(23) |
| bgobber := BinaryGobber(24) |
| tgobber := TextGobber(25) |
| err = enc.Encode(GobTest3{17, &gobber, &bgobber, &tgobber}) |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| y := new(GobTest3) |
| err = dec.Decode(y) |
| if err != nil { |
| t.Fatal("decode error:", err) |
| } |
| if *y.G != 23 || *y.B != 24 || *y.T != 25 { |
| t.Errorf("expected '23 got %d", *y.G) |
| } |
| } |
| |
| // Even though the field is a value, we can still take its address |
| // and should be able to call the methods. |
| func TestGobEncoderValueField(t *testing.T) { |
| b := new(bytes.Buffer) |
| // First a field that's a structure. |
| enc := NewEncoder(b) |
| err := enc.Encode(&GobTestValueEncDec{17, StringStruct{"HIJKL"}}) |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| dec := NewDecoder(b) |
| x := new(GobTestValueEncDec) |
| err = dec.Decode(x) |
| if err != nil { |
| t.Fatal("decode error:", err) |
| } |
| if x.G.s != "HIJKL" { |
| t.Errorf("expected `HIJKL` got %s", x.G.s) |
| } |
| } |
| |
| // GobEncode/Decode should work even if the value is |
| // more indirect than the receiver. |
| func TestGobEncoderIndirectField(t *testing.T) { |
| b := new(bytes.Buffer) |
| // First a field that's a structure. |
| enc := NewEncoder(b) |
| s := &StringStruct{"HIJKL"} |
| sp := &s |
| err := enc.Encode(GobTestIndirectEncDec{17, &sp}) |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| dec := NewDecoder(b) |
| x := new(GobTestIndirectEncDec) |
| err = dec.Decode(x) |
| if err != nil { |
| t.Fatal("decode error:", err) |
| } |
| if (***x.G).s != "HIJKL" { |
| t.Errorf("expected `HIJKL` got %s", (***x.G).s) |
| } |
| } |
| |
| // Test with a large field with methods. |
| func TestGobEncoderArrayField(t *testing.T) { |
| b := new(bytes.Buffer) |
| enc := NewEncoder(b) |
| var a GobTestArrayEncDec |
| a.X = 17 |
| for i := range a.A.a { |
| a.A.a[i] = byte(i) |
| } |
| err := enc.Encode(&a) |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| dec := NewDecoder(b) |
| x := new(GobTestArrayEncDec) |
| err = dec.Decode(x) |
| if err != nil { |
| t.Fatal("decode error:", err) |
| } |
| for i, v := range x.A.a { |
| if v != byte(i) { |
| t.Errorf("expected %x got %x", byte(i), v) |
| break |
| } |
| } |
| } |
| |
| // Test an indirection to a large field with methods. |
| func TestGobEncoderIndirectArrayField(t *testing.T) { |
| b := new(bytes.Buffer) |
| enc := NewEncoder(b) |
| var a GobTestIndirectArrayEncDec |
| a.X = 17 |
| var array ArrayStruct |
| ap := &array |
| app := &ap |
| a.A = &app |
| for i := range array.a { |
| array.a[i] = byte(i) |
| } |
| err := enc.Encode(a) |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| dec := NewDecoder(b) |
| x := new(GobTestIndirectArrayEncDec) |
| err = dec.Decode(x) |
| if err != nil { |
| t.Fatal("decode error:", err) |
| } |
| for i, v := range (***x.A).a { |
| if v != byte(i) { |
| t.Errorf("expected %x got %x", byte(i), v) |
| break |
| } |
| } |
| } |
| |
| // As long as the fields have the same name and implement the |
| // interface, we can cross-connect them. Not sure it's useful |
| // and may even be bad but it works and it's hard to prevent |
| // without exposing the contents of the object, which would |
| // defeat the purpose. |
| func TestGobEncoderFieldsOfDifferentType(t *testing.T) { |
| // first, string in field to byte in field |
| b := new(bytes.Buffer) |
| enc := NewEncoder(b) |
| err := enc.Encode(GobTest1{17, &StringStruct{"ABC"}}) |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| dec := NewDecoder(b) |
| x := new(GobTest0) |
| err = dec.Decode(x) |
| if err != nil { |
| t.Fatal("decode error:", err) |
| } |
| if x.G.a != 'A' { |
| t.Errorf("expected 'A' got %c", x.G.a) |
| } |
| // now the other direction, byte in field to string in field |
| b.Reset() |
| err = enc.Encode(GobTest0{17, &ByteStruct{'X'}}) |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| y := new(GobTest1) |
| err = dec.Decode(y) |
| if err != nil { |
| t.Fatal("decode error:", err) |
| } |
| if y.G.s != "XYZ" { |
| t.Fatalf("expected `XYZ` got %q", y.G.s) |
| } |
| } |
| |
| // Test that we can encode a value and decode into a pointer. |
| func TestGobEncoderValueEncoder(t *testing.T) { |
| // first, string in field to byte in field |
| b := new(bytes.Buffer) |
| enc := NewEncoder(b) |
| err := enc.Encode(GobTest4{17, ValueGobber("hello"), BinaryValueGobber("Καλημέρα"), TextValueGobber("こんにちは")}) |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| dec := NewDecoder(b) |
| x := new(GobTest5) |
| err = dec.Decode(x) |
| if err != nil { |
| t.Fatal("decode error:", err) |
| } |
| if *x.V != "hello" || *x.BV != "Καλημέρα" || *x.TV != "こんにちは" { |
| t.Errorf("expected `hello` got %s", *x.V) |
| } |
| } |
| |
| // Test that we can use a value then a pointer type of a GobEncoder |
| // in the same encoded value. Bug 4647. |
| func TestGobEncoderValueThenPointer(t *testing.T) { |
| v := ValueGobber("forty-two") |
| w := ValueGobber("six-by-nine") |
| bv := BinaryValueGobber("1nanocentury") |
| bw := BinaryValueGobber("πseconds") |
| tv := TextValueGobber("gravitationalacceleration") |
| tw := TextValueGobber("π²ft/s²") |
| |
| // this was a bug: encoding a GobEncoder by value before a GobEncoder |
| // pointer would cause duplicate type definitions to be sent. |
| |
| b := new(bytes.Buffer) |
| enc := NewEncoder(b) |
| if err := enc.Encode(GobTest6{42, v, &w, bv, &bw, tv, &tw}); err != nil { |
| t.Fatal("encode error:", err) |
| } |
| dec := NewDecoder(b) |
| x := new(GobTest6) |
| if err := dec.Decode(x); err != nil { |
| t.Fatal("decode error:", err) |
| } |
| |
| if got, want := x.V, v; got != want { |
| t.Errorf("v = %q, want %q", got, want) |
| } |
| if got, want := x.W, w; got == nil { |
| t.Errorf("w = nil, want %q", want) |
| } else if *got != want { |
| t.Errorf("w = %q, want %q", *got, want) |
| } |
| |
| if got, want := x.BV, bv; got != want { |
| t.Errorf("bv = %q, want %q", got, want) |
| } |
| if got, want := x.BW, bw; got == nil { |
| t.Errorf("bw = nil, want %q", want) |
| } else if *got != want { |
| t.Errorf("bw = %q, want %q", *got, want) |
| } |
| |
| if got, want := x.TV, tv; got != want { |
| t.Errorf("tv = %q, want %q", got, want) |
| } |
| if got, want := x.TW, tw; got == nil { |
| t.Errorf("tw = nil, want %q", want) |
| } else if *got != want { |
| t.Errorf("tw = %q, want %q", *got, want) |
| } |
| } |
| |
| // Test that we can use a pointer then a value type of a GobEncoder |
| // in the same encoded value. |
| func TestGobEncoderPointerThenValue(t *testing.T) { |
| v := ValueGobber("forty-two") |
| w := ValueGobber("six-by-nine") |
| bv := BinaryValueGobber("1nanocentury") |
| bw := BinaryValueGobber("πseconds") |
| tv := TextValueGobber("gravitationalacceleration") |
| tw := TextValueGobber("π²ft/s²") |
| |
| b := new(bytes.Buffer) |
| enc := NewEncoder(b) |
| if err := enc.Encode(GobTest7{42, &v, w, &bv, bw, &tv, tw}); err != nil { |
| t.Fatal("encode error:", err) |
| } |
| dec := NewDecoder(b) |
| x := new(GobTest7) |
| if err := dec.Decode(x); err != nil { |
| t.Fatal("decode error:", err) |
| } |
| |
| if got, want := x.V, v; got == nil { |
| t.Errorf("v = nil, want %q", want) |
| } else if *got != want { |
| t.Errorf("v = %q, want %q", *got, want) |
| } |
| if got, want := x.W, w; got != want { |
| t.Errorf("w = %q, want %q", got, want) |
| } |
| |
| if got, want := x.BV, bv; got == nil { |
| t.Errorf("bv = nil, want %q", want) |
| } else if *got != want { |
| t.Errorf("bv = %q, want %q", *got, want) |
| } |
| if got, want := x.BW, bw; got != want { |
| t.Errorf("bw = %q, want %q", got, want) |
| } |
| |
| if got, want := x.TV, tv; got == nil { |
| t.Errorf("tv = nil, want %q", want) |
| } else if *got != want { |
| t.Errorf("tv = %q, want %q", *got, want) |
| } |
| if got, want := x.TW, tw; got != want { |
| t.Errorf("tw = %q, want %q", got, want) |
| } |
| } |
| |
| func TestGobEncoderFieldTypeError(t *testing.T) { |
| // GobEncoder to non-decoder: error |
| b := new(bytes.Buffer) |
| enc := NewEncoder(b) |
| err := enc.Encode(GobTest1{17, &StringStruct{"ABC"}}) |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| dec := NewDecoder(b) |
| x := &GobTest2{} |
| err = dec.Decode(x) |
| if err == nil { |
| t.Fatal("expected decode error for mismatched fields (encoder to non-decoder)") |
| } |
| if !strings.Contains(err.Error(), "type") { |
| t.Fatal("expected type error; got", err) |
| } |
| // Non-encoder to GobDecoder: error |
| b.Reset() |
| err = enc.Encode(GobTest2{17, "ABC"}) |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| y := &GobTest1{} |
| err = dec.Decode(y) |
| if err == nil { |
| t.Fatal("expected decode error for mismatched fields (non-encoder to decoder)") |
| } |
| if !strings.Contains(err.Error(), "type") { |
| t.Fatal("expected type error; got", err) |
| } |
| } |
| |
| // Even though ByteStruct is a struct, it's treated as a singleton at the top level. |
| func TestGobEncoderStructSingleton(t *testing.T) { |
| b := new(bytes.Buffer) |
| enc := NewEncoder(b) |
| err := enc.Encode(&ByteStruct{'A'}) |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| dec := NewDecoder(b) |
| x := new(ByteStruct) |
| err = dec.Decode(x) |
| if err != nil { |
| t.Fatal("decode error:", err) |
| } |
| if x.a != 'A' { |
| t.Errorf("expected 'A' got %c", x.a) |
| } |
| } |
| |
| func TestGobEncoderNonStructSingleton(t *testing.T) { |
| b := new(bytes.Buffer) |
| enc := NewEncoder(b) |
| var g Gobber = 1234 |
| err := enc.Encode(&g) |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| dec := NewDecoder(b) |
| var x Gobber |
| err = dec.Decode(&x) |
| if err != nil { |
| t.Fatal("decode error:", err) |
| } |
| if x != 1234 { |
| t.Errorf("expected 1234 got %d", x) |
| } |
| } |
| |
| func TestGobEncoderIgnoreStructField(t *testing.T) { |
| b := new(bytes.Buffer) |
| // First a field that's a structure. |
| enc := NewEncoder(b) |
| err := enc.Encode(GobTest0{17, &ByteStruct{'A'}}) |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| dec := NewDecoder(b) |
| x := new(GobTestIgnoreEncoder) |
| err = dec.Decode(x) |
| if err != nil { |
| t.Fatal("decode error:", err) |
| } |
| if x.X != 17 { |
| t.Errorf("expected 17 got %c", x.X) |
| } |
| } |
| |
| func TestGobEncoderIgnoreNonStructField(t *testing.T) { |
| b := new(bytes.Buffer) |
| // First a field that's a structure. |
| enc := NewEncoder(b) |
| gobber := Gobber(23) |
| bgobber := BinaryGobber(24) |
| tgobber := TextGobber(25) |
| err := enc.Encode(GobTest3{17, &gobber, &bgobber, &tgobber}) |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| dec := NewDecoder(b) |
| x := new(GobTestIgnoreEncoder) |
| err = dec.Decode(x) |
| if err != nil { |
| t.Fatal("decode error:", err) |
| } |
| if x.X != 17 { |
| t.Errorf("expected 17 got %c", x.X) |
| } |
| } |
| |
| func TestGobEncoderIgnoreNilEncoder(t *testing.T) { |
| b := new(bytes.Buffer) |
| // First a field that's a structure. |
| enc := NewEncoder(b) |
| err := enc.Encode(GobTest0{X: 18}) // G is nil |
| if err != nil { |
| t.Fatal("encode error:", err) |
| } |
| dec := NewDecoder(b) |
| x := new(GobTest0) |
| err = dec.Decode(x) |
| if err != nil { |
| t.Fatal("decode error:", err) |
| } |
| if x.X != 18 { |
| t.Errorf("expected x.X = 18, got %v", x.X) |
| } |
| if x.G != nil { |
| t.Errorf("expected x.G = nil, got %v", x.G) |
| } |
| } |
| |
| type gobDecoderBug0 struct { |
| foo, bar string |
| } |
| |
| func (br *gobDecoderBug0) String() string { |
| return br.foo + "-" + br.bar |
| } |
| |
| func (br *gobDecoderBug0) GobEncode() ([]byte, error) { |
| return []byte(br.String()), nil |
| } |
| |
| func (br *gobDecoderBug0) GobDecode(b []byte) error { |
| br.foo = "foo" |
| br.bar = "bar" |
| return nil |
| } |
| |
| // This was a bug: the receiver has a different indirection level |
| // than the variable. |
| func TestGobEncoderExtraIndirect(t *testing.T) { |
| gdb := &gobDecoderBug0{"foo", "bar"} |
| buf := new(bytes.Buffer) |
| e := NewEncoder(buf) |
| if err := e.Encode(gdb); err != nil { |
| t.Fatalf("encode: %v", err) |
| } |
| d := NewDecoder(buf) |
| var got *gobDecoderBug0 |
| if err := d.Decode(&got); err != nil { |
| t.Fatalf("decode: %v", err) |
| } |
| if got.foo != gdb.foo || got.bar != gdb.bar { |
| t.Errorf("got = %q, want %q", got, gdb) |
| } |
| } |
| |
| // Another bug: this caused a crash with the new Go1 Time type. |
| // We throw in a gob-encoding array, to test another case of isZero, |
| // and a struct containing a nil interface, to test a third. |
| type isZeroBug struct { |
| T time.Time |
| S string |
| I int |
| A isZeroBugArray |
| F isZeroBugInterface |
| } |
| |
| type isZeroBugArray [2]uint8 |
| |
| // Receiver is value, not pointer, to test isZero of array. |
| func (a isZeroBugArray) GobEncode() (b []byte, e error) { |
| b = append(b, a[:]...) |
| return b, nil |
| } |
| |
| func (a *isZeroBugArray) GobDecode(data []byte) error { |
| if len(data) != len(a) { |
| return io.EOF |
| } |
| a[0] = data[0] |
| a[1] = data[1] |
| return nil |
| } |
| |
| type isZeroBugInterface struct { |
| I any |
| } |
| |
| func (i isZeroBugInterface) GobEncode() (b []byte, e error) { |
| return []byte{}, nil |
| } |
| |
| func (i *isZeroBugInterface) GobDecode(data []byte) error { |
| return nil |
| } |
| |
| func TestGobEncodeIsZero(t *testing.T) { |
| x := isZeroBug{time.Unix(1e9, 0), "hello", -55, isZeroBugArray{1, 2}, isZeroBugInterface{}} |
| b := new(bytes.Buffer) |
| enc := NewEncoder(b) |
| err := enc.Encode(x) |
| if err != nil { |
| t.Fatal("encode:", err) |
| } |
| var y isZeroBug |
| dec := NewDecoder(b) |
| err = dec.Decode(&y) |
| if err != nil { |
| t.Fatal("decode:", err) |
| } |
| if x != y { |
| t.Fatalf("%v != %v", x, y) |
| } |
| } |
| |
| func TestGobEncodePtrError(t *testing.T) { |
| var err error |
| b := new(bytes.Buffer) |
| enc := NewEncoder(b) |
| err = enc.Encode(&err) |
| if err != nil { |
| t.Fatal("encode:", err) |
| } |
| dec := NewDecoder(b) |
| err2 := fmt.Errorf("foo") |
| err = dec.Decode(&err2) |
| if err != nil { |
| t.Fatal("decode:", err) |
| } |
| if err2 != nil { |
| t.Fatalf("expected nil, got %v", err2) |
| } |
| } |
| |
| func TestNetIP(t *testing.T) { |
| // Encoding of net.IP{1,2,3,4} in Go 1.1. |
| enc := []byte{0x07, 0x0a, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04} |
| |
| var ip net.IP |
| err := NewDecoder(bytes.NewReader(enc)).Decode(&ip) |
| if err != nil { |
| t.Fatalf("decode: %v", err) |
| } |
| if ip.String() != "1.2.3.4" { |
| t.Errorf("decoded to %v, want 1.2.3.4", ip.String()) |
| } |
| } |
| |
| func TestIgnoreDepthLimit(t *testing.T) { |
| // We don't test the actual depth limit because it requires building an |
| // extremely large message, which takes quite a while. |
| oldNestingDepth := maxIgnoreNestingDepth |
| maxIgnoreNestingDepth = 100 |
| defer func() { maxIgnoreNestingDepth = oldNestingDepth }() |
| b := new(bytes.Buffer) |
| enc := NewEncoder(b) |
| typ := reflect.TypeFor[int]() |
| nested := reflect.ArrayOf(1, typ) |
| for i := 0; i < 100; i++ { |
| nested = reflect.ArrayOf(1, nested) |
| } |
| badStruct := reflect.New(reflect.StructOf([]reflect.StructField{{Name: "F", Type: nested}})) |
| enc.Encode(badStruct.Interface()) |
| dec := NewDecoder(b) |
| var output struct{ Hello int } |
| expectedErr := "invalid nesting depth" |
| if err := dec.Decode(&output); err == nil || err.Error() != expectedErr { |
| t.Errorf("Decode didn't fail with depth limit of 100: want %q, got %q", expectedErr, err) |
| } |
| } |