1 package xgb
2
3 import (
4 "bytes"
5 "errors"
6 "fmt"
7 "io"
8 "reflect"
9 "sync"
10 "testing"
11 "time"
12 )
13
14 func TestLeaks(t *testing.T) {
15 lm := leaksMonitor("lm")
16 if lgrs := lm.leakingGoroutines(); len(lgrs) != 0 {
17 t.Errorf("leakingGoroutines returned %d leaking goroutines, want 0", len(lgrs))
18 }
19
20 done := make(chan struct{})
21 wg := &sync.WaitGroup{}
22
23 wg.Add(1)
24 go func() {
25 <-done
26 wg.Done()
27 }()
28
29 if lgrs := lm.leakingGoroutines(); len(lgrs) != 1 {
30 t.Errorf("leakingGoroutines returned %d leaking goroutines, want 1", len(lgrs))
31 }
32
33 wg.Add(1)
34 go func() {
35 <-done
36 wg.Done()
37 }()
38
39 if lgrs := lm.leakingGoroutines(); len(lgrs) != 2 {
40 t.Errorf("leakingGoroutines returned %d leaking goroutines, want 2", len(lgrs))
41 }
42
43 close(done)
44 wg.Wait()
45
46 if lgrs := lm.leakingGoroutines(); len(lgrs) != 0 {
47 t.Errorf("leakingGoroutines returned %d leaking goroutines, want 0", len(lgrs))
48 }
49
50 lm.checkTesting(t)
51
52 }
53
54 func TestDummyNetConn(t *testing.T) {
55 ioStatesPairGenerator := func(writeStates, readStates []string) []func() (*dNC, error) {
56 writeSetters := map[string]func(*dNC) error{
57 "lock": (*dNC).WriteLock,
58 "error": (*dNC).WriteError,
59 "success": (*dNC).WriteSuccess,
60 }
61 readSetters := map[string]func(*dNC) error{
62 "lock": (*dNC).ReadLock,
63 "error": (*dNC).ReadError,
64 "success": (*dNC).ReadSuccess,
65 }
66
67 res := []func() (*dNC, error){}
68 for _, writeState := range writeStates {
69 writeState, writeSetter := writeState, writeSetters[writeState]
70 if writeSetter == nil {
71 panic("unknown write state: " + writeState)
72 continue
73 }
74 for _, readState := range readStates {
75 readState, readSetter := readState, readSetters[readState]
76 if readSetter == nil {
77 panic("unknown read state: " + readState)
78 continue
79 }
80 res = append(res, func() (*dNC, error) {
81
82
83 s := newDummyNetConn("w:"+writeState+";r:"+readState, func(b []byte) []byte { return b })
84
85 if err := readSetter(s); err != nil {
86 s.Close()
87 return nil, errors.New("set read " + readState + " error: " + err.Error())
88 }
89
90 if err := writeSetter(s); err != nil {
91 s.Close()
92 return nil, errors.New("set write " + writeState + " error: " + err.Error())
93 }
94
95 return s, nil
96 })
97 }
98 }
99 return res
100 }
101
102 timeout := 10 * time.Millisecond
103 wantResponse := func(action func(*dNC) error, want, block error) func(*dNC) error {
104 return func(s *dNC) error {
105 actionResult := make(chan error)
106 timedOut := make(chan struct{})
107 go func() {
108 err := action(s)
109 select {
110 case <-timedOut:
111 if err != block {
112 t.Errorf("after unblocking, action result=%v, want %v", err, block)
113 }
114 case actionResult <- err:
115 }
116 }()
117 select {
118 case err := <-actionResult:
119 if err != want {
120 return errors.New(fmt.Sprintf("action result=%v, want %v", err, want))
121 }
122 case <-time.After(timeout):
123 close(timedOut)
124 return errors.New(fmt.Sprintf("action did not respond for %v, result want %v", timeout, want))
125 }
126 return nil
127 }
128 }
129 wantBlock := func(action func(*dNC) error, unblock error) func(*dNC) error {
130 return func(s *dNC) error {
131 actionResult := make(chan error)
132 timedOut := make(chan struct{})
133 go func() {
134 err := action(s)
135 select {
136 case <-timedOut:
137 if err != unblock {
138 t.Errorf("after unblocking, action result=%v, want %v", err, unblock)
139 }
140 case actionResult <- err:
141 }
142 }()
143 select {
144 case err := <-actionResult:
145 return errors.New(fmt.Sprintf("action result=%v, want to be blocked", err))
146 case <-time.After(timeout):
147 close(timedOut)
148 }
149 return nil
150 }
151 }
152 write := func(b string) func(*dNC) error {
153 return func(s *dNC) error {
154 n, err := s.Write([]byte(b))
155 if err == nil && n != len(b) {
156 return errors.New("Write returned nil error, but not everything was written")
157 }
158 return err
159 }
160 }
161 read := func(b string) func(*dNC) error {
162 return func(s *dNC) error {
163 r := make([]byte, len(b))
164 n, err := s.Read(r)
165 if err == nil {
166 if n != len(b) {
167 return errors.New("Read returned nil error, but not everything was read")
168 }
169 if !reflect.DeepEqual(r, []byte(b)) {
170 return errors.New("Read=\"" + string(r) + "\", want \"" + string(b) + "\"")
171 }
172 }
173 return err
174 }
175 }
176
177 testCases := []struct {
178 description string
179 servers []func() (*dNC, error)
180 actions []func(*dNC) error
181 }{
182 {"close,close",
183 ioStatesPairGenerator(
184 []string{"lock", "error", "success"},
185 []string{"lock", "error", "success"},
186 ),
187 []func(*dNC) error{
188 wantResponse((*dNC).Close, nil, dNCErrClosed),
189 wantResponse((*dNC).Close, dNCErrClosed, dNCErrClosed),
190 },
191 },
192 {"write,close,write",
193 ioStatesPairGenerator(
194 []string{"lock"},
195 []string{"lock", "error", "success"},
196 ),
197 []func(*dNC) error{
198 wantBlock(write(""), dNCErrClosed),
199 wantResponse((*dNC).Close, nil, dNCErrClosed),
200 wantResponse(write(""), dNCErrClosed, dNCErrClosed),
201 },
202 },
203 {"write,close,write",
204 ioStatesPairGenerator(
205 []string{"error"},
206 []string{"lock", "error", "success"},
207 ),
208 []func(*dNC) error{
209 wantResponse(write(""), dNCErrWrite, dNCErrClosed),
210 wantResponse((*dNC).Close, nil, dNCErrClosed),
211 wantResponse(write(""), dNCErrClosed, dNCErrClosed),
212 },
213 },
214 {"write,close,write",
215 ioStatesPairGenerator(
216 []string{"success"},
217 []string{"lock", "error", "success"},
218 ),
219 []func(*dNC) error{
220 wantResponse(write(""), nil, dNCErrClosed),
221 wantResponse((*dNC).Close, nil, dNCErrClosed),
222 wantResponse(write(""), dNCErrClosed, dNCErrClosed),
223 },
224 },
225 {"read,close,read",
226 ioStatesPairGenerator(
227 []string{"lock", "error", "success"},
228 []string{"lock", "error", "success"},
229 ),
230 []func(*dNC) error{
231 wantBlock(read(""), io.EOF),
232 wantResponse((*dNC).Close, nil, dNCErrClosed),
233 wantResponse(read(""), io.EOF, io.EOF),
234 },
235 },
236 {"write,read",
237 ioStatesPairGenerator(
238 []string{"lock"},
239 []string{"lock", "error", "success"},
240 ),
241 []func(*dNC) error{
242 wantBlock(write("1"), dNCErrClosed),
243 wantBlock(read("1"), io.EOF),
244 },
245 },
246 {"write,read",
247 ioStatesPairGenerator(
248 []string{"error"},
249 []string{"lock", "error", "success"},
250 ),
251 []func(*dNC) error{
252 wantResponse(write("1"), dNCErrWrite, dNCErrClosed),
253 wantBlock(read("1"), io.EOF),
254 },
255 },
256 {"write,read",
257 ioStatesPairGenerator(
258 []string{"success"},
259 []string{"lock"},
260 ),
261 []func(*dNC) error{
262 wantResponse(write("1"), nil, dNCErrClosed),
263 wantBlock(read("1"), io.EOF),
264 },
265 },
266 {"write,read",
267 ioStatesPairGenerator(
268 []string{"success"},
269 []string{"error"},
270 ),
271 []func(*dNC) error{
272 wantResponse(write("1"), nil, dNCErrClosed),
273 wantResponse(read("1"), dNCErrRead, io.EOF),
274 },
275 },
276 {"write,read",
277 ioStatesPairGenerator(
278 []string{"success"},
279 []string{"success"},
280 ),
281 []func(*dNC) error{
282 wantResponse(write("1"), nil, dNCErrClosed),
283 wantResponse(read("1"), nil, io.EOF),
284 },
285 },
286 }
287 for _, tc := range testCases {
288 t.Run(tc.description, func(t *testing.T) {
289 defer leaksMonitor(tc.description).checkTesting(t)
290
291 for _, server := range tc.servers {
292 s, err := server()
293 if err != nil {
294 t.Error(err)
295 continue
296 }
297 if s == nil {
298 t.Error("nil server in testcase")
299 continue
300 }
301
302 t.Run(s.LocalAddr().String(), func(t *testing.T) {
303 defer leaksMonitor(s.LocalAddr().String()).checkTesting(t)
304 for _, action := range tc.actions {
305 if err := action(s); err != nil {
306 t.Error(err)
307 break
308 }
309 }
310 s.Close()
311 })
312 }
313 })
314 }
315 }
316
317 func TestDummyXServerReplier(t *testing.T) {
318 testCases := [][][2][]byte{
319 {
320 [2][]byte{[]byte("reply"), []byte{1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
321 [2][]byte{[]byte("eply"), []byte{1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
322 [2][]byte{[]byte("ply"), []byte{1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
323 [2][]byte{[]byte("event"), []byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
324 [2][]byte{[]byte("ly"), []byte{1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
325 [2][]byte{[]byte("y"), []byte{1, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
326 [2][]byte{[]byte(""), []byte{1, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
327 [2][]byte{[]byte("event"), []byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
328 [2][]byte{[]byte("reply"), []byte{1, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
329 [2][]byte{[]byte("error"), []byte{0, 255, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
330 [2][]byte{[]byte("ply"), []byte{1, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
331 [2][]byte{[]byte("event"), []byte{128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
332 [2][]byte{[]byte("ly"), []byte{1, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
333 [2][]byte{[]byte("noreply"), nil},
334 [2][]byte{[]byte("error"), []byte{0, 255, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
335 [2][]byte{[]byte("noreply"), nil},
336 [2][]byte{[]byte(""), []byte{1, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
337 },
338 }
339
340 for tci, tc := range testCases {
341 replier := newDummyXServerReplier()
342 for ai, ioPair := range tc {
343 in, want := ioPair[0], ioPair[1]
344 if out := replier(in); !bytes.Equal(out, want) {
345 t.Errorf("testCase %d, action %d, replier(%s) = %v, want %v", tci, ai, string(in), out, want)
346 break
347 }
348 }
349 }
350 }
351
View as plain text