generated from atomicgo/template
-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcounter_test.go
150 lines (115 loc) · 3.29 KB
/
counter_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
142
143
144
145
146
147
148
149
150
package counter
import (
"sync"
"sync/atomic"
"testing"
"time"
"github.com/MarvinJWendt/testza"
)
func TestCounter(t *testing.T) {
var c *Counter
t.Run("Start", func(t *testing.T) {
c = NewCounter().Start()
})
t.Run("Increment 10 times", func(t *testing.T) {
for i := 0; i < 10; i++ {
c.Increment()
}
testza.AssertEqual(t, uint64(10), c.Count())
})
t.Run("Increment another 10 times", func(t *testing.T) {
for i := 0; i < 10; i++ {
c.Increment()
}
testza.AssertEqual(t, uint64(20), c.Count())
})
t.Run("Reset counter", func(t *testing.T) {
c.Reset()
testza.AssertEqual(t, uint64(0), c.Count())
})
t.Run("Start timer again", func(t *testing.T) {
c.Start()
})
t.Run("Increment 1_000_000 times", func(t *testing.T) {
for i := 0; i < 1_000_000; i++ {
c.Increment()
}
testza.AssertEqual(t, uint64(1_000_000), c.Count())
})
t.Run("Stop", func(t *testing.T) {
c.Stop()
})
}
// TestAtomicOperations verifies that our counter works correctly
// with atomic operations, especially under concurrent access
func TestAtomicOperations(t *testing.T) {
c := NewCounter().Start()
const numGoroutines = 10
const incrementsPerGoroutine = 1000
var wg sync.WaitGroup
for i := 0; i < numGoroutines; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < incrementsPerGoroutine; j++ {
c.Increment()
}
}()
}
wg.Wait()
c.Stop()
expected := uint64(numGoroutines * incrementsPerGoroutine)
testza.AssertEqual(t, expected, c.Count(), "Count should be correct after concurrent increments")
}
// TestResetCleanup verifies that our Reset function properly cleans up all data
func TestResetCleanup(t *testing.T) {
c := NewCounter().WithAdvancedStats().Start()
// Add some increments
for i := 0; i < 10; i++ {
c.Increment()
time.Sleep(1 * time.Millisecond)
}
// Reset the counter
c.Reset()
// Verify count is reset to 0
testza.AssertEqual(t, uint64(0), c.Count(), "Count should be 0 after reset")
// Verify min/max rates are reset
testza.AssertEqual(t, 0.0, c.CalculateMinimumRate(time.Second), "Min rate should be 0 after reset")
testza.AssertEqual(t, 0.0, c.CalculateMaximumRate(time.Second), "Max rate should be 0 after reset")
// Start and increment again to verify we can use the counter after reset
c.Start()
c.Increment()
testza.AssertEqual(t, uint64(1), c.Count(), "Count should be 1 after reset and increment")
}
// TestReadWriteMutex verifies that our read-write mutex optimizations work correctly
func TestReadWriteMutex(t *testing.T) {
c := NewCounter().Start()
// Start multiple readers and one writer
const numReaders = 100
const readsPerGoroutine = 1000
// Counter for total reads completed
readsDone := int32(0)
// Start readers
var wg sync.WaitGroup
for i := 0; i < numReaders; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < readsPerGoroutine; j++ {
c.Count()
atomic.AddInt32(&readsDone, 1)
}
}()
}
// While readers are going, increment the counter periodically
go func() {
for atomic.LoadInt32(&readsDone) < int32(numReaders*readsPerGoroutine) {
c.Increment()
time.Sleep(1 * time.Millisecond)
}
}()
// Wait for all readers to finish
wg.Wait()
// If we get here without deadlock, the test passes
testza.AssertTrue(t, c.Count() > 0, "Counter should have been incremented")
}