| // Copyright 2009 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 gob |
| |
| import ( |
| "bytes" |
| "reflect" |
| "sync" |
| "testing" |
| ) |
| |
| type typeT struct { |
| id typeId |
| str string |
| } |
| |
| var basicTypes = []typeT{ |
| {tBool, "bool"}, |
| {tInt, "int"}, |
| {tUint, "uint"}, |
| {tFloat, "float"}, |
| {tBytes, "bytes"}, |
| {tString, "string"}, |
| } |
| |
| func getTypeUnlocked(name string, rt reflect.Type) gobType { |
| typeLock.Lock() |
| defer typeLock.Unlock() |
| t, err := getBaseType(name, rt) |
| if err != nil { |
| panic("getTypeUnlocked: " + err.Error()) |
| } |
| return t |
| } |
| |
| // Sanity checks |
| func TestBasic(t *testing.T) { |
| for _, tt := range basicTypes { |
| if tt.id.string() != tt.str { |
| t.Errorf("checkType: expected %q got %s", tt.str, tt.id.string()) |
| } |
| if tt.id == 0 { |
| t.Errorf("id for %q is zero", tt.str) |
| } |
| } |
| } |
| |
| // Reregister some basic types to check registration is idempotent. |
| func TestReregistration(t *testing.T) { |
| newtyp := getTypeUnlocked("int", reflect.TypeOf(int(0))) |
| if newtyp != tInt.gobType() { |
| t.Errorf("reregistration of %s got new type", newtyp.string()) |
| } |
| newtyp = getTypeUnlocked("uint", reflect.TypeOf(uint(0))) |
| if newtyp != tUint.gobType() { |
| t.Errorf("reregistration of %s got new type", newtyp.string()) |
| } |
| newtyp = getTypeUnlocked("string", reflect.TypeOf("hello")) |
| if newtyp != tString.gobType() { |
| t.Errorf("reregistration of %s got new type", newtyp.string()) |
| } |
| } |
| |
| func TestArrayType(t *testing.T) { |
| var a3 [3]int |
| a3int := getTypeUnlocked("foo", reflect.TypeOf(a3)) |
| newa3int := getTypeUnlocked("bar", reflect.TypeOf(a3)) |
| if a3int != newa3int { |
| t.Errorf("second registration of [3]int creates new type") |
| } |
| var a4 [4]int |
| a4int := getTypeUnlocked("goo", reflect.TypeOf(a4)) |
| if a3int == a4int { |
| t.Errorf("registration of [3]int creates same type as [4]int") |
| } |
| var b3 [3]bool |
| a3bool := getTypeUnlocked("", reflect.TypeOf(b3)) |
| if a3int == a3bool { |
| t.Errorf("registration of [3]bool creates same type as [3]int") |
| } |
| str := a3bool.string() |
| expected := "[3]bool" |
| if str != expected { |
| t.Errorf("array printed as %q; expected %q", str, expected) |
| } |
| } |
| |
| func TestSliceType(t *testing.T) { |
| var s []int |
| sint := getTypeUnlocked("slice", reflect.TypeOf(s)) |
| var news []int |
| newsint := getTypeUnlocked("slice1", reflect.TypeOf(news)) |
| if sint != newsint { |
| t.Errorf("second registration of []int creates new type") |
| } |
| var b []bool |
| sbool := getTypeUnlocked("", reflect.TypeOf(b)) |
| if sbool == sint { |
| t.Errorf("registration of []bool creates same type as []int") |
| } |
| str := sbool.string() |
| expected := "[]bool" |
| if str != expected { |
| t.Errorf("slice printed as %q; expected %q", str, expected) |
| } |
| } |
| |
| func TestMapType(t *testing.T) { |
| var m map[string]int |
| mapStringInt := getTypeUnlocked("map", reflect.TypeOf(m)) |
| var newm map[string]int |
| newMapStringInt := getTypeUnlocked("map1", reflect.TypeOf(newm)) |
| if mapStringInt != newMapStringInt { |
| t.Errorf("second registration of map[string]int creates new type") |
| } |
| var b map[string]bool |
| mapStringBool := getTypeUnlocked("", reflect.TypeOf(b)) |
| if mapStringBool == mapStringInt { |
| t.Errorf("registration of map[string]bool creates same type as map[string]int") |
| } |
| str := mapStringBool.string() |
| expected := "map[string]bool" |
| if str != expected { |
| t.Errorf("map printed as %q; expected %q", str, expected) |
| } |
| } |
| |
| type Bar struct { |
| X string |
| } |
| |
| // This structure has pointers and refers to itself, making it a good test case. |
| type Foo struct { |
| A int |
| B int32 // will become int |
| C string |
| D []byte |
| E *float64 // will become float64 |
| F ****float64 // will become float64 |
| G *Bar |
| H *Bar // should not interpolate the definition of Bar again |
| I *Foo // will not explode |
| } |
| |
| func TestStructType(t *testing.T) { |
| sstruct := getTypeUnlocked("Foo", reflect.TypeOf(Foo{})) |
| str := sstruct.string() |
| // If we can print it correctly, we built it correctly. |
| expected := "Foo = struct { A int; B int; C string; D bytes; E float; F float; G Bar = struct { X string; }; H Bar; I Foo; }" |
| if str != expected { |
| t.Errorf("struct printed as %q; expected %q", str, expected) |
| } |
| } |
| |
| // Should be OK to register the same type multiple times, as long as they're |
| // at the same level of indirection. |
| func TestRegistration(t *testing.T) { |
| type T struct{ a int } |
| Register(new(T)) |
| Register(new(T)) |
| } |
| |
| type N1 struct{} |
| type N2 struct{} |
| |
| // See comment in type.go/Register. |
| func TestRegistrationNaming(t *testing.T) { |
| testCases := []struct { |
| t interface{} |
| name string |
| }{ |
| {&N1{}, "*gob.N1"}, |
| {N2{}, "encoding/gob.N2"}, |
| } |
| |
| for _, tc := range testCases { |
| Register(tc.t) |
| |
| tct := reflect.TypeOf(tc.t) |
| ct, _ := nameToConcreteType.Load(tc.name) |
| if ct != tct { |
| t.Errorf("nameToConcreteType[%q] = %v, want %v", tc.name, ct, tct) |
| } |
| // concreteTypeToName is keyed off the base type. |
| if tct.Kind() == reflect.Ptr { |
| tct = tct.Elem() |
| } |
| if n, _ := concreteTypeToName.Load(tct); n != tc.name { |
| t.Errorf("concreteTypeToName[%v] got %v, want %v", tct, n, tc.name) |
| } |
| } |
| } |
| |
| func TestStressParallel(t *testing.T) { |
| type T2 struct{ A int } |
| c := make(chan bool) |
| const N = 10 |
| for i := 0; i < N; i++ { |
| go func() { |
| p := new(T2) |
| Register(p) |
| b := new(bytes.Buffer) |
| enc := NewEncoder(b) |
| err := enc.Encode(p) |
| if err != nil { |
| t.Error("encoder fail:", err) |
| } |
| dec := NewDecoder(b) |
| err = dec.Decode(p) |
| if err != nil { |
| t.Error("decoder fail:", err) |
| } |
| c <- true |
| }() |
| } |
| for i := 0; i < N; i++ { |
| <-c |
| } |
| } |
| |
| // Issue 23328. Note that this test name is known to cmd/dist/test.go. |
| func TestTypeRace(t *testing.T) { |
| c := make(chan bool) |
| var wg sync.WaitGroup |
| for i := 0; i < 2; i++ { |
| wg.Add(1) |
| go func(i int) { |
| defer wg.Done() |
| var buf bytes.Buffer |
| enc := NewEncoder(&buf) |
| dec := NewDecoder(&buf) |
| var x interface{} |
| switch i { |
| case 0: |
| x = &N1{} |
| case 1: |
| x = &N2{} |
| default: |
| t.Errorf("bad i %d", i) |
| return |
| } |
| m := make(map[string]string) |
| <-c |
| if err := enc.Encode(x); err != nil { |
| t.Error(err) |
| return |
| } |
| if err := enc.Encode(x); err != nil { |
| t.Error(err) |
| return |
| } |
| if err := dec.Decode(&m); err == nil { |
| t.Error("decode unexpectedly succeeded") |
| return |
| } |
| }(i) |
| } |
| close(c) |
| wg.Wait() |
| } |