Skip to content

Commit 1e5d650

Browse files
committed
feat: wip concurrent tests
1 parent 2044a3e commit 1e5d650

File tree

3 files changed

+213
-1
lines changed

3 files changed

+213
-1
lines changed

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ lint: ## Run golangci-lint fixing issues
99
golangci-lint run --fix
1010

1111
.PHONY: tests
12-
tests: ## Run tests
12+
tests: ## Run tests checking race conditions
1313
go test --tags=unit,integration -coverpkg=$(go list ./... | grep -v /example/)
1414

1515
.PHONY: example
1616
example: ## Run example
1717
go run ./example/main.go
18+
19+
.PHONY: racetests
20+
racetests: ## Run tests with race condition checking
21+
CGO_ENABLED=1 go test -race --tags=unit,integration -coverpkg=$(go list ./... | grep -v /example/)

concurrentset_test.go

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
//go:build unit
2+
3+
package goset_test
4+
5+
import (
6+
"math/rand"
7+
"runtime"
8+
"sync"
9+
"testing"
10+
11+
"github.com/kafkaphoenix/goset"
12+
)
13+
14+
const N = 1000
15+
16+
func TestConcurrent_Add_OK(t *testing.T) {
17+
runtime.GOMAXPROCS(2)
18+
19+
s := goset.NewSafeSet[int]()
20+
values := rand.Perm(N)
21+
22+
var wg sync.WaitGroup
23+
wg.Add(len(values))
24+
for i := range len(values) {
25+
go func(i int) {
26+
defer wg.Done()
27+
s.Add(i)
28+
}(i)
29+
}
30+
wg.Wait()
31+
for _, i := range values {
32+
if !s.Contains(i) {
33+
t.Errorf("Set is missing expected element: %v", i)
34+
}
35+
}
36+
}
37+
38+
func TestConcurrent_Remove_OK(t *testing.T) {
39+
runtime.GOMAXPROCS(2)
40+
41+
s := goset.NewSafeSet[int]()
42+
values := rand.Perm(N)
43+
for _, v := range values {
44+
s.Add(v)
45+
}
46+
47+
var wg sync.WaitGroup
48+
wg.Add(len(values))
49+
for _, v := range values {
50+
go func(i int) {
51+
defer wg.Done()
52+
s.Remove(i)
53+
}(v)
54+
}
55+
wg.Wait()
56+
57+
if s.Size() != 0 {
58+
t.Errorf("Set is not empty after removing elements")
59+
}
60+
}
61+
62+
func TestConcurrent_Contains_OK(t *testing.T) {
63+
runtime.GOMAXPROCS(2)
64+
65+
s := goset.NewSafeSet[int]()
66+
values := rand.Perm(N)
67+
for _, v := range values {
68+
s.Add(v)
69+
}
70+
71+
var wg sync.WaitGroup
72+
for _, v := range values {
73+
number := v
74+
wg.Add(1)
75+
go func() {
76+
defer wg.Done()
77+
if !s.Contains(number) {
78+
t.Errorf("Set is missing expected element: %v", number)
79+
}
80+
}()
81+
}
82+
wg.Wait()
83+
}
84+
85+
func TestConcurrent_Size_OK(t *testing.T) {
86+
runtime.GOMAXPROCS(2)
87+
88+
s := goset.NewSafeSet[int]()
89+
90+
var wg sync.WaitGroup
91+
wg.Add(1)
92+
go func() {
93+
defer wg.Done()
94+
size := s.Size()
95+
for range N {
96+
newSize := s.Size()
97+
if newSize < size {
98+
t.Errorf("Size changed unexpectedly from %d to %d", size, newSize)
99+
}
100+
}
101+
}()
102+
103+
for range N {
104+
s.Add(rand.Int())
105+
}
106+
wg.Wait()
107+
}
108+
109+
// func TestConcurrent_IsEmpty_OK(t *testing.T) {
110+
// runtime.GOMAXPROCS(2)
111+
112+
// s := goset.NewSafeSet[int]()
113+
// var wg sync.WaitGroup
114+
115+
// // concurrent writer
116+
// wg.Add(1)
117+
// go func() {
118+
// defer wg.Done()
119+
// for range N {
120+
// val := rand.Intn(N)
121+
// s.Add(val)
122+
// s.Remove(val)
123+
// }
124+
// }()
125+
126+
// // concurrent reader
127+
// wg.Add(1)
128+
// go func() {
129+
// defer wg.Done()
130+
// for range N {
131+
// cs := s.(*goset.TestSafeSet[int])
132+
// cs.RLock()
133+
// // deadlock and without this empty and size fail
134+
// // because each has a lock
135+
// if cs.IsEmpty() {
136+
// if cs.Size() != 0 {
137+
// t.Errorf("Set is empty but size is not 0")
138+
// }
139+
// }
140+
// cs.RUnlock()
141+
// }
142+
// }()
143+
144+
// wg.Wait()
145+
// }
146+
147+
// func TestConcurrent_IsEqual_OK(t *testing.T) {
148+
// runtime.GOMAXPROCS(2)
149+
150+
// s1 := goset.NewSafeSet[int]()
151+
// s2 := goset.NewSafeSet[int]()
152+
153+
// values := rand.Perm(N)
154+
// for _, v := range values {
155+
// s1.Add(v)
156+
// s2.Add(v)
157+
// }
158+
159+
// var wg sync.WaitGroup
160+
// for range N {
161+
// wg.Add(1)
162+
// go func() {
163+
// defer wg.Done()
164+
// s1.IsEqual(s2)
165+
// }()
166+
// }
167+
// wg.Wait()
168+
// }
169+
170+
// func TestConcurrent_IsEqual_OK(t *testing.T) {
171+
// runtime.GOMAXPROCS(2)
172+
173+
// s1 := goset.NewSafeSet[int]()
174+
// s2 := goset.NewSafeSet[int]()
175+
176+
// values := rand.Perm(N)
177+
// for _, v := range values {
178+
// s1.Add(v)
179+
// s2.Add(v)
180+
// }
181+
182+
// var wg sync.WaitGroup
183+
184+
// // Concurrently mutate s
185+
// wg.Add(1)
186+
// go func() {
187+
// defer wg.Done()
188+
// for i := 0; i < N; i++ {
189+
// s1.Add(rand.Intn(2 * N))
190+
// s1.Remove(rand.Intn(2 * N))
191+
// }
192+
// }()
193+
194+
// wg.Add(1)
195+
// go func() {
196+
// defer wg.Done()
197+
// for range N {
198+
// _ = s1.IsEqual(s2)
199+
// }
200+
// }()
201+
202+
// wg.Wait()
203+
// }

export_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//go:build unit
2+
3+
package goset
4+
5+
type TestSafeSet[T comparable] = safeSet[T]

0 commit comments

Comments
 (0)