// Copyright 2022 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package persistent import ( "fmt" "math/rand" "reflect" "sync/atomic" "testing" ) type mapEntry struct { key int value int } type validatedMap struct { impl *Map expected map[int]int deleted map[mapEntry]struct{} seen map[mapEntry]struct{} } func TestSimpleMap(t *testing.T) { deletedEntries := make(map[mapEntry]struct{}) seenEntries := make(map[mapEntry]struct{}) m1 := &validatedMap{ impl: NewMap(func(a, b interface{}) bool { return a.(int) < b.(int) }), expected: make(map[int]int), deleted: deletedEntries, seen: seenEntries, } m3 := m1.clone() validateRef(t, m1, m3) m3.set(t, 8, 8) validateRef(t, m1, m3) m3.destroy() assertSameMap(t, deletedEntries, map[mapEntry]struct{}{ {key: 8, value: 8}: {}, }) validateRef(t, m1) m1.set(t, 1, 1) validateRef(t, m1) m1.set(t, 2, 2) validateRef(t, m1) m1.set(t, 3, 3) validateRef(t, m1) m1.remove(t, 2) validateRef(t, m1) m1.set(t, 6, 6) validateRef(t, m1) assertSameMap(t, deletedEntries, map[mapEntry]struct{}{ {key: 2, value: 2}: {}, {key: 8, value: 8}: {}, }) m2 := m1.clone() validateRef(t, m1, m2) m1.set(t, 6, 60) validateRef(t, m1, m2) m1.remove(t, 1) validateRef(t, m1, m2) for i := 10; i < 14; i++ { m1.set(t, i, i) validateRef(t, m1, m2) } m1.set(t, 10, 100) validateRef(t, m1, m2) m1.remove(t, 12) validateRef(t, m1, m2) m2.set(t, 4, 4) validateRef(t, m1, m2) m2.set(t, 5, 5) validateRef(t, m1, m2) m1.destroy() assertSameMap(t, deletedEntries, map[mapEntry]struct{}{ {key: 2, value: 2}: {}, {key: 6, value: 60}: {}, {key: 8, value: 8}: {}, {key: 10, value: 10}: {}, {key: 10, value: 100}: {}, {key: 11, value: 11}: {}, {key: 12, value: 12}: {}, {key: 13, value: 13}: {}, }) m2.set(t, 7, 7) validateRef(t, m2) m2.destroy() assertSameMap(t, seenEntries, deletedEntries) } func TestRandomMap(t *testing.T) { deletedEntries := make(map[mapEntry]struct{}) seenEntries := make(map[mapEntry]struct{}) m := &validatedMap{ impl: NewMap(func(a, b interface{}) bool { return a.(int) < b.(int) }), expected: make(map[int]int), deleted: deletedEntries, seen: seenEntries, } keys := make([]int, 0, 1000) for i := 0; i < 1000; i++ { key := rand.Int() m.set(t, key, key) keys = append(keys, key) if i%10 == 1 { index := rand.Intn(len(keys)) last := len(keys) - 1 key = keys[index] keys[index], keys[last] = keys[last], keys[index] keys = keys[:last] m.remove(t, key) } } m.destroy() assertSameMap(t, seenEntries, deletedEntries) } func (vm *validatedMap) onDelete(t *testing.T, key, value int) { entry := mapEntry{key: key, value: value} if _, ok := vm.deleted[entry]; ok { t.Fatalf("tried to delete entry twice, key: %d, value: %d", key, value) } vm.deleted[entry] = struct{}{} } func validateRef(t *testing.T, maps ...*validatedMap) { t.Helper() actualCountByEntry := make(map[mapEntry]int32) nodesByEntry := make(map[mapEntry]map[*mapNode]struct{}) expectedCountByEntry := make(map[mapEntry]int32) for i, m := range maps { dfsRef(m.impl.root, actualCountByEntry, nodesByEntry) dumpMap(t, fmt.Sprintf("%d:", i), m.impl.root) } for entry, nodes := range nodesByEntry { expectedCountByEntry[entry] = int32(len(nodes)) } assertSameMap(t, expectedCountByEntry, actualCountByEntry) } func dfsRef(node *mapNode, countByEntry map[mapEntry]int32, nodesByEntry map[mapEntry]map[*mapNode]struct{}) { if node == nil { return } entry := mapEntry{key: node.key.(int), value: node.value.value.(int)} countByEntry[entry] = atomic.LoadInt32(&node.value.refCount) nodes, ok := nodesByEntry[entry] if !ok { nodes = make(map[*mapNode]struct{}) nodesByEntry[entry] = nodes } nodes[node] = struct{}{} dfsRef(node.left, countByEntry, nodesByEntry) dfsRef(node.right, countByEntry, nodesByEntry) } func dumpMap(t *testing.T, prefix string, n *mapNode) { if n == nil { t.Logf("%s nil", prefix) return } t.Logf("%s {key: %v, value: %v (ref: %v), ref: %v, weight: %v}", prefix, n.key, n.value.value, n.value.refCount, n.refCount, n.weight) dumpMap(t, prefix+"l", n.left) dumpMap(t, prefix+"r", n.right) } func (vm *validatedMap) validate(t *testing.T) { t.Helper() validateNode(t, vm.impl.root, vm.impl.less) for key, value := range vm.expected { entry := mapEntry{key: key, value: value} if _, ok := vm.deleted[entry]; ok { t.Fatalf("entry is deleted prematurely, key: %d, value: %d", key, value) } } actualMap := make(map[int]int, len(vm.expected)) vm.impl.Range(func(key, value interface{}) { if other, ok := actualMap[key.(int)]; ok { t.Fatalf("key is present twice, key: %d, first value: %d, second value: %d", key, value, other) } actualMap[key.(int)] = value.(int) }) assertSameMap(t, actualMap, vm.expected) } func validateNode(t *testing.T, node *mapNode, less func(a, b interface{}) bool) { if node == nil { return } if node.left != nil { if less(node.key, node.left.key) { t.Fatalf("left child has larger key: %v vs %v", node.left.key, node.key) } if node.left.weight > node.weight { t.Fatalf("left child has larger weight: %v vs %v", node.left.weight, node.weight) } } if node.right != nil { if less(node.right.key, node.key) { t.Fatalf("right child has smaller key: %v vs %v", node.right.key, node.key) } if node.right.weight > node.weight { t.Fatalf("right child has larger weight: %v vs %v", node.right.weight, node.weight) } } validateNode(t, node.left, less) validateNode(t, node.right, less) } func (vm *validatedMap) set(t *testing.T, key, value int) { vm.seen[mapEntry{key: key, value: value}] = struct{}{} vm.impl.Set(key, value, func(deletedKey, deletedValue interface{}) { if deletedKey != key || deletedValue != value { t.Fatalf("unexpected passed in deleted entry: %v/%v, expected: %v/%v", deletedKey, deletedValue, key, value) } vm.onDelete(t, key, value) }) vm.expected[key] = value vm.validate(t) gotValue, ok := vm.impl.Get(key) if !ok || gotValue != value { t.Fatalf("unexpected get result after insertion, key: %v, expected: %v, got: %v (%v)", key, value, gotValue, ok) } } func (vm *validatedMap) remove(t *testing.T, key int) { vm.impl.Delete(key) delete(vm.expected, key) vm.validate(t) gotValue, ok := vm.impl.Get(key) if ok { t.Fatalf("unexpected get result after removal, key: %v, got: %v", key, gotValue) } } func (vm *validatedMap) clone() *validatedMap { expected := make(map[int]int, len(vm.expected)) for key, value := range vm.expected { expected[key] = value } return &validatedMap{ impl: vm.impl.Clone(), expected: expected, deleted: vm.deleted, seen: vm.seen, } } func (vm *validatedMap) destroy() { vm.impl.Destroy() } func assertSameMap(t *testing.T, map1, map2 interface{}) { t.Helper() if !reflect.DeepEqual(map1, map2) { t.Fatalf("different maps:\n%v\nvs\n%v", map1, map2) } } func isSameMap(map1, map2 reflect.Value) bool { if map1.Len() != map2.Len() { return false } iter := map1.MapRange() for iter.Next() { key := iter.Key() value1 := iter.Value() value2 := map2.MapIndex(key) if value2.IsZero() || !reflect.DeepEqual(value1.Interface(), value2.Interface()) { return false } } return true }