blob: d1e409ef95718da4b04cf3041e6f32ff3cc98e5d [file] [log] [blame]
Dan Willemsenbc60c3c2021-12-15 01:09:00 -08001// Copyright 2020 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 intern
6
7import (
8 "fmt"
9 "runtime"
10 "testing"
11)
12
13func TestBasics(t *testing.T) {
14 clearMap()
15 foo := Get("foo")
16 bar := Get("bar")
17 empty := Get("")
18 nilEface := Get(nil)
19 i := Get(0x7777777)
20 foo2 := Get("foo")
21 bar2 := Get("bar")
22 empty2 := Get("")
23 nilEface2 := Get(nil)
24 i2 := Get(0x7777777)
25 foo3 := GetByString("foo")
26 empty3 := GetByString("")
27
28 if foo.Get() != foo2.Get() {
29 t.Error("foo/foo2 values differ")
30 }
31 if foo.Get() != foo3.Get() {
32 t.Error("foo/foo3 values differ")
33 }
34 if foo.Get() != "foo" {
35 t.Error("foo.Get not foo")
36 }
37 if foo != foo2 {
38 t.Error("foo/foo2 pointers differ")
39 }
40 if foo != foo3 {
41 t.Error("foo/foo3 pointers differ")
42 }
43
44 if bar.Get() != bar2.Get() {
45 t.Error("bar values differ")
46 }
47 if bar.Get() != "bar" {
48 t.Error("bar.Get not bar")
49 }
50 if bar != bar2 {
51 t.Error("bar pointers differ")
52 }
53
54 if i.Get() != i.Get() {
55 t.Error("i values differ")
56 }
57 if i.Get() != 0x7777777 {
58 t.Error("i.Get not 0x7777777")
59 }
60 if i != i2 {
61 t.Error("i pointers differ")
62 }
63
64 if empty.Get() != empty2.Get() {
65 t.Error("empty/empty2 values differ")
66 }
67 if empty.Get() != empty.Get() {
68 t.Error("empty/empty3 values differ")
69 }
70 if empty.Get() != "" {
71 t.Error("empty.Get not empty string")
72 }
73 if empty != empty2 {
74 t.Error("empty/empty2 pointers differ")
75 }
76 if empty != empty3 {
77 t.Error("empty/empty3 pointers differ")
78 }
79
80 if nilEface.Get() != nilEface2.Get() {
81 t.Error("nilEface values differ")
82 }
83 if nilEface.Get() != nil {
84 t.Error("nilEface.Get not nil")
85 }
86 if nilEface != nilEface2 {
87 t.Error("nilEface pointers differ")
88 }
89
90 if n := mapLen(); n != 5 {
91 t.Errorf("map len = %d; want 4", n)
92 }
93
94 wantEmpty(t)
95}
96
97func wantEmpty(t testing.TB) {
98 t.Helper()
99 const gcTries = 5000
100 for try := 0; try < gcTries; try++ {
101 runtime.GC()
102 n := mapLen()
103 if n == 0 {
104 break
105 }
106 if try == gcTries-1 {
107 t.Errorf("map len = %d after (%d GC tries); want 0, contents: %v", n, gcTries, mapKeys())
108 }
109 }
110}
111
112func TestStress(t *testing.T) {
113 iters := 10000
114 if testing.Short() {
115 iters = 1000
116 }
117 var sink []byte
118 for i := 0; i < iters; i++ {
119 _ = Get("foo")
120 sink = make([]byte, 1<<20)
121 }
122 _ = sink
123}
124
125func BenchmarkStress(b *testing.B) {
126 done := make(chan struct{})
127 defer close(done)
128 go func() {
129 for {
130 select {
131 case <-done:
132 return
133 default:
134 }
135 runtime.GC()
136 }
137 }()
138
139 clearMap()
140 v1 := Get("foo")
141 b.ReportAllocs()
142 b.RunParallel(func(pb *testing.PB) {
143 for pb.Next() {
144 v2 := Get("foo")
145 if v1 != v2 {
146 b.Fatal("wrong value")
147 }
148 // And also a key we don't retain:
149 _ = Get("bar")
150 }
151 })
152 runtime.GC()
153 wantEmpty(b)
154}
155
156func mapLen() int {
157 mu.Lock()
158 defer mu.Unlock()
159 return len(valMap)
160}
161
162func mapKeys() (keys []string) {
163 mu.Lock()
164 defer mu.Unlock()
165 for k := range valMap {
166 keys = append(keys, fmt.Sprint(k))
167 }
168 return keys
169}
170
171func clearMap() {
172 mu.Lock()
173 defer mu.Unlock()
174 for k := range valMap {
175 delete(valMap, k)
176 }
177}
178
179var (
180 globalString = "not a constant"
181 sink string
182)
183
184func TestGetByStringAllocs(t *testing.T) {
185 allocs := int(testing.AllocsPerRun(100, func() {
186 GetByString(globalString)
187 }))
188 if allocs != 0 {
189 t.Errorf("GetString allocated %d objects, want 0", allocs)
190 }
191}
192
193func BenchmarkGetByString(b *testing.B) {
194 b.ReportAllocs()
195 for i := 0; i < b.N; i++ {
196 v := GetByString(globalString)
197 sink = v.Get().(string)
198 }
199}