5
5
package containers_test
6
6
7
7
import (
8
+ "fmt"
9
+ "math/rand"
8
10
"testing"
9
11
10
12
"github.com/stretchr/testify/require"
11
13
12
14
"github.com/siderolabs/gen/containers"
15
+ "github.com/siderolabs/gen/xsync"
13
16
)
14
17
15
18
func TestConcurrentMap (t * testing.T ) {
19
+ t .Parallel ()
20
+
16
21
t .Run ("should return nothing if key doesnt exist" , func (t * testing.T ) {
22
+ t .Parallel ()
23
+
17
24
m := containers.ConcurrentMap [int , int ]{}
18
25
_ , ok := m .Get (0 )
19
26
require .False (t , ok )
20
27
})
21
28
22
29
t .Run ("should remove nothing if map is empty" , func (t * testing.T ) {
30
+ t .Parallel ()
31
+
23
32
m := containers.ConcurrentMap [int , int ]{}
24
33
m .Remove (0 )
25
34
})
26
35
27
36
t .Run ("should return setted value" , func (t * testing.T ) {
37
+ t .Parallel ()
38
+
28
39
m := containers.ConcurrentMap [int , int ]{}
29
40
m .Set (1 , 1 )
30
41
val , ok := m .Get (1 )
@@ -33,14 +44,34 @@ func TestConcurrentMap(t *testing.T) {
33
44
})
34
45
35
46
t .Run ("should remove value" , func (t * testing.T ) {
47
+ t .Parallel ()
48
+
36
49
m := containers.ConcurrentMap [int , int ]{}
37
50
m .Set (1 , 1 )
38
51
m .Remove (1 )
39
52
_ , ok := m .Get (1 )
40
53
require .False (t , ok )
54
+
55
+ m .Set (2 , 2 )
56
+ got , ok := m .RemoveAndGet (2 )
57
+ require .True (t , ok )
58
+ require .Equal (t , 2 , got )
59
+
60
+ got , ok = m .RemoveAndGet (2 )
61
+ require .False (t , ok )
62
+ require .Zero (t , got )
63
+
64
+ m .Reset ()
65
+ got , ok = m .RemoveAndGet (2 )
66
+ require .False (t , ok )
67
+ require .Zero (t , got )
68
+
69
+ require .False (t , ok )
41
70
})
42
71
43
72
t .Run ("should call fn for every key" , func (t * testing.T ) {
73
+ t .Parallel ()
74
+
44
75
m := containers.ConcurrentMap [int , int ]{}
45
76
m .Set (1 , 1 )
46
77
m .Set (2 , 2 )
@@ -52,4 +83,139 @@ func TestConcurrentMap(t *testing.T) {
52
83
})
53
84
require .Equal (t , 3 , count )
54
85
})
86
+
87
+ t .Run ("should clear the map" , func (t * testing.T ) {
88
+ t .Parallel ()
89
+
90
+ m := containers.ConcurrentMap [int , int ]{}
91
+ m .Set (1 , 1 )
92
+
93
+ require .Equal (t , 1 , m .Len ())
94
+
95
+ m .Clear ()
96
+
97
+ require .Equal (t , 0 , m .Len ())
98
+ })
99
+
100
+ t .Run ("should trunc the map" , func (t * testing.T ) {
101
+ t .Parallel ()
102
+
103
+ m := containers.ConcurrentMap [int , int ]{}
104
+ m .Set (1 , 1 )
105
+
106
+ require .Equal (t , 1 , m .Len ())
107
+
108
+ m .Reset ()
109
+
110
+ require .Equal (t , 0 , m .Len ())
111
+ })
112
+ }
113
+
114
+ func TestConcurrentMap_GetOrCall (t * testing.T ) {
115
+ var m containers.ConcurrentMap [int , int ]
116
+
117
+ t .Run ("group" , func (t * testing.T ) {
118
+ t .Run ("try to insert value" , func (t * testing.T ) {
119
+ parallelGetOrCall (t , & m , 100 , 1000 )
120
+ })
121
+
122
+ t .Run ("try to insert value #2" , func (t * testing.T ) {
123
+ parallelGetOrCall (t , & m , 1000 , 100 )
124
+ })
125
+ })
126
+ }
127
+
128
+ func parallelGetOrCall (t * testing.T , m * containers.ConcurrentMap [int , int ], our , another int ) {
129
+ t .Parallel ()
130
+
131
+ oneAnotherGet := false
132
+
133
+ for i := 0 ; i < 10000 ; i ++ {
134
+ key := int (rand .Int63n (10000 ))
135
+
136
+ res , ok := m .GetOrCall (key , func () int { return key * our })
137
+ if ok {
138
+ switch res {
139
+ case key * our :
140
+ case key * another :
141
+ oneAnotherGet = true
142
+ default :
143
+ t .Fatalf ("unexpected value %d" , res )
144
+ }
145
+ }
146
+ }
147
+
148
+ require .True (t , oneAnotherGet )
149
+ }
150
+
151
+ func TestConcurrentMap_GetOrCreate (t * testing.T ) {
152
+ var m containers.ConcurrentMap [int , int ]
153
+
154
+ t .Run ("group" , func (t * testing.T ) {
155
+ t .Run ("try to insert value" , func (t * testing.T ) {
156
+ parallelGetOrCreate (t , & m , 100 , 1000 )
157
+ })
158
+
159
+ t .Run ("try to insert value #2" , func (t * testing.T ) {
160
+ parallelGetOrCreate (t , & m , 1000 , 100 )
161
+ })
162
+ })
163
+ }
164
+
165
+ func parallelGetOrCreate (t * testing.T , m * containers.ConcurrentMap [int , int ], our , another int ) {
166
+ t .Parallel ()
167
+
168
+ oneAnotherGet := false
169
+
170
+ for i := 0 ; i < 10000 ; i ++ {
171
+ key := int (rand .Int63n (10000 ))
172
+
173
+ res , ok := m .GetOrCreate (key , key * our )
174
+ if ok {
175
+ switch res {
176
+ case key * our :
177
+ case key * another :
178
+ oneAnotherGet = true
179
+ default :
180
+ t .Fatalf ("unexpected value %d" , res )
181
+ }
182
+ }
183
+ }
184
+
185
+ require .True (t , oneAnotherGet )
186
+ }
187
+
188
+ func Example_benchConcurrentMap () {
189
+ var sink int
190
+
191
+ benchResult := testing .Benchmark (func (b * testing.B ) {
192
+ b .ReportAllocs ()
193
+
194
+ var m containers.ConcurrentMap [int , * xsync.Once [int ]]
195
+
196
+ for i := 0 ; i < b .N ; i ++ {
197
+ variable := 0
198
+
199
+ res , _ := m .GetOrCall (10 , func () * xsync.Once [int ] {
200
+ return & xsync.Once [int ]{}
201
+ })
202
+
203
+ sink = res .Do (func () int {
204
+ variable ++
205
+
206
+ return variable
207
+ })
208
+ }
209
+ })
210
+
211
+ if benchResult .AllocsPerOp () > 0 {
212
+ fmt .Println ("this benchmark should not allocate memory" )
213
+ }
214
+
215
+ fmt .Println ("ok" )
216
+
217
+ // Output:
218
+ // ok
219
+
220
+ _ = sink
55
221
}
0 commit comments