Merge lp:~chipaca/ubuntu-push/no-more-session-autoredial into lp:ubuntu-push/automatic

Proposed by John Lenton
Status: Merged
Approved by: John Lenton
Approved revision: 381
Merged at revision: 375
Proposed branch: lp:~chipaca/ubuntu-push/no-more-session-autoredial
Merge into: lp:ubuntu-push/automatic
Prerequisite: lp:~chipaca/ubuntu-push/session-state-lock
Diff against target: 1169 lines (+401/-196)
4 files modified
client/client.go (+4/-26)
client/client_test.go (+30/-109)
client/session/session.go (+106/-7)
client/session/session_test.go (+261/-54)
To merge this branch: bzr merge lp:~chipaca/ubuntu-push/no-more-session-autoredial
Reviewer Review Type Date Requested Status
Samuele Pedroni Approve
Review via email: mp+252612@code.launchpad.net

Commit message

Remove AutoRedial from ClientSession's interface.

To post a comment you must log in.
378. By John Lenton

removed spurious test

379. By John Lenton

merged from lp

Revision history for this message
Samuele Pedroni (pedronis) wrote :

see comments

Revision history for this message
John Lenton (chipaca) wrote :

Agreed on both points, fixing.

380. By John Lenton

call close from stopCh handler; set state to shutdown before closing stopCh.

381. By John Lenton

repeat myself inside the stopCh handler, because it's not *exactly* doClose what I want, and there isn't yet a clear way of generalizing that function without making a mess down-pipe.

Revision history for this message
Samuele Pedroni (pedronis) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'client/client.go'
2--- client/client.go 2015-03-03 10:46:04 +0000
3+++ client/client.go 2015-03-17 16:47:16 +0000
4@@ -115,7 +115,6 @@
5 systemImageEndp bus.Endpoint
6 systemImageInfo *systemimage.InfoResult
7 connCh chan bool
8- hasConnectivity bool
9 session session.ClientSession
10 sessionConnectedCh chan uint32
11 pushService PushService
12@@ -126,7 +125,6 @@
13 poller poller.Poller
14 accountsCh <-chan accounts.Changed
15 // session-side channels
16- errCh chan error
17 broadcastCh chan *session.BroadcastNotification
18 notificationsCh chan session.AddressedNotification
19 }
20@@ -137,7 +135,6 @@
21 return &PushClient{
22 configPath: configPath,
23 leveldbPath: leveldbPath,
24- errCh: make(chan error),
25 broadcastCh: make(chan *session.BroadcastNotification),
26 notificationsCh: make(chan session.AddressedNotification),
27 }
28@@ -212,7 +209,6 @@
29 AuthGetter: client.getAuthorization,
30 AuthURL: client.config.SessionURL,
31 AddresseeChecker: client,
32- ErrCh: client.errCh,
33 BroadcastCh: client.broadcastCh,
34 NotificationsCh: client.notificationsCh,
35 }
36@@ -317,6 +313,7 @@
37 return err
38 }
39 client.session = sess
40+ sess.KeepConnection()
41 client.poller = poller.New(client.derivePollerSetup())
42 return nil
43 }
44@@ -388,24 +385,8 @@
45 // handleConnState deals with connectivity events
46 func (client *PushClient) handleConnState(hasConnectivity bool) {
47 client.log.Debugf("handleConnState: %v", hasConnectivity)
48- if client.hasConnectivity == hasConnectivity {
49- // nothing to do!
50- return
51- }
52- client.hasConnectivity = hasConnectivity
53- client.session.Close()
54- if hasConnectivity {
55- client.session.AutoRedial(client.sessionConnectedCh)
56- }
57-}
58-
59-// handleErr deals with the session erroring out of its loop
60-func (client *PushClient) handleErr(err error) {
61- // if we're not connected, we don't really care
62- client.log.Errorf("session exited: %s", err)
63- if client.hasConnectivity {
64- client.session.AutoRedial(client.sessionConnectedCh)
65- }
66+ client.session.HasConnectivity(hasConnectivity)
67+ client.log.Debugf("handled.")
68 }
69
70 // filterBroadcastNotification finds out if the notification is about an actual
71@@ -487,7 +468,7 @@
72 }
73
74 // doLoop connects events with their handlers
75-func (client *PushClient) doLoop(connhandler func(bool), bcasthandler func(*session.BroadcastNotification) error, ucasthandler func(session.AddressedNotification) error, errhandler func(error), unregisterhandler func(*click.AppId), accountshandler func()) {
76+func (client *PushClient) doLoop(connhandler func(bool), bcasthandler func(*session.BroadcastNotification) error, ucasthandler func(session.AddressedNotification) error, unregisterhandler func(*click.AppId), accountshandler func()) {
77 for {
78 select {
79 case <-client.accountsCh:
80@@ -498,8 +479,6 @@
81 bcasthandler(bcast)
82 case aucast := <-client.notificationsCh:
83 ucasthandler(aucast)
84- case err := <-client.errCh:
85- errhandler(err)
86 case count := <-client.sessionConnectedCh:
87 client.log.Debugf("session connected after %d attempts", count)
88 case app := <-client.unregisterCh:
89@@ -524,7 +503,6 @@
90 client.doLoop(client.handleConnState,
91 client.handleBroadcastNotification,
92 client.handleUnicastNotification,
93- client.handleErr,
94 client.handleUnregister,
95 client.handleAccountsChange,
96 )
97
98=== modified file 'client/client_test.go'
99--- client/client_test.go 2015-03-03 10:46:04 +0000
100+++ client/client_test.go 2015-03-17 16:47:16 +0000
101@@ -43,7 +43,6 @@
102 clickhelp "launchpad.net/ubuntu-push/click/testing"
103 "launchpad.net/ubuntu-push/client/service"
104 "launchpad.net/ubuntu-push/client/session"
105- "launchpad.net/ubuntu-push/client/session/seenstate"
106 "launchpad.net/ubuntu-push/config"
107 "launchpad.net/ubuntu-push/identifier"
108 idtesting "launchpad.net/ubuntu-push/identifier/testing"
109@@ -427,7 +426,6 @@
110 AuthGetter: func(string) string { return "" },
111 AuthURL: "xyzzy://",
112 AddresseeChecker: cli,
113- ErrCh: make(chan error),
114 BroadcastCh: make(chan *session.BroadcastNotification),
115 NotificationsCh: make(chan session.AddressedNotification),
116 }
117@@ -444,10 +442,8 @@
118 // compare authGetter by string
119 c.Check(fmt.Sprintf("%#v", conf.AuthGetter), Equals, fmt.Sprintf("%#v", cli.getAuthorization))
120 // channels are ok as long as non-nil
121- conf.ErrCh = nil
122 conf.BroadcastCh = nil
123 conf.NotificationsCh = nil
124- expected.ErrCh = nil
125 expected.BroadcastCh = nil
126 expected.NotificationsCh = nil
127 // and set it to nil
128@@ -534,9 +530,11 @@
129 type derivePollerSession struct{}
130
131 func (s *derivePollerSession) Close() {}
132-func (s *derivePollerSession) AutoRedial(ch chan uint32) {}
133 func (s *derivePollerSession) ClearCookie() {}
134 func (s *derivePollerSession) State() session.ClientSessionState { return session.Unknown }
135+func (s *derivePollerSession) HasConnectivity(bool) error { return nil }
136+func (s *derivePollerSession) KeepConnection() error { return nil }
137+func (s *derivePollerSession) StopKeepConnection() {}
138
139 func (cs *clientSuite) TestDerivePollerSetup(c *C) {
140 cs.writeTestConfig(map[string]interface{}{})
141@@ -722,22 +720,6 @@
142 }
143
144 /*****************************************************************
145- handleErr tests
146-******************************************************************/
147-
148-func (cs *clientSuite) TestHandleErr(c *C) {
149- cli := NewPushClient(cs.configPath, cs.leveldbPath)
150- cli.log = cs.log
151- cli.systemImageInfo = siInfoRes
152- c.Assert(cli.initSessionAndPoller(), IsNil)
153- cs.log.ResetCapture()
154- cli.hasConnectivity = true
155- defer cli.session.Close()
156- cli.handleErr(errors.New("bananas"))
157- c.Check(cs.log.Captured(), Matches, ".*session exited.*bananas\n")
158-}
159-
160-/*****************************************************************
161 seenStateFactory tests
162 ******************************************************************/
163
164@@ -758,66 +740,6 @@
165 }
166
167 /*****************************************************************
168- handleConnState tests
169-******************************************************************/
170-
171-type handleConnStateSession struct {
172- connected bool
173-}
174-
175-func (s *handleConnStateSession) AutoRedial(ch chan uint32) { s.connected = true }
176-func (s *handleConnStateSession) Close() { s.connected = false }
177-func (s *handleConnStateSession) ClearCookie() {}
178-func (s *handleConnStateSession) State() session.ClientSessionState { return session.Unknown }
179-
180-func (cs *clientSuite) TestHandleConnStateD2C(c *C) {
181- cli := NewPushClient(cs.configPath, cs.leveldbPath)
182- cli.log = cs.log
183- sess := &handleConnStateSession{connected: false}
184- cli.session = sess
185-
186- c.Assert(cli.hasConnectivity, Equals, false)
187- cli.handleConnState(true)
188- c.Check(cli.hasConnectivity, Equals, true)
189- c.Check(sess.connected, Equals, true)
190-}
191-
192-func (cs *clientSuite) TestHandleConnStateSame(c *C) {
193- cli := NewPushClient(cs.configPath, cs.leveldbPath)
194- cli.log = cs.log
195- // here we want to check that we don't do anything
196- c.Assert(cli.session, IsNil)
197- c.Assert(cli.hasConnectivity, Equals, false)
198- cli.handleConnState(false)
199- c.Check(cli.session, IsNil)
200-
201- cli.hasConnectivity = true
202- cli.handleConnState(true)
203- c.Check(cli.session, IsNil)
204-}
205-
206-func (cs *clientSuite) TestHandleConnStateC2D(c *C) {
207- cli := NewPushClient(cs.configPath, cs.leveldbPath)
208- cli.log = cs.log
209- sess := &handleConnStateSession{connected: true}
210- cli.session = sess
211- cli.hasConnectivity = true
212-
213- cli.handleConnState(false)
214- c.Check(sess.connected, Equals, false)
215-}
216-
217-func (cs *clientSuite) TestHandleConnStateC2DPending(c *C) {
218- cli := NewPushClient(cs.configPath, cs.leveldbPath)
219- cli.log = cs.log
220- cli.session, _ = session.NewSession(cli.config.Addr, cli.deriveSessionConfig(nil), cli.deviceId, seenstate.NewSeenState, cs.log)
221- cli.hasConnectivity = true
222-
223- cli.handleConnState(false)
224- c.Check(cli.session.State(), Equals, session.Disconnected)
225-}
226-
227-/*****************************************************************
228 filterBroadcastNotification tests
229 ******************************************************************/
230
231@@ -1035,7 +957,6 @@
232 var nopConn = func(bool) {}
233 var nopBcast = func(*session.BroadcastNotification) error { return nil }
234 var nopUcast = func(session.AddressedNotification) error { return nil }
235-var nopError = func(error) {}
236 var nopUnregister = func(*click.AppId) {}
237 var nopAcct = func() {}
238
239@@ -1048,7 +969,7 @@
240 c.Assert(cli.initSessionAndPoller(), IsNil)
241
242 ch := make(chan bool, 1)
243- go cli.doLoop(func(bool) { ch <- true }, nopBcast, nopUcast, nopError, nopUnregister, nopAcct)
244+ go cli.doLoop(func(bool) { ch <- true }, nopBcast, nopUcast, nopUnregister, nopAcct)
245 c.Check(takeNextBool(ch), Equals, true)
246 }
247
248@@ -1061,7 +982,7 @@
249 cli.broadcastCh <- &session.BroadcastNotification{}
250
251 ch := make(chan bool, 1)
252- go cli.doLoop(nopConn, func(_ *session.BroadcastNotification) error { ch <- true; return nil }, nopUcast, nopError, nopUnregister, nopAcct)
253+ go cli.doLoop(nopConn, func(_ *session.BroadcastNotification) error { ch <- true; return nil }, nopUcast, nopUnregister, nopAcct)
254 c.Check(takeNextBool(ch), Equals, true)
255 }
256
257@@ -1074,20 +995,7 @@
258 cli.notificationsCh <- session.AddressedNotification{}
259
260 ch := make(chan bool, 1)
261- go cli.doLoop(nopConn, nopBcast, func(session.AddressedNotification) error { ch <- true; return nil }, nopError, nopUnregister, nopAcct)
262- c.Check(takeNextBool(ch), Equals, true)
263-}
264-
265-func (cs *clientSuite) TestDoLoopErr(c *C) {
266- cli := NewPushClient(cs.configPath, cs.leveldbPath)
267- cli.log = cs.log
268- cli.systemImageInfo = siInfoRes
269- c.Assert(cli.initSessionAndPoller(), IsNil)
270- cli.errCh = make(chan error, 1)
271- cli.errCh <- nil
272-
273- ch := make(chan bool, 1)
274- go cli.doLoop(nopConn, nopBcast, nopUcast, func(error) { ch <- true }, nopUnregister, nopAcct)
275+ go cli.doLoop(nopConn, nopBcast, func(session.AddressedNotification) error { ch <- true; return nil }, nopUnregister, nopAcct)
276 c.Check(takeNextBool(ch), Equals, true)
277 }
278
279@@ -1100,7 +1008,7 @@
280 cli.unregisterCh <- app1
281
282 ch := make(chan bool, 1)
283- go cli.doLoop(nopConn, nopBcast, nopUcast, nopError, func(app *click.AppId) { c.Check(app.Original(), Equals, appId1); ch <- true }, nopAcct)
284+ go cli.doLoop(nopConn, nopBcast, nopUcast, func(app *click.AppId) { c.Check(app.Original(), Equals, appId1); ch <- true }, nopAcct)
285 c.Check(takeNextBool(ch), Equals, true)
286 }
287
288@@ -1114,7 +1022,7 @@
289 cli.accountsCh = acctCh
290
291 ch := make(chan bool, 1)
292- go cli.doLoop(nopConn, nopBcast, nopUcast, nopError, nopUnregister, func() { ch <- true })
293+ go cli.doLoop(nopConn, nopBcast, nopUcast, nopUnregister, func() { ch <- true })
294 c.Check(takeNextBool(ch), Equals, true)
295 }
296
297@@ -1149,6 +1057,21 @@
298 Loop() tests
299 ******************************************************************/
300
301+type loopSession struct{ hasConn bool }
302+
303+func (s *loopSession) Close() {}
304+func (s *loopSession) ClearCookie() {}
305+func (s *loopSession) State() session.ClientSessionState {
306+ if s.hasConn {
307+ return session.Connected
308+ } else {
309+ return session.Disconnected
310+ }
311+}
312+func (s *loopSession) HasConnectivity(hasConn bool) error { s.hasConn = hasConn; return nil }
313+func (s *loopSession) KeepConnection() error { return nil }
314+func (s *loopSession) StopKeepConnection() {}
315+
316 func (cs *clientSuite) TestLoop(c *C) {
317 cli := NewPushClient(cs.configPath, cs.leveldbPath)
318 cli.connCh = make(chan bool)
319@@ -1164,7 +1087,6 @@
320 c.Assert(cli.initSessionAndPoller(), IsNil)
321
322 cli.broadcastCh = make(chan *session.BroadcastNotification)
323- cli.errCh = make(chan error)
324
325 // we use tick() to make sure things have been through the
326 // event loop at least once before looking at things;
327@@ -1179,26 +1101,25 @@
328 tick()
329 c.Check(cs.log.Captured(), Matches, "(?msi).*Session connected after 42 attempts$")
330
331+ c.Assert(cli.session, NotNil)
332+ cli.session.StopKeepConnection()
333+ cli.session = &loopSession{}
334+
335 // loop() should have connected:
336 // * connCh to the connectivity checker
337- c.Check(cli.hasConnectivity, Equals, false)
338+ c.Check(cli.session.State(), Equals, session.Disconnected)
339 cli.connCh <- true
340 tick()
341- c.Check(cli.hasConnectivity, Equals, true)
342+ c.Check(cli.session.State(), Equals, session.Connected)
343 cli.connCh <- false
344 tick()
345- c.Check(cli.hasConnectivity, Equals, false)
346+ c.Check(cli.session.State(), Equals, session.Disconnected)
347
348 // * session.BroadcastCh to the notifications handler
349 c.Check(d.bcastCount, Equals, 0)
350 cli.broadcastCh <- positiveBroadcastNotification
351 tick()
352 c.Check(d.bcastCount, Equals, 1)
353-
354- // * session.ErrCh to the error handler
355- cli.errCh <- nil
356- tick()
357- c.Check(cs.log.Captured(), Matches, "(?ms).*session exited.*")
358 }
359
360 /*****************************************************************
361
362=== modified file 'client/session/session.go'
363--- client/session/session.go 2015-03-12 12:05:40 +0000
364+++ client/session/session.go 2015-03-17 16:47:16 +0000
365@@ -70,10 +70,12 @@
366
367 const (
368 Error ClientSessionState = iota
369+ Pristine
370 Disconnected
371 Connected
372 Started
373 Running
374+ Shutdown
375 Unknown
376 )
377
378@@ -83,10 +85,12 @@
379 }
380 return [Unknown]string{
381 "Error",
382+ "Pristine",
383 "Disconnected",
384 "Connected",
385 "Started",
386 "Running",
387+ "Shutdown",
388 }[s]
389 }
390
391@@ -118,7 +122,6 @@
392 AuthGetter func(string) string
393 AuthURL string
394 AddresseeChecker AddresseeChecking
395- ErrCh chan error
396 BroadcastCh chan *BroadcastNotification
397 NotificationsCh chan AddressedNotification
398 }
399@@ -126,9 +129,11 @@
400 // ClientSession holds a client<->server session and its configuration.
401 type ClientSession interface {
402 Close()
403- AutoRedial(doneCh chan uint32)
404 ClearCookie()
405 State() ClientSessionState
406+ HasConnectivity(bool) error
407+ KeepConnection() error
408+ StopKeepConnection()
409 }
410
411 type clientSession struct {
412@@ -169,6 +174,20 @@
413 redialJitter func(time.Duration) time.Duration
414 redialDelays []time.Duration
415 redialDelaysIdx int
416+ // connection events come in over here
417+ connCh chan bool
418+ // last seen connection event is here
419+ lastConn bool
420+ // connection events are handled by this
421+ connHandler func(bool)
422+ // autoredial goes over here (xxx spurious goroutine involved)
423+ doneCh chan uint32
424+ // main loop errors out through here (possibly another spurious goroutine)
425+ errCh chan error
426+ // main loop errors are handled by this
427+ errHandler func(error)
428+ // look, a stopper!
429+ stopCh chan struct{}
430 }
431
432 func redialDelay(sess *clientSession) time.Duration {
433@@ -207,10 +226,10 @@
434 Protocolator: protocol.NewProtocol0,
435 SeenState: seenState,
436 TLS: &tls.Config{},
437- state: Disconnected,
438+ state: Pristine,
439 timeSince: time.Since,
440 shouldDelayP: &shouldDelay,
441- redialDelay: redialDelay,
442+ redialDelay: redialDelay, // NOTE there are tests that use calling sess.redialDelay as an indication of calling autoRedial!
443 redialDelays: util.Timeouts(),
444 }
445 sess.redialJitter = sess.Jitter
446@@ -222,6 +241,15 @@
447 }
448 sess.TLS.RootCAs = cp
449 }
450+ sess.doneCh = make(chan uint32, 1)
451+ sess.stopCh = make(chan struct{})
452+ sess.connCh = make(chan bool, 1)
453+ sess.errCh = make(chan error, 1)
454+
455+ // to be overridden by tests
456+ sess.connHandler = sess.handleConn
457+ sess.errHandler = sess.handleErr
458+
459 return sess, nil
460 }
461
462@@ -383,7 +411,7 @@
463 }
464 }
465
466-func (sess *clientSession) AutoRedial(doneCh chan uint32) {
467+func (sess *clientSession) autoRedial() {
468 sess.stopRedial()
469 if time.Since(sess.lastAutoRedial) < 2*time.Second {
470 sess.setShouldDelay()
471@@ -405,7 +433,7 @@
472 return
473 }
474 sess.Log.Debugf("session autoredialier launching Redial goroutine")
475- doneCh <- retrier.Redial()
476+ sess.doneCh <- retrier.Redial()
477 }()
478 }
479
480@@ -657,7 +685,7 @@
481 if err == nil {
482 err = starter()
483 if err == nil {
484- go func() { sess.ErrCh <- looper() }()
485+ go func() { sess.errCh <- looper() }()
486 }
487 }
488 return err
489@@ -684,6 +712,77 @@
490 return sess.run(sess.doClose, sess.addAuthorization, sess.getHosts, sess.connect, sess.start, sess.loop)
491 }
492
493+func (sess *clientSession) doKeepConnection() {
494+Loop:
495+ for {
496+ select {
497+ case hasConn := <-sess.connCh:
498+ sess.connHandler(hasConn)
499+ case <-sess.stopCh:
500+ sess.Log.Infof("session shutting down.")
501+ sess.connLock.Lock()
502+ defer sess.connLock.Unlock()
503+ sess.stopRedial()
504+ if sess.Connection != nil {
505+ sess.Connection.Close()
506+ sess.Connection = nil
507+ }
508+ break Loop
509+ case n := <-sess.doneCh:
510+ sess.Log.Debugf("connected after %d attempts.", n)
511+ case err := <-sess.errCh:
512+ sess.errHandler(err)
513+ }
514+ }
515+}
516+
517+func (sess *clientSession) handleConn(hasConn bool) {
518+ sess.lastConn = hasConn
519+
520+ // Note this does not depend on the current state! That's because Dial
521+ // starts with doClose, which gets you to Disconnected even if you're
522+ // connected, and you can call Close when Disconnected without it
523+ // losing its stuff.
524+ if hasConn {
525+ sess.autoRedial()
526+ } else {
527+ sess.Close()
528+ }
529+}
530+
531+func (sess *clientSession) handleErr(err error) {
532+ sess.Log.Errorf("session error'ed out with %v", err)
533+ sess.stateLock.Lock()
534+ if sess.state == Disconnected && sess.lastConn {
535+ sess.autoRedial()
536+ }
537+ sess.stateLock.Unlock()
538+}
539+
540+func (sess *clientSession) KeepConnection() error {
541+ sess.stateLock.Lock()
542+ defer sess.stateLock.Unlock()
543+ if sess.state != Pristine {
544+ return errors.New("don't call KeepConnection() on a non-pristine session.")
545+ }
546+ sess.state = Disconnected
547+
548+ go sess.doKeepConnection()
549+
550+ return nil
551+}
552+
553+func (sess *clientSession) StopKeepConnection() {
554+ sess.setState(Shutdown)
555+ close(sess.stopCh)
556+}
557+
558+func (sess *clientSession) HasConnectivity(hasConn bool) error {
559+ sess.connCh <- hasConn
560+ // XXX throw errors if called from weird state?
561+ return nil
562+}
563+
564 func init() {
565 rand.Seed(time.Now().Unix()) // good enough for us (we're not using it for crypto)
566 }
567
568=== modified file 'client/session/session_test.go'
569--- client/session/session_test.go 2015-02-13 11:20:41 +0000
570+++ client/session/session_test.go 2015-03-17 16:47:16 +0000
571@@ -190,6 +190,24 @@
572 cs.lvls = func() (seenstate.SeenState, error) { return seenstate.NewSqliteSeenState(":memory:") }
573 }
574
575+func (cs *clientSessionSuite) TestStateString(c *C) {
576+ for _, i := range []struct {
577+ v ClientSessionState
578+ s string
579+ }{
580+ {Error, "Error"},
581+ {Pristine, "Pristine"},
582+ {Disconnected, "Disconnected"},
583+ {Connected, "Connected"},
584+ {Started, "Started"},
585+ {Running, "Running"},
586+ {Shutdown, "Shutdown"},
587+ {Unknown, fmt.Sprintf("??? (%d)", Unknown)},
588+ } {
589+ c.Check(i.v.String(), Equals, i.s)
590+ }
591+}
592+
593 /****************************************************************
594 parseServerAddrSpec() tests
595 ****************************************************************/
596@@ -214,7 +232,6 @@
597
598 func dummyConf() ClientSessionConfig {
599 return ClientSessionConfig{
600- ErrCh: make(chan error, 1),
601 BroadcastCh: make(chan *BroadcastNotification, 5),
602 NotificationsCh: make(chan AddressedNotification, 5),
603 }
604@@ -231,7 +248,9 @@
605 c.Check(sess.redialDelays, DeepEquals, util.Timeouts())
606 // but no root CAs set
607 c.Check(sess.TLS.RootCAs, IsNil)
608- c.Check(sess.State(), Equals, Disconnected)
609+ c.Check(sess.State(), Equals, Pristine)
610+ c.Check(sess.stopCh, NotNil)
611+ c.Check(sess.connCh, NotNil)
612 }
613
614 func (cs *clientSessionSuite) TestNewSessionHostEndpointWorks(c *C) {
615@@ -569,9 +588,9 @@
616 c.Check(ar.stopped, Equals, true)
617 }
618
619-/****************************************************************
620- AutoRedial() tests
621-****************************************************************/
622+// /****************************************************************
623+// AutoRedial() tests
624+// ****************************************************************/
625
626 func (cs *clientSessionSuite) TestAutoRedialWorks(c *C) {
627 // checks that AutoRedial sets up a retrier and tries redialing it
628@@ -580,7 +599,8 @@
629 ar := new(derp)
630 sess.retrier = ar
631 c.Check(ar.stopped, Equals, false)
632- sess.AutoRedial(nil)
633+ sess.autoRedial()
634+ defer sess.stopRedial()
635 c.Check(ar.stopped, Equals, true)
636 }
637
638@@ -588,20 +608,21 @@
639 // checks that AutoRedial stops the previous retrier
640 sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log)
641 c.Assert(err, IsNil)
642- ch := make(chan uint32)
643+ sess.doneCh = make(chan uint32)
644 c.Check(sess.retrier, IsNil)
645- sess.AutoRedial(ch)
646+ sess.autoRedial()
647 c.Assert(sess.retrier, NotNil)
648 sess.retrier.Stop()
649- c.Check(<-ch, Not(Equals), 0)
650+ c.Check(<-sess.doneCh, Not(Equals), 0)
651 }
652
653 func (cs *clientSessionSuite) TestAutoRedialCallsRedialDelay(c *C) {
654+ // NOTE there are tests that use calling redialDelay as an indication of calling autoRedial!
655 sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log)
656 c.Assert(err, IsNil)
657 flag := false
658 sess.redialDelay = func(sess *clientSession) time.Duration { flag = true; return 0 }
659- sess.AutoRedial(nil)
660+ sess.autoRedial()
661 c.Check(flag, Equals, true)
662 }
663
664@@ -609,11 +630,11 @@
665 sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log)
666 c.Assert(err, IsNil)
667 sess.redialDelay = func(sess *clientSession) time.Duration { return 0 }
668- sess.AutoRedial(nil)
669+ sess.autoRedial()
670 c.Check(sess.ShouldDelay(), Equals, false)
671 sess.stopRedial()
672 sess.clearShouldDelay()
673- sess.AutoRedial(nil)
674+ sess.autoRedial()
675 c.Check(sess.ShouldDelay(), Equals, true)
676 }
677
678@@ -625,7 +646,6 @@
679 sess *clientSession
680 upCh chan interface{}
681 downCh chan interface{}
682- errCh chan error
683 }
684
685 var _ = Suite(&msgSuite{})
686@@ -637,7 +657,6 @@
687 s.sess, err = NewSession("", conf, "wah", seenstate.NewSeenState, helpers.NewTestLogger(c, "debug"))
688 c.Assert(err, IsNil)
689 s.sess.Connection = &testConn{Name: "TestHandle*"}
690- s.errCh = conf.ErrCh
691 s.upCh = make(chan interface{}, 5)
692 s.downCh = make(chan interface{}, 5)
693 s.sess.proto = &testProtocol{up: s.upCh, down: s.downCh}
694@@ -696,10 +715,10 @@
695 json.RawMessage(`{"img1/m1":[102,"tubular"]}`),
696 },
697 }
698- go func() { s.errCh <- s.sess.handleBroadcast(msg) }()
699+ go func() { s.sess.errCh <- s.sess.handleBroadcast(msg) }()
700 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"})
701 s.upCh <- nil // ack ok
702- c.Check(<-s.errCh, Equals, nil)
703+ c.Check(<-s.sess.errCh, Equals, nil)
704 c.Assert(len(s.sess.BroadcastCh), Equals, 1)
705 c.Check(<-s.sess.BroadcastCh, DeepEquals, &BroadcastNotification{
706 TopLevel: 2,
707@@ -728,11 +747,11 @@
708 TopLevel: 2,
709 Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)},
710 }
711- go func() { s.errCh <- s.sess.handleBroadcast(msg) }()
712+ go func() { s.sess.errCh <- s.sess.handleBroadcast(msg) }()
713 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"})
714 failure := errors.New("ACK ACK ACK")
715 s.upCh <- failure
716- c.Assert(<-s.errCh, Equals, failure)
717+ c.Assert(<-s.sess.errCh, Equals, failure)
718 c.Check(s.sess.State(), Equals, Error)
719 }
720
721@@ -746,10 +765,10 @@
722 TopLevel: 2,
723 Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)},
724 }
725- go func() { s.errCh <- s.sess.handleBroadcast(msg) }()
726+ go func() { s.sess.errCh <- s.sess.handleBroadcast(msg) }()
727 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"})
728 s.upCh <- nil // ack ok
729- c.Check(<-s.errCh, IsNil)
730+ c.Check(<-s.sess.errCh, IsNil)
731 c.Check(len(s.sess.BroadcastCh), Equals, 0)
732 }
733
734@@ -764,10 +783,10 @@
735 TopLevel: 2,
736 Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)},
737 }
738- go func() { s.errCh <- s.sess.handleBroadcast(msg) }()
739+ go func() { s.sess.errCh <- s.sess.handleBroadcast(msg) }()
740 s.upCh <- nil // ack ok
741 // start returns with error
742- c.Check(<-s.errCh, Not(Equals), nil)
743+ c.Check(<-s.sess.errCh, Not(Equals), nil)
744 c.Check(s.sess.State(), Equals, Error)
745 // no message sent out
746 c.Check(len(s.sess.BroadcastCh), Equals, 0)
747@@ -780,10 +799,10 @@
748 s.sess.setShouldDelay()
749
750 msg := &serverMsg{Type: "broadcast"}
751- go func() { s.errCh <- s.sess.handleBroadcast(msg) }()
752+ go func() { s.sess.errCh <- s.sess.handleBroadcast(msg) }()
753 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"})
754 s.upCh <- nil // ack ok
755- c.Check(<-s.errCh, IsNil)
756+ c.Check(<-s.sess.errCh, IsNil)
757
758 c.Check(s.sess.ShouldDelay(), Equals, false)
759 }
760@@ -792,10 +811,10 @@
761 s.sess.setShouldDelay()
762
763 msg := &serverMsg{Type: "broadcast"}
764- go func() { s.errCh <- s.sess.handleBroadcast(msg) }()
765+ go func() { s.sess.errCh <- s.sess.handleBroadcast(msg) }()
766 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"})
767 s.upCh <- errors.New("bcast")
768- c.Check(<-s.errCh, NotNil)
769+ c.Check(<-s.sess.errCh, NotNil)
770
771 c.Check(s.sess.ShouldDelay(), Equals, true)
772 }
773@@ -845,10 +864,10 @@
774 msg.NotificationsMsg = protocol.NotificationsMsg{
775 Notifications: []protocol.Notification{n1, n2},
776 }
777- go func() { s.errCh <- s.sess.handleNotifications(msg) }()
778+ go func() { s.sess.errCh <- s.sess.handleNotifications(msg) }()
779 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"})
780 s.upCh <- nil // ack ok
781- c.Check(<-s.errCh, Equals, nil)
782+ c.Check(<-s.sess.errCh, Equals, nil)
783 c.Check(s.sess.ShouldDelay(), Equals, false)
784 c.Assert(s.sess.NotificationsCh, HasLen, 2)
785 app1, err := click.ParseAppId("com.example.app1_app1")
786@@ -891,10 +910,10 @@
787 msg.NotificationsMsg = protocol.NotificationsMsg{
788 Notifications: []protocol.Notification{n1, n2},
789 }
790- go func() { s.errCh <- s.sess.handleNotifications(msg) }()
791+ go func() { s.sess.errCh <- s.sess.handleNotifications(msg) }()
792 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"})
793 s.upCh <- nil // ack ok
794- c.Check(<-s.errCh, Equals, nil)
795+ c.Check(<-s.sess.errCh, Equals, nil)
796 c.Check(s.sess.ShouldDelay(), Equals, false)
797 c.Assert(s.sess.NotificationsCh, HasLen, 1)
798 app2, err := click.ParseAppId("com.example.app2_app2")
799@@ -926,10 +945,10 @@
800 msg.NotificationsMsg = protocol.NotificationsMsg{
801 Notifications: []protocol.Notification{n1, n2},
802 }
803- go func() { s.errCh <- s.sess.handleNotifications(msg) }()
804+ go func() { s.sess.errCh <- s.sess.handleNotifications(msg) }()
805 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"})
806 s.upCh <- nil // ack ok
807- c.Check(<-s.errCh, Equals, nil)
808+ c.Check(<-s.sess.errCh, Equals, nil)
809 c.Assert(s.sess.NotificationsCh, HasLen, 2)
810 app1, err := click.ParseAppId("com.example.app1_app1")
811 c.Assert(err, IsNil)
812@@ -946,10 +965,10 @@
813 c.Check(ac.ops, HasLen, 3)
814
815 // second time they get ignored
816- go func() { s.errCh <- s.sess.handleNotifications(msg) }()
817+ go func() { s.sess.errCh <- s.sess.handleNotifications(msg) }()
818 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"})
819 s.upCh <- nil // ack ok
820- c.Check(<-s.errCh, Equals, nil)
821+ c.Check(<-s.sess.errCh, Equals, nil)
822 c.Assert(s.sess.NotificationsCh, HasLen, 0)
823 c.Check(ac.ops, HasLen, 4)
824 }
825@@ -966,11 +985,11 @@
826 msg.NotificationsMsg = protocol.NotificationsMsg{
827 Notifications: []protocol.Notification{n1},
828 }
829- go func() { s.errCh <- s.sess.handleNotifications(msg) }()
830+ go func() { s.sess.errCh <- s.sess.handleNotifications(msg) }()
831 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"})
832 failure := errors.New("ACK ACK ACK")
833 s.upCh <- failure
834- c.Assert(<-s.errCh, Equals, failure)
835+ c.Assert(<-s.sess.errCh, Equals, failure)
836 c.Check(s.sess.State(), Equals, Error)
837 // didn't get to clear
838 c.Check(s.sess.ShouldDelay(), Equals, true)
839@@ -989,10 +1008,10 @@
840 msg.NotificationsMsg = protocol.NotificationsMsg{
841 Notifications: []protocol.Notification{n1},
842 }
843- go func() { s.errCh <- s.sess.handleNotifications(msg) }()
844+ go func() { s.sess.errCh <- s.sess.handleNotifications(msg) }()
845 s.upCh <- nil // ack ok
846 // start returns with error
847- c.Check(<-s.errCh, Not(Equals), nil)
848+ c.Check(<-s.sess.errCh, Not(Equals), nil)
849 c.Check(s.sess.State(), Equals, Error)
850 // no message sent out
851 c.Check(len(s.sess.NotificationsCh), Equals, 0)
852@@ -1013,8 +1032,8 @@
853 msg.ConnBrokenMsg = protocol.ConnBrokenMsg{
854 Reason: "REASON",
855 }
856- go func() { s.errCh <- s.sess.handleConnBroken(msg) }()
857- c.Check(<-s.errCh, ErrorMatches, "server broke connection: REASON")
858+ go func() { s.sess.errCh <- s.sess.handleConnBroken(msg) }()
859+ c.Check(<-s.sess.errCh, ErrorMatches, "server broke connection: REASON")
860 c.Check(s.sess.State(), Equals, Error)
861 }
862
863@@ -1025,8 +1044,8 @@
864 Reason: protocol.BrokenHostMismatch,
865 }
866 s.sess.deliveryHosts = []string{"foo:443", "bar:443"}
867- go func() { s.errCh <- s.sess.handleConnBroken(msg) }()
868- c.Check(<-s.errCh, ErrorMatches, "server broke connection: host-mismatch")
869+ go func() { s.sess.errCh <- s.sess.handleConnBroken(msg) }()
870+ c.Check(<-s.sess.errCh, ErrorMatches, "server broke connection: host-mismatch")
871 c.Check(s.sess.State(), Equals, Error)
872 // hosts were reset
873 c.Check(s.sess.deliveryHosts, IsNil)
874@@ -1044,14 +1063,14 @@
875 (*msgSuite)(s).SetUpTest(c)
876 s.sess.Connection.(*testConn).Name = "TestLoop*"
877 go func() {
878- s.errCh <- s.sess.loop()
879+ s.sess.errCh <- s.sess.loop()
880 }()
881 }
882
883 func (s *loopSuite) TestLoopReadError(c *C) {
884 c.Check(s.sess.State(), Equals, Running)
885 s.upCh <- errors.New("Read")
886- err := <-s.errCh
887+ err := <-s.sess.errCh
888 c.Check(err, ErrorMatches, "Read")
889 c.Check(s.sess.State(), Equals, Error)
890 }
891@@ -1063,7 +1082,7 @@
892 c.Check(takeNext(s.downCh), Equals, protocol.PingPongMsg{Type: "pong"})
893 failure := errors.New("pong")
894 s.upCh <- failure
895- c.Check(<-s.errCh, Equals, failure)
896+ c.Check(<-s.sess.errCh, Equals, failure)
897 }
898
899 func (s *loopSuite) TestLoopLoopsDaLoop(c *C) {
900@@ -1076,7 +1095,7 @@
901 }
902 failure := errors.New("pong")
903 s.upCh <- failure
904- c.Check(<-s.errCh, Equals, failure)
905+ c.Check(<-s.sess.errCh, Equals, failure)
906 }
907
908 func (s *loopSuite) TestLoopBroadcast(c *C) {
909@@ -1093,7 +1112,7 @@
910 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"})
911 failure := errors.New("ack")
912 s.upCh <- failure
913- c.Check(<-s.errCh, Equals, failure)
914+ c.Check(<-s.sess.errCh, Equals, failure)
915 }
916
917 func (s *loopSuite) TestLoopNotifications(c *C) {
918@@ -1113,7 +1132,7 @@
919 c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"})
920 failure := errors.New("ack")
921 s.upCh <- failure
922- c.Check(<-s.errCh, Equals, failure)
923+ c.Check(<-s.sess.errCh, Equals, failure)
924 }
925
926 func (s *loopSuite) TestLoopSetParams(c *C) {
927@@ -1126,7 +1145,7 @@
928 s.upCh <- setParams
929 failure := errors.New("fail")
930 s.upCh <- failure
931- c.Assert(<-s.errCh, Equals, failure)
932+ c.Assert(<-s.sess.errCh, Equals, failure)
933 c.Check(s.sess.getCookie(), Equals, "COOKIE")
934 }
935
936@@ -1138,7 +1157,7 @@
937 }
938 c.Check(takeNext(s.downCh), Equals, "deadline 1ms")
939 s.upCh <- broken
940- c.Check(<-s.errCh, NotNil)
941+ c.Check(<-s.sess.errCh, NotNil)
942 }
943
944 func (s *loopSuite) TestLoopConnWarn(c *C) {
945@@ -1159,7 +1178,7 @@
946 s.upCh <- warn
947 s.upCh <- connwarn
948 s.upCh <- failure
949- c.Check(<-s.errCh, Equals, failure)
950+ c.Check(<-s.sess.errCh, Equals, failure)
951 c.Check(log.Captured(),
952 Matches, `(?ms).* warning: XXX$.*`)
953 c.Check(log.Captured(),
954@@ -1426,12 +1445,12 @@
955 func() error { sess.BroadcastCh <- notf; return <-failureCh })
956 c.Check(err, Equals, nil)
957 // if run doesn't error it sets up the channels
958- c.Assert(sess.ErrCh, NotNil)
959+ c.Assert(sess.errCh, NotNil)
960 c.Assert(sess.BroadcastCh, NotNil)
961 c.Check(<-sess.BroadcastCh, Equals, notf)
962 failure := errors.New("TestRunRunsEvenIfLoopFails")
963 failureCh <- failure
964- c.Check(<-sess.ErrCh, Equals, failure)
965+ c.Check(<-sess.errCh, Equals, failure)
966 // so now you know it was running in a goroutine :)
967 }
968
969@@ -1632,7 +1651,7 @@
970 c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"})
971 failure := errors.New("pongs")
972 upCh <- failure
973- c.Check(<-sess.ErrCh, Equals, failure)
974+ c.Check(<-sess.errCh, Equals, failure)
975 }
976
977 func (cs *clientSessionSuite) TestDialWorksDirect(c *C) {
978@@ -1703,3 +1722,191 @@
979 sess.ClearCookie()
980 c.Check(sess.getCookie(), Equals, "")
981 }
982+
983+/****************************************************************
984+ KeepConnection() (and related) tests
985+****************************************************************/
986+
987+func (cs *clientSessionSuite) TestKeepConnectionDoesNothingIfNotConnected(c *C) {
988+ // how do you test "does nothing?"
989+ sess, err := NewSession("foo:443", dummyConf(), "", cs.lvls, cs.log)
990+ c.Assert(err, IsNil)
991+ c.Assert(sess, NotNil)
992+ c.Assert(sess.State(), Equals, Pristine)
993+ c.Assert(sess.KeepConnection(), IsNil)
994+ // stopCh is meant to be used just for closing it, but abusing
995+ // it for testing seems the right thing to do: this ensures
996+ // the thing is ticking along before we check the state of
997+ // stuff.
998+ sess.stopCh <- struct{}{}
999+ c.Check(sess.State(), Equals, Disconnected)
1000+}
1001+
1002+func (cs *clientSessionSuite) TestYouCantCallKeepConnectionTwice(c *C) {
1003+ sess, err := NewSession("foo:443", dummyConf(), "", cs.lvls, cs.log)
1004+ c.Assert(err, IsNil)
1005+ c.Assert(sess, NotNil)
1006+ c.Assert(sess.State(), Equals, Pristine)
1007+ c.Assert(sess.KeepConnection(), IsNil)
1008+ defer sess.StopKeepConnection()
1009+ c.Check(sess.KeepConnection(), NotNil)
1010+}
1011+
1012+func (cs *clientSessionSuite) TestStopKeepConnectionShutsdown(c *C) {
1013+ sess, err := NewSession("foo:443", dummyConf(), "", cs.lvls, cs.log)
1014+ c.Assert(err, IsNil)
1015+ c.Assert(sess, NotNil)
1016+ sess.StopKeepConnection()
1017+ c.Check(sess.State(), Equals, Shutdown)
1018+}
1019+
1020+func (cs *clientSessionSuite) TestHasConnectivityTriggersConnectivityHandler(c *C) {
1021+ sess, err := NewSession("foo:443", dummyConf(), "", cs.lvls, cs.log)
1022+ c.Assert(err, IsNil)
1023+ c.Assert(sess, NotNil)
1024+ testCh := make(chan bool)
1025+ sess.connHandler = func(p bool) { testCh <- p }
1026+ go sess.doKeepConnection()
1027+ defer sess.StopKeepConnection()
1028+ sess.HasConnectivity(true)
1029+ c.Check(<-testCh, Equals, true)
1030+ sess.HasConnectivity(false)
1031+ c.Check(<-testCh, Equals, false)
1032+}
1033+
1034+func (cs *clientSessionSuite) TestDoneChIsEmptiedAndLogged(c *C) {
1035+ sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log)
1036+ c.Assert(err, IsNil)
1037+ sess.doneCh = make(chan uint32) // unbuffered
1038+
1039+ sess.KeepConnection()
1040+ defer sess.StopKeepConnection()
1041+
1042+ sess.doneCh <- 23
1043+
1044+ c.Check(cs.log.Captured(),
1045+ Matches, `(?ms).* connected after 23 attempts\.`)
1046+}
1047+
1048+func (cs *clientSessionSuite) TestErrChIsEmptiedAndLoggedAndAutoRedial(c *C) {
1049+ sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log)
1050+ c.Assert(err, IsNil)
1051+ ch := make(chan struct{}, 1)
1052+ sess.errCh = make(chan error) // unbuffered
1053+ sess.redialDelay = func(sess *clientSession) time.Duration { ch <- struct{}{}; return 0 }
1054+ sess.lastConn = true // -> autoRedial, if the session is in Disconnected
1055+
1056+ sess.KeepConnection()
1057+ defer sess.StopKeepConnection()
1058+
1059+ sess.errCh <- errors.New("potato")
1060+ c.Assert(sess.State(), Equals, Disconnected)
1061+ select {
1062+ case <-ch:
1063+ // all ok
1064+ case <-time.After(100 * time.Millisecond):
1065+ c.Fatalf("redialDelay not called (-> autoRedial not called)?")
1066+ }
1067+
1068+ c.Check(cs.log.Captured(),
1069+ Matches, `(?ms).* session error.*potato`)
1070+}
1071+
1072+func (cs *clientSessionSuite) TestErrChIsEmptiedAndLoggedNoAutoRedial(c *C) {
1073+ sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log)
1074+ c.Assert(err, IsNil)
1075+ ch := make(chan struct{}, 1)
1076+ sess.errCh = make(chan error) // unbuffered
1077+ sess.redialDelay = func(sess *clientSession) time.Duration { ch <- struct{}{}; return 0 }
1078+ sess.connHandler = func(bool) {}
1079+ sess.lastConn = false // so, no autoredial
1080+
1081+ sess.KeepConnection()
1082+ defer sess.StopKeepConnection()
1083+
1084+ sess.errCh <- errors.New("potato")
1085+ c.Assert(sess.State(), Equals, Disconnected)
1086+ select {
1087+ case <-ch:
1088+ c.Fatalf("redialDelay called (-> autoRedial called) when disconnected?")
1089+ case <-time.After(100 * time.Millisecond):
1090+ // all ok
1091+ }
1092+
1093+ c.Check(cs.log.Captured(),
1094+ Matches, `(?ms).* session error.*potato`)
1095+}
1096+
1097+func (cs *clientSessionSuite) TestHandleConnConnFromConnected(c *C) {
1098+ sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log)
1099+ c.Assert(err, IsNil)
1100+ ch := make(chan struct{}, 1)
1101+ sess.redialDelay = func(sess *clientSession) time.Duration { ch <- struct{}{}; return 0 }
1102+ sess.state = Connected
1103+ sess.lastConn = true
1104+ sess.handleConn(true)
1105+ c.Check(sess.lastConn, Equals, true)
1106+
1107+ select {
1108+ case <-ch:
1109+ // all ok
1110+ case <-time.After(100 * time.Millisecond):
1111+ c.Fatalf("redialDelay not called (-> autoRedial not called)?")
1112+ }
1113+}
1114+
1115+func (cs *clientSessionSuite) TestHandleConnConnFromDisconnected(c *C) {
1116+ sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log)
1117+ c.Assert(err, IsNil)
1118+ ch := make(chan struct{}, 1)
1119+ sess.redialDelay = func(sess *clientSession) time.Duration { ch <- struct{}{}; return 0 }
1120+ sess.state = Disconnected
1121+ sess.lastConn = false
1122+ sess.handleConn(true)
1123+ c.Check(sess.lastConn, Equals, true)
1124+
1125+ select {
1126+ case <-ch:
1127+ // all ok
1128+ case <-time.After(100 * time.Millisecond):
1129+ c.Fatalf("redialDelay not called (-> autoRedial not called)?")
1130+ }
1131+}
1132+
1133+func (cs *clientSessionSuite) TestHandleConnNotConnFromDisconnected(c *C) {
1134+ sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log)
1135+ c.Assert(err, IsNil)
1136+ ch := make(chan struct{}, 1)
1137+ sess.redialDelay = func(sess *clientSession) time.Duration { ch <- struct{}{}; return 0 }
1138+ sess.state = Disconnected
1139+ sess.lastConn = false
1140+ sess.handleConn(false)
1141+ c.Check(sess.lastConn, Equals, false)
1142+
1143+ select {
1144+ case <-ch:
1145+ c.Fatalf("redialDelay called (-> autoRedial called)?")
1146+ case <-time.After(100 * time.Millisecond):
1147+ // all ok
1148+ }
1149+ c.Check(cs.log.Captured(), Matches, `(?ms).*-> Disconnected`)
1150+}
1151+
1152+func (cs *clientSessionSuite) TestHandleConnNotConnFromConnected(c *C) {
1153+ sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log)
1154+ c.Assert(err, IsNil)
1155+ ch := make(chan struct{}, 1)
1156+ sess.redialDelay = func(sess *clientSession) time.Duration { ch <- struct{}{}; return 0 }
1157+ sess.state = Connected
1158+ sess.lastConn = true
1159+ sess.handleConn(false)
1160+ c.Check(sess.lastConn, Equals, false)
1161+
1162+ select {
1163+ case <-ch:
1164+ c.Fatalf("redialDelay called (-> autoRedial called)?")
1165+ case <-time.After(100 * time.Millisecond):
1166+ // all ok
1167+ }
1168+ c.Check(cs.log.Captured(), Matches, `(?ms).*-> Disconnected`)
1169+}

Subscribers

People subscribed via source and target branches