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
37 func TestHandle(t *testing.T) {
38 testHandle(t, testString("foo"))
39 testHandle(t, testString("bar"))
40 testHandle(t, testString(""))
41 testHandle(t, testIntArray{7, 77, 777, 7777})
42 testHandle(t, testEface(nil))
43 testHandle(t, testStringArray{"a", "b", "c"})
44 testHandle(t, testStringStruct{"x"})
45 testHandle(t, testStringStructArrayStruct{
46 s: [2]testStringStruct{{"y"}, {"z"}},
47 })
48 testHandle(t, testStruct{0.5, "184"})
49 testHandle(t, testEface("hello"))
50 testHandle(t, testZeroSize(struct{}{}))
51 }
52
53 func testHandle[T comparable](t *testing.T, value T) {
54 name := reflect.TypeFor[T]().Name()
55 t.Run(fmt.Sprintf("%s/%#v", name, value), func(t *testing.T) {
56 t.Parallel()
57
58 v0 := Make(value)
59 v1 := Make(value)
60
61 if v0.Value() != v1.Value() {
62 t.Error("v0.Value != v1.Value")
63 }
64 if v0.Value() != value {
65 t.Errorf("v0.Value not %#v", value)
66 }
67 if v0 != v1 {
68 t.Error("v0 != v1")
69 }
70
71 drainMaps[T](t)
72 checkMapsFor(t, value)
73 })
74 }
75
76
77 func drainMaps[T comparable](t *testing.T) {
78 t.Helper()
79
80 if unsafe.Sizeof(*(new(T))) == 0 {
81 return
82 }
83
84 wait := make(chan struct{}, 1)
85
86
87
88
89
90 cleanupMu.Lock()
91 cleanupNotify = append(cleanupNotify, func() {
92 select {
93 case wait <- struct{}{}:
94 default:
95 }
96 })
97
98 runtime.GC()
99 cleanupMu.Unlock()
100
101
102 <-wait
103 }
104
105 func checkMapsFor[T comparable](t *testing.T, value T) {
106
107 typ := abi.TypeFor[T]()
108 a, ok := uniqueMaps.Load(typ)
109 if !ok {
110 return
111 }
112 m := a.(*uniqueMap[T])
113 wp, ok := m.Load(value)
114 if !ok {
115 return
116 }
117 if wp.Strong() != nil {
118 t.Errorf("value %v still referenced a handle (or tiny block?) ", value)
119 return
120 }
121 t.Errorf("failed to drain internal maps of %v", value)
122 }
123
124 func TestMakeClonesStrings(t *testing.T) {
125 s := strings.Clone("abcdefghijklmnopqrstuvwxyz")
126 ran := make(chan bool)
127 runtime.SetFinalizer(unsafe.StringData(s), func(_ *byte) {
128 ran <- true
129 })
130 h := Make(s)
131
132
133 runtime.GC()
134
135 select {
136 case <-time.After(1 * time.Second):
137 t.Fatal("string was improperly retained")
138 case <-ran:
139 }
140 runtime.KeepAlive(h)
141 }
142
143 func TestHandleUnsafeString(t *testing.T) {
144 var testData []string
145 for i := range 1024 {
146 testData = append(testData, strconv.Itoa(i))
147 }
148 var buf []byte
149 var handles []Handle[string]
150 for _, s := range testData {
151 if len(buf) < len(s) {
152 buf = make([]byte, len(s)*2)
153 }
154 copy(buf, s)
155 sbuf := unsafe.String(&buf[0], len(s))
156 handles = append(handles, Make(sbuf))
157 }
158 for i, s := range testData {
159 h := Make(s)
160 if handles[i].Value() != h.Value() {
161 t.Fatal("unsafe string improperly retained internally")
162 }
163 }
164 }
165
View as plain text