1 package xgb
2
3 import (
4 "errors"
5 "fmt"
6 "testing"
7 "time"
8 )
9
10 func TestConnOnNonBlockingDummyXServer(t *testing.T) {
11 timeout := 10 * time.Millisecond
12 checkedReply := func(wantError bool) func(*Conn) error {
13 request := "reply"
14 if wantError {
15 request = "error"
16 }
17 return func(c *Conn) error {
18 cookie := c.NewCookie(true, true)
19 c.NewRequest([]byte(request), cookie)
20 _, err := cookie.Reply()
21 if wantError && err == nil {
22 return errors.New(fmt.Sprintf("checked request \"%v\" with reply resulted in nil error, want some error", request))
23 }
24 if !wantError && err != nil {
25 return errors.New(fmt.Sprintf("checked request \"%v\" with reply resulted in error %v, want nil error", request, err))
26 }
27 return nil
28 }
29 }
30 checkedNoreply := func(wantError bool) func(*Conn) error {
31 request := "noreply"
32 if wantError {
33 request = "error"
34 }
35 return func(c *Conn) error {
36 cookie := c.NewCookie(true, false)
37 c.NewRequest([]byte(request), cookie)
38 err := cookie.Check()
39 if wantError && err == nil {
40 return errors.New(fmt.Sprintf("checked request \"%v\" with no reply resulted in nil error, want some error", request))
41 }
42 if !wantError && err != nil {
43 return errors.New(fmt.Sprintf("checked request \"%v\" with no reply resulted in error %v, want nil error", request, err))
44 }
45 return nil
46 }
47 }
48 uncheckedReply := func(wantError bool) func(*Conn) error {
49 request := "reply"
50 if wantError {
51 request = "error"
52 }
53 return func(c *Conn) error {
54 cookie := c.NewCookie(false, true)
55 c.NewRequest([]byte(request), cookie)
56 _, err := cookie.Reply()
57 if err != nil {
58 return errors.New(fmt.Sprintf("unchecked request \"%v\" with reply resulted in %v, want nil", request, err))
59 }
60 return nil
61 }
62 }
63 uncheckedNoreply := func(wantError bool) func(*Conn) error {
64 request := "noreply"
65 if wantError {
66 request = "error"
67 }
68 return func(c *Conn) error {
69 cookie := c.NewCookie(false, false)
70 c.NewRequest([]byte(request), cookie)
71 return nil
72 }
73 }
74 event := func() func(*Conn) error {
75 return func(c *Conn) error {
76 _, err := c.conn.Write([]byte("event"))
77 if err != nil {
78 return errors.New(fmt.Sprintf("asked dummy server to send event, but resulted in error: %v\n", err))
79 }
80 return err
81 }
82 }
83 waitEvent := func(wantError bool) func(*Conn) error {
84 return func(c *Conn) error {
85 _, err := c.WaitForEvent()
86 if wantError && err == nil {
87 return errors.New(fmt.Sprintf("wait for event resulted in nil error, want some error"))
88 }
89 if !wantError && err != nil {
90 return errors.New(fmt.Sprintf("wait for event resulted in error %v, want nil error", err))
91 }
92 return nil
93 }
94 }
95 checkClosed := func(c *Conn) error {
96 select {
97 case eoe, ok := <-c.eventChan:
98 if ok {
99 return fmt.Errorf("(*Conn).eventChan should be closed, but is not and returns %v", eoe)
100 }
101 case <-time.After(timeout):
102 return fmt.Errorf("(*Conn).eventChan should be closed, but is not and was blocking for %v", timeout)
103 }
104 return nil
105 }
106
107 testCases := []struct {
108 description string
109 actions []func(*Conn) error
110 }{
111 {"close",
112 []func(*Conn) error{},
113 },
114 {"double close",
115 []func(*Conn) error{
116 func(c *Conn) error {
117 c.Close()
118 return nil
119 },
120 },
121 },
122 {"checked requests with reply",
123 []func(*Conn) error{
124 checkedReply(false),
125 checkedReply(true),
126 checkedReply(false),
127 checkedReply(true),
128 },
129 },
130 {"checked requests no reply",
131 []func(*Conn) error{
132 checkedNoreply(false),
133 checkedNoreply(true),
134 checkedNoreply(false),
135 checkedNoreply(true),
136 },
137 },
138 {"unchecked requests with reply",
139 []func(*Conn) error{
140 uncheckedReply(false),
141 uncheckedReply(true),
142 waitEvent(true),
143 uncheckedReply(false),
144 event(),
145 waitEvent(false),
146 },
147 },
148 {"unchecked requests no reply",
149 []func(*Conn) error{
150 uncheckedNoreply(false),
151 uncheckedNoreply(true),
152 waitEvent(true),
153 uncheckedNoreply(false),
154 event(),
155 waitEvent(false),
156 },
157 },
158 {"close with pending requests",
159 []func(*Conn) error{
160 func(c *Conn) error {
161 c.conn.(*dNC).ReadLock()
162 defer c.conn.(*dNC).ReadUnlock()
163 c.NewRequest([]byte("reply"), c.NewCookie(false, true))
164 c.Close()
165 return nil
166 },
167 checkClosed,
168 },
169 },
170 {"unexpected conn close",
171 []func(*Conn) error{
172 func(c *Conn) error {
173 c.conn.Close()
174 if ev, err := c.WaitForEvent(); ev != nil || err != nil {
175 return fmt.Errorf("WaitForEvent() = (%v, %v), want (nil, nil)", ev, err)
176 }
177 return nil
178 },
179 checkClosed,
180 },
181 },
182 }
183 for _, tc := range testCases {
184 t.Run(tc.description, func(t *testing.T) {
185 sclm := leaksMonitor("after server close, before testcase exit")
186 defer sclm.checkTesting(t)
187
188 s := newDummyNetConn("dummyX", newDummyXServerReplier())
189 defer s.Close()
190
191 c, err := postNewConn(&Conn{conn: s})
192 if err != nil {
193 t.Errorf("connect to dummy server error: %v", err)
194 return
195 }
196
197 defer leaksMonitor("after actions end", sclm).checkTesting(t)
198
199 for _, action := range tc.actions {
200 if err := action(c); err != nil {
201 t.Error(err)
202 break
203 }
204 }
205
206 recovered := false
207 func() {
208 defer func() {
209 if err := recover(); err != nil {
210 t.Errorf("(*Conn).Close() panic recover: %v", err)
211 recovered = true
212 }
213 }()
214
215 c.Close()
216 }()
217 if !recovered {
218 if err := checkClosed(c); err != nil {
219 t.Error(err)
220 }
221 }
222
223 })
224 }
225 }
226
View as plain text