Source file
src/unique/handle_test.go
1
2
3
4
5 package unique
6
7 import (
8 "fmt"
9 "internal/abi"
10 "reflect"
11 "runtime"
12 "strconv"
13 "strings"
14 "testing"
15 "time"
16 "unsafe"
17 )
18
19
20
21 type testString string
22 type testIntArray [4]int
23 type testEface any
24 type testStringArray [3]string
25 type testStringStruct struct {
26 a string
27 }
28 type testStringStructArrayStruct struct {
29 s [2]testStringStruct
30 }
31 type testStruct struct {
32 z float64
33 b string
34 }
35 type testZeroSize struct{}
36 type testNestedHandle struct {
37 next Handle[testNestedHandle]
38 arr [6]int
39 }
40
41 func TestHandle(t *testing.T) {
42 testHandle(t, testString("foo"))
43 testHandle(t, testString("bar"))
44 testHandle(t, testString(""))
45 testHandle(t, testIntArray{7, 77, 777, 7777})
46 testHandle(t, testEface(nil))
47 testHandle(t, testStringArray{"a", "b", "c"})
48 testHandle(t, testStringStruct{"x"})
49 testHandle(t, testStringStructArrayStruct{
50 s: [2]testStringStruct{{"y"}, {"z"}},
51 })
52 testHandle(t, testStruct{0.5, "184"})
53 testHandle(t, testEface("hello"))
54 testHandle(t, testZeroSize(struct{}{}))
55 }
56
57 func testHandle[T comparable](t *testing.T, value T) {
58 name := reflect.TypeFor[T]().Name()
59 t.Run(fmt.Sprintf("%s/%#v", name, value), func(t *testing.T) {
60 v0 := Make(value)
61 v1 := Make(value)
62
63 if v0.Value() != v1.Value() {
64 t.Error("v0.Value != v1.Value")
65 }
66 if v0.Value() != value {
67 t.Errorf("v0.Value not %#v", value)
68 }
69 if v0 != v1 {
70 t.Error("v0 != v1")
71 }
72
73 drainMaps[T](t)
74 checkMapsFor(t, value)
75 })
76 }
77
78
79 func drainMaps[T comparable](t *testing.T) {
80 t.Helper()
81
82 if unsafe.Sizeof(*(new(T))) == 0 {
83 return
84 }
85 drainCleanupQueue(t)
86 }
87
88 func drainCleanupQueue(t *testing.T) {
89 t.Helper()
90
91 runtime.GC()
92 runtime_blockUntilEmptyCleanupQueue(int64(5 * time.Second))
93 }
94
95 func checkMapsFor[T comparable](t *testing.T, value T) {
96
97 typ := abi.TypeFor[T]()
98 a, ok := uniqueMaps.Load(typ)
99 if !ok {
100 return
101 }
102 m := a.(*uniqueMap[T])
103 p := m.Load(value)
104 if p != nil {
105 t.Errorf("value %v still referenced by a handle (or tiny block?): internal pointer %p", value, p)
106 }
107 }
108
109 func TestMakeClonesStrings(t *testing.T) {
110 s := strings.Clone("abcdefghijklmnopqrstuvwxyz")
111 ran := make(chan bool)
112 runtime.AddCleanup(unsafe.StringData(s), func(ch chan bool) {
113 ch <- true
114 }, ran)
115 h := Make(s)
116
117
118 runtime.GC()
119
120 select {
121 case <-time.After(1 * time.Second):
122 t.Fatal("string was improperly retained")
123 case <-ran:
124 }
125 runtime.KeepAlive(h)
126 }
127
128 func TestHandleUnsafeString(t *testing.T) {
129 var testData []string
130 for i := range 1024 {
131 testData = append(testData, strconv.Itoa(i))
132 }
133 var buf []byte
134 var handles []Handle[string]
135 for _, s := range testData {
136 if len(buf) < len(s) {
137 buf = make([]byte, len(s)*2)
138 }
139 copy(buf, s)
140 sbuf := unsafe.String(&buf[0], len(s))
141 handles = append(handles, Make(sbuf))
142 }
143 for i, s := range testData {
144 h := Make(s)
145 if handles[i].Value() != h.Value() {
146 t.Fatal("unsafe string improperly retained internally")
147 }
148 }
149 }
150
151 func nestHandle(n testNestedHandle) testNestedHandle {
152 return testNestedHandle{
153 next: Make(n),
154 arr: n.arr,
155 }
156 }
157
158 func TestNestedHandle(t *testing.T) {
159 n0 := testNestedHandle{arr: [6]int{1, 2, 3, 4, 5, 6}}
160 n1 := nestHandle(n0)
161 n2 := nestHandle(n1)
162 n3 := nestHandle(n2)
163
164 if v := n3.next.Value(); v != n2 {
165 t.Errorf("n3.Value != n2: %#v vs. %#v", v, n2)
166 }
167 if v := n2.next.Value(); v != n1 {
168 t.Errorf("n2.Value != n1: %#v vs. %#v", v, n1)
169 }
170 if v := n1.next.Value(); v != n0 {
171 t.Errorf("n1.Value != n0: %#v vs. %#v", v, n0)
172 }
173
174
175
176 drainMaps[testNestedHandle](t)
177 checkMapsFor(t, n0)
178 }
179
180
181
182
183
184
185 func runtime_blockUntilEmptyCleanupQueue(timeout int64) bool
186
View as plain text