1
16
17 package synctrack
18
19 import (
20 "strings"
21 "sync"
22 "time"
23
24 "testing"
25 )
26
27 func testSingleFileFuncs(upstreamHasSynced func() bool) (start func(), finished func(), hasSynced func() bool) {
28 tracker := SingleFileTracker{
29 UpstreamHasSynced: upstreamHasSynced,
30 }
31 return tracker.Start, tracker.Finished, tracker.HasSynced
32 }
33
34 func testAsyncFuncs(upstreamHasSynced func() bool) (start func(), finished func(), hasSynced func() bool) {
35 tracker := AsyncTracker[string]{
36 UpstreamHasSynced: upstreamHasSynced,
37 }
38 return func() { tracker.Start("key") }, func() { tracker.Finished("key") }, tracker.HasSynced
39 }
40
41 func TestBasicLogic(t *testing.T) {
42 table := []struct {
43 name string
44 construct func(func() bool) (func(), func(), func() bool)
45 }{
46 {"SingleFile", testSingleFileFuncs},
47 {"Async", testAsyncFuncs},
48 }
49
50 for _, entry := range table {
51 t.Run(entry.name, func(t *testing.T) {
52 table := []struct {
53 synced bool
54 start bool
55 finish bool
56 expectSynced bool
57 }{
58 {false, true, true, false},
59 {true, true, false, false},
60 {false, true, false, false},
61 {true, true, true, true},
62 }
63 for _, tt := range table {
64 Start, Finished, HasSynced := entry.construct(func() bool { return tt.synced })
65 if tt.start {
66 Start()
67 }
68 if tt.finish {
69 Finished()
70 }
71 got := HasSynced()
72 if e, a := tt.expectSynced, got; e != a {
73 t.Errorf("for %#v got %v (wanted %v)", tt, a, e)
74 }
75 }
76 })
77 }
78 }
79
80 func TestAsyncLocking(t *testing.T) {
81 aft := AsyncTracker[int]{UpstreamHasSynced: func() bool { return true }}
82
83 var wg sync.WaitGroup
84 for _, i := range []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} {
85 wg.Add(1)
86 go func(i int) {
87 aft.Start(i)
88 go func() {
89 aft.Finished(i)
90 wg.Done()
91 }()
92 }(i)
93 }
94 wg.Wait()
95 if !aft.HasSynced() {
96 t.Errorf("async tracker must have made a threading error?")
97 }
98
99 }
100
101 func TestSingleFileCounting(t *testing.T) {
102 sft := SingleFileTracker{UpstreamHasSynced: func() bool { return true }}
103
104 for i := 0; i < 100; i++ {
105 sft.Start()
106 }
107 if sft.HasSynced() {
108 t.Fatal("Unexpectedly synced?")
109 }
110 for i := 0; i < 99; i++ {
111 sft.Finished()
112 }
113 if sft.HasSynced() {
114 t.Fatal("Unexpectedly synced?")
115 }
116
117 sft.Finished()
118 if !sft.HasSynced() {
119 t.Fatal("Unexpectedly not synced?")
120 }
121
122
123 func() {
124 defer func() {
125 x := recover()
126 if x == nil {
127 t.Error("no panic?")
128 return
129 }
130 msg, ok := x.(string)
131 if !ok {
132 t.Errorf("unexpected panic value: %v", x)
133 return
134 }
135 if !strings.Contains(msg, "negative counter") {
136 t.Errorf("unexpected panic message: %v", msg)
137 return
138 }
139 }()
140 sft.Finished()
141 }()
142
143
144 if !sft.HasSynced() {
145 t.Fatal("Unexpectedly not synced?")
146 }
147 }
148
149 func TestSingleFile(t *testing.T) {
150 table := []struct {
151 synced bool
152 starts int
153 stops int
154 expectSynced bool
155 }{
156 {false, 1, 1, false},
157 {true, 1, 0, false},
158 {false, 1, 0, false},
159 {true, 1, 1, true},
160 }
161 for _, tt := range table {
162 sft := SingleFileTracker{UpstreamHasSynced: func() bool { return tt.synced }}
163 for i := 0; i < tt.starts; i++ {
164 sft.Start()
165 }
166 for i := 0; i < tt.stops; i++ {
167 sft.Finished()
168 }
169 got := sft.HasSynced()
170 if e, a := tt.expectSynced, got; e != a {
171 t.Errorf("for %#v got %v (wanted %v)", tt, a, e)
172 }
173 }
174
175 }
176
177 func TestNoStaleValue(t *testing.T) {
178 table := []struct {
179 name string
180 construct func(func() bool) (func(), func(), func() bool)
181 }{
182 {"SingleFile", testSingleFileFuncs},
183 {"Async", testAsyncFuncs},
184 }
185
186 for _, entry := range table {
187 t.Run(entry.name, func(t *testing.T) {
188 var lock sync.Mutex
189 upstreamHasSynced := func() bool {
190 lock.Lock()
191 defer lock.Unlock()
192 return true
193 }
194
195 Start, Finished, HasSynced := entry.construct(upstreamHasSynced)
196
197
198
199 if !HasSynced() {
200 t.Fatal("Unexpectedly not synced??")
201 }
202
203 Start()
204 if HasSynced() {
205 t.Fatal("Unexpectedly synced??")
206 }
207 Finished()
208 if !HasSynced() {
209 t.Fatal("Unexpectedly not synced??")
210 }
211
212
213
214 lock.Lock()
215
216
217 var wg sync.WaitGroup
218 wg.Add(1)
219 go func() {
220 defer wg.Done()
221 if HasSynced() {
222 t.Error("Unexpectedly synced??")
223 }
224 }()
225
226
227
228
229 go func() {
230 time.Sleep(time.Millisecond)
231 Start()
232 lock.Unlock()
233 }()
234
235 wg.Wait()
236 })
237 }
238
239 }
240
View as plain text