forked from puppetlabs-toy-chest/wash
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathentryMap_test.go
141 lines (118 loc) · 2.98 KB
/
entryMap_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package plugin
import (
"strconv"
"sync"
"testing"
"time"
"github.com/stretchr/testify/suite"
)
type EntryMapTestSuite struct {
suite.Suite
}
func (suite *EntryMapTestSuite) TestLoad_Delete_Len() {
m := newEntryMap()
_, ok := m.Load("foo")
suite.False(ok)
suite.Equal(0, m.Len())
expectedEntry := newCacheTestsMockEntry("foo")
m.mp["foo"] = expectedEntry
actualEntry, ok := m.Load("foo")
suite.Equal(expectedEntry, actualEntry)
suite.True(ok)
suite.Equal(1, m.Len())
m.Delete("foo")
_, ok = m.Load("foo")
suite.False(ok)
suite.Equal(0, m.Len())
}
func (suite *EntryMapTestSuite) TestRange() {
m := newEntryMap()
entries := []Entry{
newCacheTestsMockEntry("foo"),
newCacheTestsMockEntry("bar"),
}
for _, entry := range entries {
m.mp[CName(entry)] = entry
}
// Test that iteration works
entryMap := make(map[string]Entry)
m.Range(func(cname string, entry Entry) bool {
entryMap[cname] = entry
return true
})
suite.Equal(m.Map(), entryMap)
// Test that break works
count := 0
m.Range(func(cname string, entry Entry) bool {
if cname == "foo" {
return false
}
count++
return true
})
// <= 1 is to account for the fact that Go map's iteration order
// is random.
suite.True(count <= 1)
}
func (suite *EntryMapTestSuite) TestConcurrentReadWrite() {
m := newEntryMap()
var wg sync.WaitGroup
var startCh = make(chan struct{})
var doneCh = make(chan struct{})
// Load, Delete, and Range all acquire locks to avoid a concurrent
// read/write panic. Thus, this test launches goroutines that invoke
// each method concurrently. Idea is that if any one of those methods
// fail to acquire a lock (including the right lock, like a Write lock
// for Delete), then this test will panic. NumGoroutinesPerMethod is
// arbitrary, idea is it should be high enough that we can detect a panic
// but low enough that the test won't take too long.
//
// The test passes if all the goroutines successfully return.
//
// NOTE: Testing Len() is a bit tricky because a concurrent len(mp) and
// delete(mp) does not cause a read/write panic. Since its implementation is
// simple enough, we omit Len() to avoid further complicating the tests.
NumGoroutinesPerMethod := 40
for i := 0; i < NumGoroutinesPerMethod; i++ {
key := strconv.Itoa(i)
m.mp[key] = nil
// Load
wg.Add(1)
go func(i int) {
defer wg.Done()
<-startCh
m.Load(key)
}(i)
// Delete
wg.Add(1)
go func(i int) {
defer wg.Done()
<-startCh
m.Delete(key)
}(i)
// Range
wg.Add(1)
go func(i int) {
defer wg.Done()
<-startCh
m.Range(func(_ string, _ Entry) bool {
return false
})
}(i)
}
time.AfterFunc(5*time.Second, func() {
select {
case <-doneCh:
// Pass-thru
default:
panic("goroutines did not successfully return after 5 seconds. Did you forget to release a lock?")
}
})
// Start the goroutines and wait for them to finish.
close(startCh)
wg.Wait()
close(doneCh)
}
func TestEntryMap(t *testing.T) {
suite.Run(t, new(EntryMapTestSuite))
}