1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package gob
6
7import (
8	"bytes"
9	"reflect"
10	"sync"
11	"testing"
12)
13
14type typeT struct {
15	id  typeId
16	str string
17}
18
19var basicTypes = []typeT{
20	{tBool, "bool"},
21	{tInt, "int"},
22	{tUint, "uint"},
23	{tFloat, "float"},
24	{tBytes, "bytes"},
25	{tString, "string"},
26}
27
28func getTypeUnlocked(name string, rt reflect.Type) gobType {
29	typeLock.Lock()
30	defer typeLock.Unlock()
31	t, err := getBaseType(name, rt)
32	if err != nil {
33		panic("getTypeUnlocked: " + err.Error())
34	}
35	return t
36}
37
38// Sanity checks
39func TestBasic(t *testing.T) {
40	for _, tt := range basicTypes {
41		if tt.id.string() != tt.str {
42			t.Errorf("checkType: expected %q got %s", tt.str, tt.id.string())
43		}
44		if tt.id == 0 {
45			t.Errorf("id for %q is zero", tt.str)
46		}
47	}
48}
49
50// Reregister some basic types to check registration is idempotent.
51func TestReregistration(t *testing.T) {
52	newtyp := getTypeUnlocked("int", reflect.TypeOf(int(0)))
53	if newtyp != tInt.gobType() {
54		t.Errorf("reregistration of %s got new type", newtyp.string())
55	}
56	newtyp = getTypeUnlocked("uint", reflect.TypeOf(uint(0)))
57	if newtyp != tUint.gobType() {
58		t.Errorf("reregistration of %s got new type", newtyp.string())
59	}
60	newtyp = getTypeUnlocked("string", reflect.TypeOf("hello"))
61	if newtyp != tString.gobType() {
62		t.Errorf("reregistration of %s got new type", newtyp.string())
63	}
64}
65
66func TestArrayType(t *testing.T) {
67	var a3 [3]int
68	a3int := getTypeUnlocked("foo", reflect.TypeOf(a3))
69	newa3int := getTypeUnlocked("bar", reflect.TypeOf(a3))
70	if a3int != newa3int {
71		t.Errorf("second registration of [3]int creates new type")
72	}
73	var a4 [4]int
74	a4int := getTypeUnlocked("goo", reflect.TypeOf(a4))
75	if a3int == a4int {
76		t.Errorf("registration of [3]int creates same type as [4]int")
77	}
78	var b3 [3]bool
79	a3bool := getTypeUnlocked("", reflect.TypeOf(b3))
80	if a3int == a3bool {
81		t.Errorf("registration of [3]bool creates same type as [3]int")
82	}
83	str := a3bool.string()
84	expected := "[3]bool"
85	if str != expected {
86		t.Errorf("array printed as %q; expected %q", str, expected)
87	}
88}
89
90func TestSliceType(t *testing.T) {
91	var s []int
92	sint := getTypeUnlocked("slice", reflect.TypeOf(s))
93	var news []int
94	newsint := getTypeUnlocked("slice1", reflect.TypeOf(news))
95	if sint != newsint {
96		t.Errorf("second registration of []int creates new type")
97	}
98	var b []bool
99	sbool := getTypeUnlocked("", reflect.TypeOf(b))
100	if sbool == sint {
101		t.Errorf("registration of []bool creates same type as []int")
102	}
103	str := sbool.string()
104	expected := "[]bool"
105	if str != expected {
106		t.Errorf("slice printed as %q; expected %q", str, expected)
107	}
108}
109
110func TestMapType(t *testing.T) {
111	var m map[string]int
112	mapStringInt := getTypeUnlocked("map", reflect.TypeOf(m))
113	var newm map[string]int
114	newMapStringInt := getTypeUnlocked("map1", reflect.TypeOf(newm))
115	if mapStringInt != newMapStringInt {
116		t.Errorf("second registration of map[string]int creates new type")
117	}
118	var b map[string]bool
119	mapStringBool := getTypeUnlocked("", reflect.TypeOf(b))
120	if mapStringBool == mapStringInt {
121		t.Errorf("registration of map[string]bool creates same type as map[string]int")
122	}
123	str := mapStringBool.string()
124	expected := "map[string]bool"
125	if str != expected {
126		t.Errorf("map printed as %q; expected %q", str, expected)
127	}
128}
129
130type Bar struct {
131	X string
132}
133
134// This structure has pointers and refers to itself, making it a good test case.
135type Foo struct {
136	A int
137	B int32 // will become int
138	C string
139	D []byte
140	E *float64    // will become float64
141	F ****float64 // will become float64
142	G *Bar
143	H *Bar // should not interpolate the definition of Bar again
144	I *Foo // will not explode
145}
146
147func TestStructType(t *testing.T) {
148	sstruct := getTypeUnlocked("Foo", reflect.TypeOf(Foo{}))
149	str := sstruct.string()
150	// If we can print it correctly, we built it correctly.
151	expected := "Foo = struct { A int; B int; C string; D bytes; E float; F float; G Bar = struct { X string; }; H Bar; I Foo; }"
152	if str != expected {
153		t.Errorf("struct printed as %q; expected %q", str, expected)
154	}
155}
156
157// Should be OK to register the same type multiple times, as long as they're
158// at the same level of indirection.
159func TestRegistration(t *testing.T) {
160	type T struct{ a int }
161	Register(new(T))
162	Register(new(T))
163}
164
165type N1 struct{}
166type N2 struct{}
167
168// See comment in type.go/Register.
169func TestRegistrationNaming(t *testing.T) {
170	testCases := []struct {
171		t    interface{}
172		name string
173	}{
174		{&N1{}, "*gob.N1"},
175		{N2{}, "encoding/gob.N2"},
176	}
177
178	for _, tc := range testCases {
179		Register(tc.t)
180
181		tct := reflect.TypeOf(tc.t)
182		ct, _ := nameToConcreteType.Load(tc.name)
183		if ct != tct {
184			t.Errorf("nameToConcreteType[%q] = %v, want %v", tc.name, ct, tct)
185		}
186		// concreteTypeToName is keyed off the base type.
187		if tct.Kind() == reflect.Ptr {
188			tct = tct.Elem()
189		}
190		if n, _ := concreteTypeToName.Load(tct); n != tc.name {
191			t.Errorf("concreteTypeToName[%v] got %v, want %v", tct, n, tc.name)
192		}
193	}
194}
195
196func TestStressParallel(t *testing.T) {
197	type T2 struct{ A int }
198	c := make(chan bool)
199	const N = 10
200	for i := 0; i < N; i++ {
201		go func() {
202			p := new(T2)
203			Register(p)
204			b := new(bytes.Buffer)
205			enc := NewEncoder(b)
206			err := enc.Encode(p)
207			if err != nil {
208				t.Error("encoder fail:", err)
209			}
210			dec := NewDecoder(b)
211			err = dec.Decode(p)
212			if err != nil {
213				t.Error("decoder fail:", err)
214			}
215			c <- true
216		}()
217	}
218	for i := 0; i < N; i++ {
219		<-c
220	}
221}
222
223// Issue 23328. Note that this test name is known to cmd/dist/test.go.
224func TestTypeRace(t *testing.T) {
225	c := make(chan bool)
226	var wg sync.WaitGroup
227	for i := 0; i < 2; i++ {
228		wg.Add(1)
229		go func(i int) {
230			defer wg.Done()
231			var buf bytes.Buffer
232			enc := NewEncoder(&buf)
233			dec := NewDecoder(&buf)
234			var x interface{}
235			switch i {
236			case 0:
237				x = &N1{}
238			case 1:
239				x = &N2{}
240			default:
241				t.Errorf("bad i %d", i)
242				return
243			}
244			m := make(map[string]string)
245			<-c
246			if err := enc.Encode(x); err != nil {
247				t.Error(err)
248				return
249			}
250			if err := enc.Encode(x); err != nil {
251				t.Error(err)
252				return
253			}
254			if err := dec.Decode(&m); err == nil {
255				t.Error("decode unexpectedly succeeded")
256				return
257			}
258		}(i)
259	}
260	close(c)
261	wg.Wait()
262}
263