github.com/searKing/golang/go@v1.2.117/exp/maps/nested_test.go (about) 1 // Copyright 2023 The searKing Author. 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 5 package maps_test 6 7 import ( 8 "fmt" 9 "math/rand" 10 "reflect" 11 "runtime" 12 "sync/atomic" 13 "testing" 14 15 maps_ "github.com/searKing/golang/go/exp/maps" 16 ) 17 18 type mapOp string 19 20 const ( 21 opLoad = mapOp("Load") 22 opStore = mapOp("Store") 23 opLoadOrStore = mapOp("LoadOrStore") 24 opLoadAndDelete = mapOp("LoadAndDelete") 25 opDelete = mapOp("Delete") 26 opSwap = mapOp("Swap") 27 opCompareAndSwap = mapOp("CompareAndSwap") 28 opCompareAndDelete = mapOp("CompareAndDelete") 29 ) 30 31 var mapOps = [...]mapOp{ 32 opLoad, 33 opStore, 34 opLoadOrStore, 35 opLoadAndDelete, 36 opDelete, 37 opSwap, 38 opCompareAndSwap, 39 opCompareAndDelete, 40 } 41 42 // mapCall is a quick.Generator for calls on mapInterface. 43 type mapCall[K ~string] struct { 44 op mapOp 45 k []K 46 v any 47 } 48 49 func (c mapCall[K]) apply(m mapInterface[K]) (any, bool) { 50 switch c.op { 51 case opLoad: 52 return m.Load(c.k) 53 case opStore: 54 m.Store(c.k, c.v) 55 return nil, false 56 case opLoadOrStore: 57 return m.LoadOrStore(c.k, c.v) 58 case opLoadAndDelete: 59 return m.LoadAndDelete(c.k) 60 case opDelete: 61 m.Delete(c.k) 62 return nil, false 63 case opSwap: 64 return m.Swap(c.k, c.v) 65 case opCompareAndSwap: 66 if m.CompareAndSwap(c.k, c.v, rand.Int()) { 67 m.Delete(c.k) 68 return c.v, true 69 } 70 return nil, false 71 case opCompareAndDelete: 72 if m.CompareAndDelete(c.k, c.v) { 73 if _, ok := m.Load(c.k); !ok { 74 return nil, true 75 } 76 } 77 return nil, false 78 default: 79 panic("invalid mapOp") 80 } 81 } 82 83 type mapResult struct { 84 value any 85 ok bool 86 } 87 88 func (mr *mapResult) Reset() { 89 var z mapResult 90 *mr = z 91 } 92 93 func randKey[K ~string](r *rand.Rand) []K { 94 b := make([]K, r.Intn(4)) 95 for i := range b { 96 b[i] = K('a' + byte(rand.Intn(26))) 97 } 98 return b 99 } 100 101 func randValue(r *rand.Rand) any { 102 b := make([]byte, r.Intn(4)) 103 for i := range b { 104 b[i] = 'a' + byte(rand.Intn(26)) 105 } 106 return string(b) 107 } 108 109 func (mapCall[K]) Generate(r *rand.Rand, size int) reflect.Value { 110 c := mapCall[K]{op: mapOps[rand.Intn(len(mapOps))], k: randKey[K](r)} 111 switch c.op { 112 case opStore, opLoadOrStore: 113 c.v = randValue(r) 114 } 115 return reflect.ValueOf(c) 116 } 117 118 func applyCalls[K ~string](m mapInterface[K], calls [2]mapCall[K]) (results []mapResult, final map[any]any) { 119 for _, c := range calls { 120 v, ok := c.apply(m) 121 results = append(results, mapResult{v, ok}) 122 } 123 124 final = make(map[any]any) 125 m.Range(func(k []K, v any) bool { 126 final[fmt.Sprintf("%v", k)] = v 127 return true 128 }) 129 130 return results, final 131 } 132 133 func applyMap[K ~string](calls [2]mapCall[K]) ([]mapResult, map[any]any) { 134 return applyCalls[K](make(maps_.NestedMap[K]), calls) 135 } 136 137 func TestIssue40999(t *testing.T) { 138 var m = maps_.NestedMap[*int]{} 139 140 // Since the miss-counting in missLocked (via Delete) 141 // compares the miss count with len(m.dirty), 142 // add an initial entry to bias len(m.dirty) above the miss count. 143 m.Store(nil, struct{}{}) 144 145 var finalized uint32 146 147 // Set finalizers that count for collected keys. A non-zero count 148 // indicates that keys have not been leaked. 149 for atomic.LoadUint32(&finalized) == 0 { 150 p := new(int) 151 runtime.SetFinalizer(p, func(*int) { 152 atomic.AddUint32(&finalized, 1) 153 }) 154 m.Store([]*int{p}, struct{}{}) 155 m.Delete([]*int{p}) 156 runtime.GC() 157 } 158 } 159 160 func TestNestedMapRangeCall(t *testing.T) { // Issue 46399 161 var m = maps_.NestedMap[int]{} 162 for i, v := range [3]string{"hello", "world", "Go"} { 163 m.Store([]int{i, i, i}, v) 164 } 165 166 var dummyKey = []int{42, 42, 42, 42} 167 m.Range(func(keys []int, value any) bool { 168 m.Range(func(keys []int, value any) bool { 169 // We should be able to load the key offered in the Range callback, 170 // because there are no concurrent Delete involved in this tested map. 171 if v, ok := m.Load(keys); !ok || !reflect.DeepEqual(v, value) { 172 t.Fatalf("Nested Range loads unexpected value, got %+v want %+v", v, value) 173 } 174 175 // We didn't keep dummyKey and a value into the map before, if somehow we loaded 176 // a value from such a key, meaning there must be an internal bug regarding 177 // nested range in the Map. 178 if _, loaded := m.LoadOrStore(dummyKey, "dummy"); loaded { 179 t.Fatalf("Nested Range loads unexpected value, want store a new value") 180 } 181 182 // Try to Store then LoadAndDelete the corresponding value with the key 183 // 42 to the Map. In this case, the key 42 and associated value should be 184 // removed from the Map. Therefore any future range won't observe key 42 185 // as we checked in above. 186 val := "maps_.NestedMap[int]" 187 m.Store(dummyKey, val) 188 if v, loaded := m.LoadAndDelete(dummyKey); !loaded || !reflect.DeepEqual(v, val) { 189 t.Fatalf("Nested Range loads unexpected value, got %v, want %v", v, val) 190 } 191 return true 192 }) 193 194 // Remove key from Map on-the-fly. 195 m.Delete(keys) 196 return true 197 }) 198 199 // After a Range of Delete, all keys should be removed and any 200 // further Range won't invoke the callback. Hence length remains 0. 201 length := 0 202 m.Range(func(keys []int, value any) bool { 203 length++ 204 return true 205 }) 206 207 if length != 0 { 208 t.Fatalf("Unexpected maps_.NestedMap[int] size, got %v want %v", length, 0) 209 } 210 } 211 212 func TestCompareAndSwap_NonExistingKey(t *testing.T) { 213 m := &maps_.NestedMap[int]{} 214 if m.CompareAndSwap([]int{404}, nil, 42) { 215 t.Fatalf("CompareAndSwap on an non-existing key succeeded") 216 } 217 } 218 219 var nestedMapTests = []struct { 220 calls []mapCall[string] 221 want []mapResult 222 }{ 223 { 224 calls: []mapCall[string]{ 225 { 226 op: opStore, 227 k: nil, 228 v: nil, 229 }, 230 { 231 op: opLoad, 232 k: nil, 233 v: nil, 234 }, 235 { 236 op: opStore, 237 k: []string{"name"}, 238 v: "Alice", 239 }, 240 { 241 op: opLoad, 242 k: []string{"name"}, 243 }, 244 { 245 op: opLoad, 246 k: []string{"sex"}, 247 }, 248 { 249 op: opLoadOrStore, 250 k: []string{"sex"}, 251 v: "Male", 252 }, 253 { 254 op: opLoadAndDelete, 255 k: []string{"sex_to_delete"}, 256 }, 257 { 258 op: opStore, 259 k: []string{"sex_to_delete"}, 260 v: "Female", 261 }, 262 { 263 op: opLoadAndDelete, 264 k: []string{"sex_to_delete"}, 265 }, 266 { 267 op: opLoadOrStore, 268 k: []string{"sex_to_delete"}, 269 v: "Middle", 270 }, 271 { 272 op: opCompareAndSwap, 273 k: []string{"country", "province"}, 274 v: "Nanjing", 275 }, 276 { 277 op: opLoadOrStore, 278 k: []string{"country", "province"}, 279 v: "Shanghai", 280 }, 281 { 282 op: opLoad, 283 k: []string{"country", "province"}, 284 }, 285 { 286 op: opCompareAndDelete, 287 k: []string{"country", "province"}, 288 v: "Nanjing", 289 }, 290 { 291 op: opLoad, 292 k: []string{"country", "province"}, 293 }, 294 { 295 op: opCompareAndDelete, 296 k: []string{"country", "province"}, 297 v: "Shanghai", 298 }, 299 { 300 op: opLoad, 301 k: []string{"country", "province"}, 302 }, 303 { 304 op: opDelete, 305 k: []string{"country", "province"}, 306 }, 307 { 308 op: opLoad, 309 k: []string{"country", "province"}, 310 }, 311 }, 312 want: []mapResult{ 313 { 314 value: nil, 315 ok: false, 316 }, 317 { 318 value: nil, 319 ok: false, 320 }, 321 { 322 value: nil, 323 ok: false, 324 }, 325 { 326 value: "Alice", 327 ok: true, 328 }, 329 { 330 value: nil, 331 ok: false, 332 }, 333 { 334 value: "Male", 335 ok: false, 336 }, 337 { 338 value: nil, 339 ok: false, 340 }, 341 { 342 value: nil, 343 ok: false, 344 }, 345 { 346 value: "Female", 347 ok: true, 348 }, 349 { 350 value: "Middle", 351 ok: false, 352 }, 353 { 354 value: nil, 355 ok: false, 356 }, 357 { 358 value: "Shanghai", 359 ok: false, 360 }, 361 { 362 value: "Shanghai", 363 ok: true, 364 }, 365 { 366 ok: false, 367 }, 368 { 369 value: "Shanghai", 370 ok: true, 371 }, 372 { 373 ok: true, 374 }, 375 { 376 value: nil, 377 ok: false, 378 }, 379 { 380 value: nil, 381 ok: false, 382 }, 383 { 384 value: nil, 385 ok: false, 386 }, 387 }, 388 }, 389 { 390 calls: []mapCall[string]{ 391 { 392 op: opStore, 393 k: []string{"name"}, 394 v: "Alice", 395 }, 396 { 397 op: opLoad, 398 k: []string{"name"}, 399 }, 400 { 401 op: opStore, 402 k: []string{"name", "sex"}, 403 v: "Female", 404 }, 405 { 406 op: opLoad, 407 k: []string{"name"}, 408 }, 409 { 410 op: opLoad, 411 k: []string{"name", "sex"}, 412 }, 413 { 414 op: opDelete, 415 k: []string{"name"}, 416 }, 417 { 418 op: opLoad, 419 k: []string{"name"}, 420 }, 421 { 422 op: opLoad, 423 k: []string{"name", "sex"}, 424 }, 425 }, 426 want: []mapResult{ 427 {}, 428 { 429 value: "Alice", 430 ok: true, 431 }, 432 { 433 value: nil, 434 ok: false, 435 }, 436 { 437 value: maps_.NestedMap[string]{"sex": "Female"}, 438 ok: true, 439 }, 440 { 441 value: "Female", 442 ok: true, 443 }, 444 {}, 445 { 446 value: nil, 447 ok: false, 448 }, 449 { 450 value: nil, 451 ok: false, 452 }, 453 }, 454 }, 455 } 456 457 func TestNestedMap(t *testing.T) { 458 for i, test := range nestedMapTests { 459 m := &maps_.NestedMap[string]{} 460 var mr mapResult 461 for j, c := range test.calls { 462 mr.Reset() 463 mr.value, mr.ok = c.apply(m) 464 if !reflect.DeepEqual(mr, test.want[j]) { 465 t.Errorf("#%d-%d: %s(%v, %v) = %v, want %v", i, j, c.op, c.k, c.v, mr, test.want[j]) 466 break 467 } 468 } 469 } 470 }