Merge lp:~chipaca/ubuntu-push/redialer into lp:ubuntu-push
- redialer
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | John Lenton |
Approved revision: | 31 |
Merged at revision: | 26 |
Proposed branch: | lp:~chipaca/ubuntu-push/redialer |
Merge into: | lp:ubuntu-push |
Prerequisite: | lp:~chipaca/ubuntu-push/endpoint-not-bus |
Diff against target: |
1152 lines (+393/-183) 16 files modified
bus/bus.go (+3/-8) bus/bus_test.go (+5/-20) bus/connectivity/connectivity.go (+12/-33) bus/connectivity/connectivity_test.go (+20/-41) bus/connectivity/webchecker_test.go (+15/-5) bus/endpoint.go (+34/-12) bus/endpoint_test.go (+27/-0) bus/networkmanager/networkmanager_test.go (+8/-8) bus/notifications/raw_test.go (+5/-5) bus/testing/testing_bus.go (+7/-13) bus/testing/testing_bus_test.go (+13/-19) bus/testing/testing_endpoint.go (+29/-8) bus/testing/testing_endpoint_test.go (+43/-9) bus/urldispatcher/urldispatcher_test.go (+2/-2) util/redialer.go (+86/-0) util/redialer_test.go (+84/-0) |
To merge this branch: | bzr merge lp:~chipaca/ubuntu-push/redialer |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Samuele Pedroni | Approve | ||
Review via email: mp+202870@code.launchpad.net |
Commit message
reworked bus.Endpoint to have a Dial() method, added an AutoRedialer() and put the redialing logic in there (for use on sessionbus as well, later).
Description of the change
Reworked bus.Endpoint to have a Dial() method, added an AutoRedialer() and put the redialing logic in there (for use on sessionbus as well, later).
- 24. By John Lenton
-
be less silly for tests
Samuele Pedroni (pedronis) wrote : | # |
John Lenton (chipaca) wrote : | # |
Agreed about the jitter on both counts; will come back to it and refactor it a tad once it's been used so I don't miss anything.
Fixing the double-invoking, and the test slowness.
Samuele Pedroni (pedronis) wrote : | # |
406 + endp.Close()
maybe there should be a defer endp.Close around as well
- 25. By John Lenton
-
made timeouts public, and stomp on them in the tests
John Lenton (chipaca) wrote : | # |
defer endp.Close around where? Sorry if I'm being obtuse...
- 26. By John Lenton
-
merged trunk
Samuele Pedroni (pedronis) wrote : | # |
so here is a commented version of what I was saying: https:/
- 27. By John Lenton
-
made the jitter something provided by the dialer itself
- 28. By John Lenton
-
Merged endpoint-not-bus into redialer.
- 29. By John Lenton
-
merged trunk
- 30. By John Lenton
-
make Jitter take the base duration as argument
- 31. By John Lenton
-
made the docs a bit better
Samuele Pedroni (pedronis) : | # |
Preview Diff
1 | === modified file 'bus/bus.go' |
2 | --- bus/bus.go 2014-01-20 19:02:17 +0000 |
3 | +++ bus/bus.go 2014-01-27 13:02:37 +0000 |
4 | @@ -31,7 +31,7 @@ |
5 | // This is the Bus itself. |
6 | type Bus interface { |
7 | String() string |
8 | - Connect(Address, logger.Logger) (Endpoint, error) |
9 | + Endpoint(Address, logger.Logger) Endpoint |
10 | } |
11 | |
12 | type concreteBus dbus.StandardBus |
13 | @@ -58,13 +58,8 @@ |
14 | } |
15 | |
16 | // Connect() connects to the bus, and returns the bus endpoint (and/or error). |
17 | -func (bus concreteBus) Connect(addr Address, log logger.Logger) (Endpoint, error) { |
18 | - conn, err := dbus.Connect(bus.dbusType()) |
19 | - if err != nil { |
20 | - return nil, err |
21 | - } else { |
22 | - return newEndpoint(conn, addr, log), nil |
23 | - } |
24 | +func (bus concreteBus) Endpoint(addr Address, log logger.Logger) Endpoint { |
25 | + return newEndpoint(bus, addr, log) |
26 | } |
27 | |
28 | /* |
29 | |
30 | === modified file 'bus/bus_test.go' |
31 | --- bus/bus_test.go 2014-01-20 12:36:21 +0000 |
32 | +++ bus/bus_test.go 2014-01-27 13:02:37 +0000 |
33 | @@ -22,12 +22,11 @@ |
34 | "launchpad.net/go-dbus/v1" |
35 | . "launchpad.net/gocheck" |
36 | "launchpad.net/ubuntu-push/logger" |
37 | - "os" |
38 | "testing" |
39 | ) |
40 | |
41 | // hook up gocheck |
42 | -func Test(t *testing.T) { TestingT(t) } |
43 | +func BusTest(t *testing.T) { TestingT(t) } |
44 | |
45 | type BusSuite struct{} |
46 | |
47 | @@ -46,22 +45,8 @@ |
48 | c.Check(SessionBus.(concreteBus).dbusType(), DeepEquals, dbus.SessionBus) |
49 | } |
50 | |
51 | -// Tests that we can connect to the *actual* system bus. |
52 | -// XXX maybe connect to a mock/fake/etc bus? |
53 | -func (s *BusSuite) TestConnect(c *C) { |
54 | - b, err := SystemBus.Connect(Address{"", "", ""}, nullog) |
55 | - c.Assert(err, IsNil) |
56 | - defer b.Close() |
57 | -} |
58 | - |
59 | -// Test that if we try to connect to the session bus when no session |
60 | -// bus is available, we get a reasonable result (i.e., an error). |
61 | -func (s *BusSuite) TestConnectCanFail(c *C) { |
62 | - db := "DBUS_SESSION_BUS_ADDRESS" |
63 | - odb := os.Getenv(db) |
64 | - defer os.Setenv(db, odb) |
65 | - os.Setenv(db, "") |
66 | - |
67 | - _, err := SessionBus.Connect(Address{"", "", ""}, nullog) |
68 | - c.Check(err, NotNil) |
69 | +// Tests that we can get an endpoint back |
70 | +func (s *BusSuite) TestEndpoint(c *C) { |
71 | + endp := SystemBus.Endpoint(Address{"", "", ""}, nullog) |
72 | + c.Assert(endp, NotNil) |
73 | } |
74 | |
75 | === modified file 'bus/connectivity/connectivity.go' |
76 | --- bus/connectivity/connectivity.go 2014-01-27 13:02:37 +0000 |
77 | +++ bus/connectivity/connectivity.go 2014-01-27 13:02:37 +0000 |
78 | @@ -28,14 +28,13 @@ |
79 | "launchpad.net/ubuntu-push/bus/networkmanager" |
80 | "launchpad.net/ubuntu-push/config" |
81 | "launchpad.net/ubuntu-push/logger" |
82 | + "launchpad.net/ubuntu-push/util" |
83 | "time" |
84 | ) |
85 | |
86 | // the configuration for ConnectedState, with the idea that you'd populate it |
87 | // from a config file. |
88 | type Config struct { |
89 | - // a list of timeouts, for backoff. Should be roughly doubling. |
90 | - ConnectTimeouts []config.ConfigTimeDuration |
91 | // how long to wait after a state change to make sure it's "stable" |
92 | // before acting on it |
93 | StabilizingTimeout config.ConfigTimeDuration |
94 | @@ -51,7 +50,7 @@ |
95 | networkStateCh <-chan networkmanager.State |
96 | config Config |
97 | log logger.Logger |
98 | - bus bus.Bus |
99 | + endp bus.Endpoint |
100 | connAttempts uint32 |
101 | webget func(ch chan<- bool) |
102 | webgetCh chan bool |
103 | @@ -60,41 +59,19 @@ |
104 | timer *time.Timer |
105 | } |
106 | |
107 | -// implements the logic for connect timeouts backoff |
108 | -// |
109 | -// (walk the list of timeouts, and repeat the last one until done; cope with |
110 | -// the list being empty; keep track of connection attempts). |
111 | -func (cs *connectedState) connectTimeout() time.Duration { |
112 | - var timeout config.ConfigTimeDuration |
113 | - timeouts := cs.config.ConnectTimeouts |
114 | - if cs.connAttempts < uint32(len(timeouts)) { |
115 | - timeout = timeouts[cs.connAttempts] |
116 | - } else if len(timeouts) > 0 { |
117 | - timeout = cs.config.ConnectTimeouts[len(timeouts)-1] |
118 | - } |
119 | - cs.connAttempts++ |
120 | - return timeout.Duration |
121 | -} |
122 | - |
123 | // start connects to the bus, gets the initial NetworkManager state, and sets |
124 | // up the watch. |
125 | func (cs *connectedState) start() networkmanager.State { |
126 | var initial networkmanager.State |
127 | for { |
128 | - time.Sleep(cs.connectTimeout()) |
129 | - cs.log.Debugf("Starting DBus connection attempt %d\n", cs.connAttempts) |
130 | - conn, err := cs.bus.Connect(networkmanager.BusAddress, cs.log) |
131 | - if err != nil { |
132 | - cs.log.Debugf("DBus connection attempt %d failed.\n", cs.connAttempts) |
133 | - continue |
134 | - } |
135 | - nm := networkmanager.New(conn, cs.log) |
136 | + cs.connAttempts += util.AutoRedial(cs.endp) |
137 | + nm := networkmanager.New(cs.endp, cs.log) |
138 | |
139 | // Get the current state. |
140 | initial = nm.GetState() |
141 | if initial == networkmanager.Unknown { |
142 | - cs.log.Debugf("Failed to get state at attempt.") |
143 | - conn.Close() |
144 | + cs.log.Debugf("Failed to get state.") |
145 | + cs.endp.Close() |
146 | continue |
147 | } |
148 | |
149 | @@ -102,12 +79,11 @@ |
150 | ch, err := nm.WatchState() |
151 | if err != nil { |
152 | cs.log.Debugf("Failed to set up the watch: %s", err) |
153 | - conn.Close() |
154 | + cs.endp.Close() |
155 | continue |
156 | } |
157 | |
158 | cs.networkStateCh = ch |
159 | - cs.log.Debugf("worked at attempt %d. Resetting counter.\n", cs.connAttempts) |
160 | return initial |
161 | } |
162 | } |
163 | @@ -161,12 +137,15 @@ |
164 | // ConnectedState sends the initial NetworkManager state and changes to it |
165 | // over the "out" channel. Sends "false" as soon as it detects trouble, "true" |
166 | // after checking actual connectivity. |
167 | -func ConnectedState(busType bus.Bus, config Config, log logger.Logger, out chan<- bool) { |
168 | +// |
169 | +// The endpoint need not be dialed; connectivity will Dial() and Close() |
170 | +// it as it sees fit. |
171 | +func ConnectedState(endp bus.Endpoint, config Config, log logger.Logger, out chan<- bool) { |
172 | wg := NewWebchecker(config.ConnectivityCheckURL, config.ConnectivityCheckMD5, log) |
173 | cs := &connectedState{ |
174 | config: config, |
175 | log: log, |
176 | - bus: busType, |
177 | + endp: endp, |
178 | webget: wg.Webcheck, |
179 | } |
180 | |
181 | |
182 | === modified file 'bus/connectivity/connectivity_test.go' |
183 | --- bus/connectivity/connectivity_test.go 2014-01-27 13:02:37 +0000 |
184 | +++ bus/connectivity/connectivity_test.go 2014-01-27 13:02:37 +0000 |
185 | @@ -24,6 +24,7 @@ |
186 | "launchpad.net/ubuntu-push/config" |
187 | "launchpad.net/ubuntu-push/logger" |
188 | "launchpad.net/ubuntu-push/testing/condition" |
189 | + "launchpad.net/ubuntu-push/util" |
190 | "net/http/httptest" |
191 | "testing" |
192 | "time" |
193 | @@ -32,39 +33,22 @@ |
194 | // hook up gocheck |
195 | func Test(t *testing.T) { TestingT(t) } |
196 | |
197 | -type ConnSuite struct{} |
198 | +type ConnSuite struct { |
199 | + timeouts []time.Duration |
200 | +} |
201 | |
202 | var _ = Suite(&ConnSuite{}) |
203 | |
204 | var nullog = logger.NewSimpleLogger(ioutil.Discard, "error") |
205 | |
206 | -/* |
207 | - tests for connectedState's ConnectTimeout() method |
208 | -*/ |
209 | - |
210 | -// When given no timeouts, ConnectTimeout() returns 0 forever |
211 | -func (s *ConnSuite) TestConnectTimeoutWorksWithNoTimeouts(c *C) { |
212 | - cs := connectedState{} |
213 | - c.Check(cs.connectTimeout(), Equals, time.Duration(0)) |
214 | - c.Check(cs.connectTimeout(), Equals, time.Duration(0)) |
215 | +func (s *ConnSuite) SetUpSuite(c *C) { |
216 | + s.timeouts = util.Timeouts |
217 | + util.Timeouts = []time.Duration{0, 0, 0, 0} |
218 | } |
219 | |
220 | -// when given a few timeouts, ConnectTimeout() returns them each in |
221 | -// turn, and then repeats the last one |
222 | -func (s *ConnSuite) TestConnectTimeoutWorks(c *C) { |
223 | - ts := []config.ConfigTimeDuration{ |
224 | - config.ConfigTimeDuration{0}, |
225 | - config.ConfigTimeDuration{2 * time.Second}, |
226 | - config.ConfigTimeDuration{time.Second}, |
227 | - } |
228 | - cs := connectedState{config: Config{ConnectTimeouts: ts}} |
229 | - c.Check(cs.connectTimeout(), Equals, time.Duration(0)) |
230 | - c.Check(cs.connectTimeout(), Equals, 2*time.Second) |
231 | - c.Check(cs.connectTimeout(), Equals, time.Second) |
232 | - c.Check(cs.connectTimeout(), Equals, time.Second) |
233 | - c.Check(cs.connectTimeout(), Equals, time.Second) |
234 | - c.Check(cs.connectTimeout(), Equals, time.Second) |
235 | - // ... ad nauseam |
236 | +func (s *ConnSuite) TearDownSuite(c *C) { |
237 | + util.Timeouts = s.timeouts |
238 | + s.timeouts = nil |
239 | } |
240 | |
241 | /* |
242 | @@ -73,19 +57,16 @@ |
243 | |
244 | // when given a working config and bus, Start() will work |
245 | func (s *ConnSuite) TestStartWorks(c *C) { |
246 | - cfg := Config{} |
247 | - tb := testingbus.NewTestingBus(condition.Work(true), condition.Work(true), uint32(networkmanager.Connecting)) |
248 | - cs := connectedState{config: cfg, log: nullog, bus: tb} |
249 | + endp := testingbus.NewTestingEndpoint(condition.Work(true), condition.Work(true), uint32(networkmanager.Connecting)) |
250 | + cs := connectedState{config: Config{}, log: nullog, endp: endp} |
251 | |
252 | c.Check(cs.start(), Equals, networkmanager.Connecting) |
253 | } |
254 | |
255 | // if the bus fails a couple of times, we're still OK |
256 | func (s *ConnSuite) TestStartRetriesConnect(c *C) { |
257 | - timeouts := []config.ConfigTimeDuration{config.ConfigTimeDuration{0}} |
258 | - cfg := Config{ConnectTimeouts: timeouts} |
259 | - tb := testingbus.NewTestingBus(condition.Fail2Work(2), condition.Work(true), uint32(networkmanager.Connecting)) |
260 | - cs := connectedState{config: cfg, log: nullog, bus: tb} |
261 | + endp := testingbus.NewTestingEndpoint(condition.Fail2Work(2), condition.Work(true), uint32(networkmanager.Connecting)) |
262 | + cs := connectedState{config: Config{}, log: nullog, endp: endp} |
263 | |
264 | c.Check(cs.start(), Equals, networkmanager.Connecting) |
265 | c.Check(cs.connAttempts, Equals, uint32(3)) // 1 more than the Fail2Work |
266 | @@ -93,9 +74,8 @@ |
267 | |
268 | // when the calls to NetworkManager fail for a bit, we're still OK |
269 | func (s *ConnSuite) TestStartRetriesCall(c *C) { |
270 | - cfg := Config{} |
271 | - tb := testingbus.NewTestingBus(condition.Work(true), condition.Fail2Work(5), uint32(networkmanager.Connecting)) |
272 | - cs := connectedState{config: cfg, log: nullog, bus: tb} |
273 | + endp := testingbus.NewTestingEndpoint(condition.Work(true), condition.Fail2Work(5), uint32(networkmanager.Connecting)) |
274 | + cs := connectedState{config: Config{}, log: nullog, endp: endp} |
275 | |
276 | c.Check(cs.start(), Equals, networkmanager.Connecting) |
277 | |
278 | @@ -110,11 +90,10 @@ |
279 | 1, condition.Work(true), // 1 call to nm works |
280 | 1, condition.Work(false), // 1 call to nm fails |
281 | 0, condition.Work(true)) // and everything works from there on |
282 | - cfg := Config{} |
283 | - tb := testingbus.NewTestingBus(condition.Work(true), nmcond, |
284 | + endp := testingbus.NewTestingEndpoint(condition.Work(true), nmcond, |
285 | uint32(networkmanager.Connecting), |
286 | uint32(networkmanager.ConnectedGlobal)) |
287 | - cs := connectedState{config: cfg, log: nullog, bus: tb} |
288 | + cs := connectedState{config: Config{}, log: nullog, endp: endp} |
289 | |
290 | c.Check(cs.start(), Equals, networkmanager.Connecting) |
291 | c.Check(cs.connAttempts, Equals, uint32(2)) |
292 | @@ -211,7 +190,7 @@ |
293 | RecheckTimeout: config.ConfigTimeDuration{time.Second}, |
294 | } |
295 | |
296 | - busType := testingbus.NewTestingBus(condition.Work(true), condition.Work(true), |
297 | + endp := testingbus.NewTestingEndpoint(condition.Work(true), condition.Work(true), |
298 | uint32(networkmanager.ConnectedGlobal), |
299 | uint32(networkmanager.ConnectedGlobal), |
300 | uint32(networkmanager.Disconnected), |
301 | @@ -220,7 +199,7 @@ |
302 | out := make(chan bool) |
303 | dt := time.Second / 10 |
304 | timer := time.NewTimer(dt) |
305 | - go ConnectedState(busType, cfg, nullog, out) |
306 | + go ConnectedState(endp, cfg, nullog, out) |
307 | var v bool |
308 | expecteds := []bool{ |
309 | false, // first state is always false |
310 | |
311 | === modified file 'bus/connectivity/webchecker_test.go' |
312 | --- bus/connectivity/webchecker_test.go 2014-01-27 13:02:37 +0000 |
313 | +++ bus/connectivity/webchecker_test.go 2014-01-27 13:02:37 +0000 |
314 | @@ -18,15 +18,15 @@ |
315 | |
316 | import ( |
317 | . "launchpad.net/gocheck" |
318 | + "launchpad.net/ubuntu-push/util" |
319 | "net/http" |
320 | "net/http/httptest" |
321 | - "testing" |
322 | + "time" |
323 | ) |
324 | |
325 | -// hook up gocheck |
326 | -func TestWebcheck(t *testing.T) { TestingT(t) } |
327 | - |
328 | -type WebcheckerSuite struct{} |
329 | +type WebcheckerSuite struct { |
330 | + timeouts []time.Duration |
331 | +} |
332 | |
333 | var _ = Suite(&WebcheckerSuite{}) |
334 | |
335 | @@ -60,6 +60,16 @@ |
336 | } |
337 | } |
338 | |
339 | +func (s *WebcheckerSuite) SetUpSuite(c *C) { |
340 | + s.timeouts = util.Timeouts |
341 | + util.Timeouts = []time.Duration{0} |
342 | +} |
343 | + |
344 | +func (s *WebcheckerSuite) TearDownSuite(c *C) { |
345 | + util.Timeouts = s.timeouts |
346 | + s.timeouts = nil |
347 | +} |
348 | + |
349 | // Webchecker sends true when everything works |
350 | func (s *WebcheckerSuite) TestWorks(c *C) { |
351 | ts := httptest.NewServer(mkHandler(staticText)) |
352 | |
353 | === modified file 'bus/endpoint.go' |
354 | --- bus/endpoint.go 2014-01-23 00:21:38 +0000 |
355 | +++ bus/endpoint.go 2014-01-27 13:02:37 +0000 |
356 | @@ -22,6 +22,7 @@ |
357 | "fmt" |
358 | "launchpad.net/go-dbus/v1" |
359 | "launchpad.net/ubuntu-push/logger" |
360 | + "time" |
361 | ) |
362 | |
363 | /***************************************************************** |
364 | @@ -33,25 +34,23 @@ |
365 | WatchSignal(member string, f func(...interface{}), d func()) error |
366 | Call(member string, args ...interface{}) ([]interface{}, error) |
367 | GetProperty(property string) (interface{}, error) |
368 | + Dial() error |
369 | Close() |
370 | + String() string |
371 | + Jitter(time.Duration) time.Duration |
372 | } |
373 | |
374 | type endpoint struct { |
375 | + busT Bus |
376 | bus *dbus.Connection |
377 | proxy *dbus.ObjectProxy |
378 | - iface string |
379 | + addr Address |
380 | log logger.Logger |
381 | } |
382 | |
383 | // constructor |
384 | -func newEndpoint(bus *dbus.Connection, addr Address, log logger.Logger) *endpoint { |
385 | - endp := new(endpoint) |
386 | - endp.bus = bus |
387 | - endp.proxy = bus.Object(addr.Name, dbus.ObjectPath(addr.Path)) |
388 | - endp.iface = addr.Interface |
389 | - endp.log = log |
390 | - |
391 | - return endp |
392 | +func newEndpoint(bus Bus, addr Address, log logger.Logger) *endpoint { |
393 | + return &endpoint{busT: bus, addr: addr, log: log} |
394 | } |
395 | |
396 | // ensure endpoint implements Endpoint |
397 | @@ -61,13 +60,24 @@ |
398 | public methods |
399 | */ |
400 | |
401 | +// Dial() (re)establishes the connection with dbus |
402 | +func (endp *endpoint) Dial() error { |
403 | + bus, err := dbus.Connect(endp.busT.(concreteBus).dbusType()) |
404 | + if err != nil { |
405 | + return err |
406 | + } |
407 | + endp.bus = bus |
408 | + endp.proxy = bus.Object(endp.addr.Name, dbus.ObjectPath(endp.addr.Path)) |
409 | + return nil |
410 | +} |
411 | + |
412 | // WatchSignal() takes a member name and sets up a watch for it (on the name, |
413 | // path and interface provided when creating the endpoint), and then calls f() |
414 | // with the unpacked value. If it's unable to set up the watch it'll return an |
415 | // error. If the watch fails once established, d() is called. Typically f() |
416 | // sends the values over a channel, and d() would close the channel. |
417 | func (endp *endpoint) WatchSignal(member string, f func(...interface{}), d func()) error { |
418 | - watch, err := endp.proxy.WatchSignal(endp.iface, member) |
419 | + watch, err := endp.proxy.WatchSignal(endp.addr.Interface, member) |
420 | if err != nil { |
421 | endp.log.Debugf("Failed to set up the watch: %s", err) |
422 | return err |
423 | @@ -82,7 +92,7 @@ |
424 | // provided when creating the endpoint). The return value is unpacked before |
425 | // being returned. |
426 | func (endp *endpoint) Call(member string, args ...interface{}) ([]interface{}, error) { |
427 | - msg, err := endp.proxy.Call(endp.iface, member, args...) |
428 | + msg, err := endp.proxy.Call(endp.addr.Interface, member, args...) |
429 | if err != nil { |
430 | return nil, err |
431 | } |
432 | @@ -95,7 +105,7 @@ |
433 | // creating the endpoint. The return value is unpacked into a dbus.Variant, |
434 | // and its value returned. |
435 | func (endp *endpoint) GetProperty(property string) (interface{}, error) { |
436 | - msg, err := endp.proxy.Call("org.freedesktop.DBus.Properties", "Get", endp.iface, property) |
437 | + msg, err := endp.proxy.Call("org.freedesktop.DBus.Properties", "Get", endp.addr.Interface, property) |
438 | if err != nil { |
439 | return nil, err |
440 | } |
441 | @@ -118,6 +128,18 @@ |
442 | // Close the connection to dbus. |
443 | func (endp *endpoint) Close() { |
444 | endp.bus.Close() |
445 | + endp.bus = nil |
446 | + endp.proxy = nil |
447 | +} |
448 | + |
449 | +// String() performs advanced endpoint stringification |
450 | +func (endp *endpoint) String() string { |
451 | + return fmt.Sprintf("<Connection to %s %#v>", endp.bus, endp.addr) |
452 | +} |
453 | + |
454 | +// Jitter() returns 0: no need to jitter D-Bus connections. |
455 | +func (endp *endpoint) Jitter(_ time.Duration) time.Duration { |
456 | + return 0 |
457 | } |
458 | |
459 | /* |
460 | |
461 | === modified file 'bus/endpoint_test.go' |
462 | --- bus/endpoint_test.go 2014-01-20 13:45:30 +0000 |
463 | +++ bus/endpoint_test.go 2014-01-27 13:02:37 +0000 |
464 | @@ -18,6 +18,7 @@ |
465 | |
466 | import ( |
467 | . "launchpad.net/gocheck" |
468 | + "os" |
469 | "testing" |
470 | ) |
471 | |
472 | @@ -30,3 +31,29 @@ |
473 | |
474 | // TODO: this is going to remain empty until go-dbus grows some |
475 | // testing amenities (already talked about it with jamesh) |
476 | + |
477 | +// Tests that we can connect to the *actual* system bus. |
478 | +// XXX maybe connect to a mock/fake/etc bus? |
479 | +func (s *BusSuite) TestDial(c *C) { |
480 | + endp := newEndpoint(SystemBus, Address{"", "", ""}, nullog) |
481 | + c.Assert(endp.bus, IsNil) |
482 | + err := endp.Dial() |
483 | + c.Assert(err, IsNil) |
484 | + defer endp.Close() // yes, a second close. On purpose. |
485 | + c.Assert(endp.bus, NotNil) |
486 | + endp.Close() // the first close. If you're counting right. |
487 | + c.Assert(endp.bus, IsNil) // Close cleans up |
488 | +} |
489 | + |
490 | +// Test that if we try to connect to the session bus when no session |
491 | +// bus is available, we get a reasonable result (i.e., an error). |
492 | +func (s *BusSuite) TestDialCanFail(c *C) { |
493 | + db := "DBUS_SESSION_BUS_ADDRESS" |
494 | + odb := os.Getenv(db) |
495 | + defer os.Setenv(db, odb) |
496 | + os.Setenv(db, "") |
497 | + |
498 | + endp := newEndpoint(SessionBus, Address{"", "", ""}, nullog) |
499 | + err := endp.Dial() |
500 | + c.Check(err, NotNil) |
501 | +} |
502 | |
503 | === modified file 'bus/networkmanager/networkmanager_test.go' |
504 | --- bus/networkmanager/networkmanager_test.go 2014-01-23 00:21:38 +0000 |
505 | +++ bus/networkmanager/networkmanager_test.go 2014-01-27 13:02:37 +0000 |
506 | @@ -47,41 +47,41 @@ |
507 | |
508 | // TestNew doesn't test much at all. If this fails, all is wrong in the world. |
509 | func (s *NMSuite) TestNew(c *C) { |
510 | - nm := New(testingbus.NewTestingEndpoint(condition.Work(true)), nullog) |
511 | + nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(true)), nullog) |
512 | c.Check(nm, NotNil) |
513 | } |
514 | |
515 | // GetState returns the right state when everything works |
516 | func (s *NMSuite) TestGetState(c *C) { |
517 | - nm := New(testingbus.NewTestingEndpoint(condition.Work(true), uint32(ConnectedGlobal)), nullog) |
518 | + nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(true), uint32(ConnectedGlobal)), nullog) |
519 | state := nm.GetState() |
520 | c.Check(state, Equals, ConnectedGlobal) |
521 | } |
522 | |
523 | // GetState returns the right state when dbus fails |
524 | func (s *NMSuite) TestGetStateFail(c *C) { |
525 | - nm := New(testingbus.NewTestingEndpoint(condition.Work(false), uint32(ConnectedGlobal)), nullog) |
526 | + nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(false), uint32(ConnectedGlobal)), nullog) |
527 | state := nm.GetState() |
528 | c.Check(state, Equals, Unknown) |
529 | } |
530 | |
531 | // GetState returns the right state when dbus works but delivers rubbish values |
532 | func (s *NMSuite) TestGetStateRubbishValues(c *C) { |
533 | - nm := New(testingbus.NewTestingEndpoint(condition.Work(false), 42), nullog) |
534 | + nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(false), 42), nullog) |
535 | state := nm.GetState() |
536 | c.Check(state, Equals, Unknown) |
537 | } |
538 | |
539 | // GetState returns the right state when dbus works but delivers a rubbish structure |
540 | func (s *NMSuite) TestGetStateRubbishStructure(c *C) { |
541 | - nm := New(testingbus.NewMultiValuedTestingEndpoint(condition.Work(true), []interface{}{}), nullog) |
542 | + nm := New(testingbus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{}), nullog) |
543 | state := nm.GetState() |
544 | c.Check(state, Equals, Unknown) |
545 | } |
546 | |
547 | // WatchState sends a stream of States over the channel |
548 | func (s *NMSuite) TestWatchState(c *C) { |
549 | - tc := testingbus.NewTestingEndpoint(condition.Work(true), uint32(Unknown), uint32(Asleep), uint32(ConnectedGlobal)) |
550 | + tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), uint32(Unknown), uint32(Asleep), uint32(ConnectedGlobal)) |
551 | nm := New(tc, nullog) |
552 | ch, err := nm.WatchState() |
553 | c.Check(err, IsNil) |
554 | @@ -91,14 +91,14 @@ |
555 | |
556 | // WatchState returns on error if the dbus call fails |
557 | func (s *NMSuite) TestWatchStateFails(c *C) { |
558 | - nm := New(testingbus.NewTestingEndpoint(condition.Work(false)), nullog) |
559 | + nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(false)), nullog) |
560 | _, err := nm.WatchState() |
561 | c.Check(err, NotNil) |
562 | } |
563 | |
564 | // WatchState calls close on its channel when the watch bails |
565 | func (s *NMSuite) TestWatchClosesOnWatchBail(c *C) { |
566 | - tc := testingbus.NewTestingEndpoint(condition.Work(true)) |
567 | + tc := testingbus.NewTestingEndpoint(nil, condition.Work(true)) |
568 | nm := New(tc, nullog) |
569 | ch, err := nm.WatchState() |
570 | c.Check(err, IsNil) |
571 | |
572 | === modified file 'bus/notifications/raw_test.go' |
573 | --- bus/notifications/raw_test.go 2014-01-27 13:02:37 +0000 |
574 | +++ bus/notifications/raw_test.go 2014-01-27 13:02:37 +0000 |
575 | @@ -39,7 +39,7 @@ |
576 | var nullog = logger.NewSimpleLogger(ioutil.Discard, "error") |
577 | |
578 | func (s *RawSuite) TestNotifies(c *C) { |
579 | - endp := testibus.NewTestingEndpoint(condition.Work(true), uint32(1)) |
580 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
581 | raw := Raw(endp, nullog) |
582 | nid, err := raw.Notify("", 0, "", "", "", nil, nil, 0) |
583 | c.Check(err, IsNil) |
584 | @@ -47,21 +47,21 @@ |
585 | } |
586 | |
587 | func (s *RawSuite) TestNotifiesFails(c *C) { |
588 | - endp := testibus.NewTestingEndpoint(condition.Work(false)) |
589 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) |
590 | raw := Raw(endp, nullog) |
591 | _, err := raw.Notify("", 0, "", "", "", nil, nil, 0) |
592 | c.Check(err, NotNil) |
593 | } |
594 | |
595 | func (s *RawSuite) TestNotifiesFailsWeirdly(c *C) { |
596 | - endp := testibus.NewMultiValuedTestingEndpoint(condition.Work(true), []interface{}{1, 2}) |
597 | + endp := testibus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{1, 2}) |
598 | raw := Raw(endp, nullog) |
599 | _, err := raw.Notify("", 0, "", "", "", nil, nil, 0) |
600 | c.Check(err, NotNil) |
601 | } |
602 | |
603 | func (s *RawSuite) TestWatchActions(c *C) { |
604 | - endp := testibus.NewMultiValuedTestingEndpoint(condition.Work(true), |
605 | + endp := testibus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), |
606 | []interface{}{uint32(1), "hello"}) |
607 | raw := Raw(endp, nullog) |
608 | ch, err := raw.WatchActions() |
609 | @@ -80,7 +80,7 @@ |
610 | } |
611 | |
612 | func (s *RawSuite) TestWatchActionsFails(c *C) { |
613 | - endp := testibus.NewTestingEndpoint(condition.Work(false)) |
614 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) |
615 | raw := Raw(endp, nullog) |
616 | _, err := raw.WatchActions() |
617 | c.Check(err, NotNil) |
618 | |
619 | === modified file 'bus/testing/testing_bus.go' |
620 | --- bus/testing/testing_bus.go 2014-01-21 13:21:19 +0000 |
621 | +++ bus/testing/testing_bus.go 2014-01-27 13:02:37 +0000 |
622 | @@ -21,7 +21,6 @@ |
623 | // Here, the bus.Bus implementation. |
624 | |
625 | import ( |
626 | - "errors" |
627 | "launchpad.net/ubuntu-push/bus" |
628 | "launchpad.net/ubuntu-push/logger" |
629 | "launchpad.net/ubuntu-push/testing/condition" |
630 | @@ -32,22 +31,21 @@ |
631 | */ |
632 | |
633 | type testingBus struct { |
634 | - TestCond condition.Interface |
635 | - TestEndp bus.Endpoint |
636 | + endp bus.Endpoint |
637 | } |
638 | |
639 | // Build a bus.Bus that takes a condition to determine whether it should work, |
640 | // as well as a condition and series of return values for the testing |
641 | // bus.Endpoint it builds. |
642 | -func NewTestingBus(clientTC condition.Interface, busTC condition.Interface, retvals ...interface{}) bus.Bus { |
643 | - return &testingBus{clientTC, NewTestingEndpoint(busTC, retvals...)} |
644 | +func NewTestingBus(dialTC condition.Interface, callTC condition.Interface, retvals ...interface{}) bus.Bus { |
645 | + return &testingBus{NewTestingEndpoint(dialTC, callTC, retvals...)} |
646 | } |
647 | |
648 | // Build a bus.Bus that takes a condition to determine whether it should work, |
649 | // as well as a condition and a series of lists of return values for the |
650 | // testing bus.Endpoint it builds. |
651 | -func NewMultiValuedTestingBus(clientTC condition.Interface, busTC condition.Interface, retvalses ...[]interface{}) bus.Bus { |
652 | - return &testingBus{clientTC, NewMultiValuedTestingEndpoint(busTC, retvalses...)} |
653 | +func NewMultiValuedTestingBus(dialTC condition.Interface, callTC condition.Interface, retvalses ...[]interface{}) bus.Bus { |
654 | + return &testingBus{NewMultiValuedTestingEndpoint(dialTC, callTC, retvalses...)} |
655 | } |
656 | |
657 | // ensure testingBus implements bus.Interface |
658 | @@ -57,12 +55,8 @@ |
659 | public methods |
660 | */ |
661 | |
662 | -func (tb *testingBus) Connect(info bus.Address, log logger.Logger) (bus.Endpoint, error) { |
663 | - if tb.TestCond.OK() { |
664 | - return tb.TestEndp, nil |
665 | - } else { |
666 | - return nil, errors.New(tb.TestCond.String()) |
667 | - } |
668 | +func (tb *testingBus) Endpoint(info bus.Address, log logger.Logger) bus.Endpoint { |
669 | + return tb.endp |
670 | } |
671 | |
672 | func (tb *testingBus) String() string { |
673 | |
674 | === modified file 'bus/testing/testing_bus_test.go' |
675 | --- bus/testing/testing_bus_test.go 2014-01-21 13:38:03 +0000 |
676 | +++ bus/testing/testing_bus_test.go 2014-01-27 13:02:37 +0000 |
677 | @@ -30,46 +30,40 @@ |
678 | |
679 | var _ = Suite(&TestingBusSuite{}) |
680 | |
681 | -// Test Connect() on a working bus returns an endpoint that looks right |
682 | -func (s *TestingBusSuite) TestConnectWorks(c *C) { |
683 | +// Test Endpoint() on a working bus returns an endpoint that looks right |
684 | +func (s *TestingBusSuite) TestEndpointWorks(c *C) { |
685 | addr := bus.Address{"", "", ""} |
686 | tb := NewTestingBus(condition.Work(true), condition.Work(false), 42, 42, 42) |
687 | - endp, err := tb.Connect(addr, nil) |
688 | + endp := tb.Endpoint(addr, nil) |
689 | + err := endp.Dial() |
690 | c.Check(err, IsNil) |
691 | - c.Check(endp, FitsTypeOf, &testingEndpoint{}) |
692 | - c.Check(endp.(*testingEndpoint).cond.OK(), Equals, false) |
693 | + c.Assert(endp, FitsTypeOf, &testingEndpoint{}) |
694 | + c.Check(endp.(*testingEndpoint).callCond.OK(), Equals, false) |
695 | c.Check(endp.(*testingEndpoint).retvals, HasLen, 3) |
696 | } |
697 | |
698 | -// Test Connect() on a working "multi-valued" bus returns an endpoint that looks right |
699 | -func (s *TestingBusSuite) TestConnectMultiValued(c *C) { |
700 | +// Test Endpoint() on a working "multi-valued" bus returns an endpoint that looks right |
701 | +func (s *TestingBusSuite) TestEndpointMultiValued(c *C) { |
702 | addr := bus.Address{"", "", ""} |
703 | tb := NewMultiValuedTestingBus(condition.Work(true), condition.Work(true), |
704 | []interface{}{42, 17}, |
705 | []interface{}{42, 17, 13}, |
706 | []interface{}{42}, |
707 | ) |
708 | - endpp, err := tb.Connect(addr, nil) |
709 | + endpp := tb.Endpoint(addr, nil) |
710 | + err := endpp.Dial() |
711 | c.Check(err, IsNil) |
712 | endp, ok := endpp.(*testingEndpoint) |
713 | - c.Check(ok, Equals, true) |
714 | - c.Check(endp.cond.OK(), Equals, true) |
715 | + c.Assert(ok, Equals, true) |
716 | + c.Check(endp.callCond.OK(), Equals, true) |
717 | c.Assert(endp.retvals, HasLen, 3) |
718 | c.Check(endp.retvals[0], HasLen, 2) |
719 | c.Check(endp.retvals[1], HasLen, 3) |
720 | c.Check(endp.retvals[2], HasLen, 1) |
721 | } |
722 | |
723 | -// Test Connect() with a non-working bus fails |
724 | -func (s *TestingBusSuite) TestConnectNoWork(c *C) { |
725 | - addr := bus.Address{"", "", ""} |
726 | - tb := NewTestingBus(condition.Work(false), condition.Work(true)) |
727 | - _, err := tb.Connect(addr, nil) |
728 | - c.Check(err, NotNil) |
729 | -} |
730 | - |
731 | // Test TestingBus stringifies sanely |
732 | func (s *TestingBusSuite) TestStringifyBus(c *C) { |
733 | - tb := NewTestingBus(condition.Work(false), condition.Work(true)) |
734 | + tb := NewTestingBus(nil, nil) |
735 | c.Check(tb.String(), Matches, ".*TestingBus.*") |
736 | } |
737 | |
738 | === modified file 'bus/testing/testing_endpoint.go' |
739 | --- bus/testing/testing_endpoint.go 2014-01-27 10:26:34 +0000 |
740 | +++ bus/testing/testing_endpoint.go 2014-01-27 13:02:37 +0000 |
741 | @@ -20,14 +20,16 @@ |
742 | |
743 | import ( |
744 | "errors" |
745 | + "fmt" |
746 | "launchpad.net/ubuntu-push/bus" |
747 | "launchpad.net/ubuntu-push/testing/condition" |
748 | "time" |
749 | ) |
750 | |
751 | type testingEndpoint struct { |
752 | - cond condition.Interface |
753 | - retvals [][]interface{} |
754 | + dialCond condition.Interface |
755 | + callCond condition.Interface |
756 | + retvals [][]interface{} |
757 | } |
758 | |
759 | // Build a bus.Endpoint that calls OK() on its condition before returning |
760 | @@ -35,22 +37,22 @@ |
761 | // |
762 | // NOTE: Call() always returns the first return value; Watch() will provide |
763 | // each of them in turn, irrespective of whether Call has been called. |
764 | -func NewMultiValuedTestingEndpoint(cond condition.Interface, retvalses ...[]interface{}) bus.Endpoint { |
765 | - return &testingEndpoint{cond, retvalses} |
766 | +func NewMultiValuedTestingEndpoint(dialCond condition.Interface, callCond condition.Interface, retvalses ...[]interface{}) bus.Endpoint { |
767 | + return &testingEndpoint{dialCond, callCond, retvalses} |
768 | } |
769 | |
770 | -func NewTestingEndpoint(cond condition.Interface, retvals ...interface{}) bus.Endpoint { |
771 | +func NewTestingEndpoint(dialCond condition.Interface, callCond condition.Interface, retvals ...interface{}) bus.Endpoint { |
772 | retvalses := make([][]interface{}, len(retvals)) |
773 | for i, x := range retvals { |
774 | retvalses[i] = []interface{}{x} |
775 | } |
776 | - return &testingEndpoint{cond, retvalses} |
777 | + return &testingEndpoint{dialCond, callCond, retvalses} |
778 | } |
779 | |
780 | // See Endpoint's WatchSignal. This WatchSignal will check its condition to |
781 | // decide whether to return an error, or provide each of its return values |
782 | func (tc *testingEndpoint) WatchSignal(member string, f func(...interface{}), d func()) error { |
783 | - if tc.cond.OK() { |
784 | + if tc.callCond.OK() { |
785 | go func() { |
786 | for _, v := range tc.retvals { |
787 | f(v...) |
788 | @@ -67,7 +69,7 @@ |
789 | // See Endpoint's Call. This Call will check its condition to decide whether |
790 | // to return an error, or the first of its return values |
791 | func (tc *testingEndpoint) Call(member string, args ...interface{}) ([]interface{}, error) { |
792 | - if tc.cond.OK() { |
793 | + if tc.callCond.OK() { |
794 | if len(tc.retvals) == 0 { |
795 | panic("No return values provided!") |
796 | } |
797 | @@ -90,8 +92,27 @@ |
798 | return rvs[0], err |
799 | } |
800 | |
801 | +// See Endpoint's Dial. This one will check its dialCondition to |
802 | +// decide whether to return an error or not. |
803 | +func (endp *testingEndpoint) Dial() error { |
804 | + if endp.dialCond.OK() { |
805 | + return nil |
806 | + } else { |
807 | + return errors.New("dialCond said No.") |
808 | + } |
809 | +} |
810 | + |
811 | +// Advanced stringifobabulation |
812 | +func (endp *testingEndpoint) String() string { |
813 | + return fmt.Sprintf("&testingEndpoint{dialCond:(%s) callCond:(%s) retvals:(%#v)", |
814 | + endp.dialCond, endp.callCond, endp.retvals) |
815 | +} |
816 | + |
817 | // see Endpoint's Close. This one does nothing. |
818 | func (tc *testingEndpoint) Close() {} |
819 | |
820 | +// see Endpoint's Jitter. |
821 | +func (tc *testingEndpoint) Jitter(_ time.Duration) time.Duration { return 0 } |
822 | + |
823 | // ensure testingEndpoint implements bus.Endpoint |
824 | var _ bus.Endpoint = &testingEndpoint{} |
825 | |
826 | === modified file 'bus/testing/testing_endpoint_test.go' |
827 | --- bus/testing/testing_endpoint_test.go 2014-01-23 00:21:38 +0000 |
828 | +++ bus/testing/testing_endpoint_test.go 2014-01-27 13:02:37 +0000 |
829 | @@ -20,6 +20,7 @@ |
830 | . "launchpad.net/gocheck" |
831 | "launchpad.net/ubuntu-push/testing/condition" |
832 | "testing" |
833 | + "time" |
834 | ) |
835 | |
836 | // hook up gocheck |
837 | @@ -33,7 +34,7 @@ |
838 | // provided, as advertised. |
839 | func (s *TestingEndpointSuite) TestCallReturnsFirstRetval(c *C) { |
840 | var m, n uint32 = 42, 17 |
841 | - endp := NewTestingEndpoint(condition.Work(true), m, n) |
842 | + endp := NewTestingEndpoint(nil, condition.Work(true), m, n) |
843 | vs, e := endp.Call("what") |
844 | c.Check(e, IsNil) |
845 | c.Check(vs, HasLen, 1) |
846 | @@ -43,7 +44,7 @@ |
847 | // Test the same Call() but with multi-valued endpoint |
848 | func (s *TestingEndpointSuite) TestMultiValuedCall(c *C) { |
849 | var m, n uint32 = 42, 17 |
850 | - endp := NewMultiValuedTestingEndpoint(condition.Work(true), []interface{}{m}, []interface{}{n}) |
851 | + endp := NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{m}, []interface{}{n}) |
852 | vs, e := endp.Call("what") |
853 | c.Check(e, IsNil) |
854 | c.Check(vs, HasLen, 1) |
855 | @@ -52,7 +53,7 @@ |
856 | |
857 | // Test that Call() with a negative condition returns an error. |
858 | func (s *TestingEndpointSuite) TestCallFails(c *C) { |
859 | - endp := NewTestingEndpoint(condition.Work(false)) |
860 | + endp := NewTestingEndpoint(nil, condition.Work(false)) |
861 | _, e := endp.Call("what") |
862 | c.Check(e, NotNil) |
863 | } |
864 | @@ -60,7 +61,7 @@ |
865 | // Test that Call() with a positive condition and no return values panics with |
866 | // a helpful message. |
867 | func (s *TestingEndpointSuite) TestCallPanicsWithNiceMessage(c *C) { |
868 | - endp := NewTestingEndpoint(condition.Work(true)) |
869 | + endp := NewTestingEndpoint(nil, condition.Work(true)) |
870 | c.Check(func() { endp.Call("") }, PanicMatches, "No return values provided.*") |
871 | } |
872 | |
873 | @@ -68,7 +69,7 @@ |
874 | // values over the channel. |
875 | func (s *TestingEndpointSuite) TestWatch(c *C) { |
876 | var m, n uint32 = 42, 17 |
877 | - endp := NewTestingEndpoint(condition.Work(true), m, n) |
878 | + endp := NewTestingEndpoint(nil, condition.Work(true), m, n) |
879 | ch := make(chan uint32) |
880 | e := endp.WatchSignal("what", func(us ...interface{}) { ch <- us[0].(uint32) }, func() { close(ch) }) |
881 | c.Check(e, IsNil) |
882 | @@ -78,7 +79,7 @@ |
883 | |
884 | // Test that WatchSignal() calls the destructor callback when it runs out values |
885 | func (s *TestingEndpointSuite) TestWatchDestructor(c *C) { |
886 | - endp := NewTestingEndpoint(condition.Work(true)) |
887 | + endp := NewTestingEndpoint(nil, condition.Work(true)) |
888 | ch := make(chan uint32) |
889 | e := endp.WatchSignal("what", func(us ...interface{}) {}, func() { close(ch) }) |
890 | c.Check(e, IsNil) |
891 | @@ -88,14 +89,14 @@ |
892 | |
893 | // Test the endpoint can be closed |
894 | func (s *TestingEndpointSuite) TestCloser(c *C) { |
895 | - endp := NewTestingEndpoint(condition.Work(true)) |
896 | + endp := NewTestingEndpoint(nil, condition.Work(true)) |
897 | endp.Close() |
898 | // ... yay? |
899 | } |
900 | |
901 | // Test that WatchSignal() with a negative condition returns an error. |
902 | func (s *TestingEndpointSuite) TestWatchFails(c *C) { |
903 | - endp := NewTestingEndpoint(condition.Work(false)) |
904 | + endp := NewTestingEndpoint(nil, condition.Work(false)) |
905 | e := endp.WatchSignal("what", func(us ...interface{}) {}, func() {}) |
906 | c.Check(e, NotNil) |
907 | } |
908 | @@ -103,8 +104,41 @@ |
909 | // Tests that GetProperty() works |
910 | func (s *TestingEndpointSuite) TestGetProperty(c *C) { |
911 | var m uint32 = 42 |
912 | - endp := NewTestingEndpoint(condition.Work(true), m) |
913 | + endp := NewTestingEndpoint(nil, condition.Work(true), m) |
914 | v, e := endp.GetProperty("what") |
915 | c.Check(e, IsNil) |
916 | c.Check(v, Equals, m) |
917 | } |
918 | + |
919 | +// Tests that GetProperty() fails, too |
920 | +func (s *TestingEndpointSuite) TestGetPropertyFails(c *C) { |
921 | + endp := NewTestingEndpoint(nil, condition.Work(false)) |
922 | + _, e := endp.GetProperty("what") |
923 | + c.Check(e, NotNil) |
924 | +} |
925 | + |
926 | +// Tests that GetProperty() also fails if it's fed garbage |
927 | +func (s *TestingEndpointSuite) TestGetPropertyFailsGargling(c *C) { |
928 | + endp := NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{}) |
929 | + _, e := endp.GetProperty("what") |
930 | + c.Check(e, NotNil) |
931 | +} |
932 | + |
933 | +// Test Dial() with a non-working bus fails |
934 | +func (s *TestingBusSuite) TestDialNoWork(c *C) { |
935 | + endp := NewTestingEndpoint(condition.Work(false), nil) |
936 | + err := endp.Dial() |
937 | + c.Check(err, NotNil) |
938 | +} |
939 | + |
940 | +// Test testingEndpoints serialize, more or less |
941 | +func (s *TestingBusSuite) TestEndpointString(c *C) { |
942 | + endp := NewTestingEndpoint(condition.Fail2Work(2), nil, "hello there") |
943 | + c.Check(endp.String(), Matches, ".*Still Broken.*hello there.*") |
944 | +} |
945 | + |
946 | +// Test testingEndpoints have no jitters |
947 | +func (s *TestingBusSuite) TestEndpointJitter(c *C) { |
948 | + endp := NewTestingEndpoint(nil, nil) |
949 | + c.Check(endp.Jitter(time.Duration(42)), Equals, time.Duration(0)) |
950 | +} |
951 | |
952 | === modified file 'bus/urldispatcher/urldispatcher_test.go' |
953 | --- bus/urldispatcher/urldispatcher_test.go 2014-01-27 13:02:37 +0000 |
954 | +++ bus/urldispatcher/urldispatcher_test.go 2014-01-27 13:02:37 +0000 |
955 | @@ -35,14 +35,14 @@ |
956 | var nullog = logger.NewSimpleLogger(ioutil.Discard, "error") |
957 | |
958 | func (s *UDSuite) TestWorks(c *C) { |
959 | - endp := testibus.NewMultiValuedTestingEndpoint(condition.Work(true), []interface{}{}) |
960 | + endp := testibus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{}) |
961 | ud := New(endp, nullog) |
962 | err := ud.DispatchURL("this") |
963 | c.Check(err, IsNil) |
964 | } |
965 | |
966 | func (s *UDSuite) TestFailsIfCallFails(c *C) { |
967 | - endp := testibus.NewTestingEndpoint(condition.Work(false)) |
968 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) |
969 | ud := New(endp, nullog) |
970 | err := ud.DispatchURL("this") |
971 | c.Check(err, NotNil) |
972 | |
973 | === added directory 'util' |
974 | === added file 'util/redialer.go' |
975 | --- util/redialer.go 1970-01-01 00:00:00 +0000 |
976 | +++ util/redialer.go 2014-01-27 13:02:37 +0000 |
977 | @@ -0,0 +1,86 @@ |
978 | +/* |
979 | + Copyright 2013-2014 Canonical Ltd. |
980 | + |
981 | + This program is free software: you can redistribute it and/or modify it |
982 | + under the terms of the GNU General Public License version 3, as published |
983 | + by the Free Software Foundation. |
984 | + |
985 | + This program is distributed in the hope that it will be useful, but |
986 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
987 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
988 | + PURPOSE. See the GNU General Public License for more details. |
989 | + |
990 | + You should have received a copy of the GNU General Public License along |
991 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
992 | +*/ |
993 | + |
994 | +package util |
995 | + |
996 | +import ( |
997 | + "math/rand" |
998 | + "time" |
999 | +) |
1000 | + |
1001 | +// A Dialer is an object that knows how to establish a connection, and |
1002 | +// where you'd usually want some kind of back off if that connection |
1003 | +// fails. |
1004 | +type Dialer interface { |
1005 | + Dial() error |
1006 | + String() string |
1007 | + Jitter(time.Duration) time.Duration |
1008 | +} |
1009 | + |
1010 | +// The timeouts used during backoff. While this is public, you'd |
1011 | +// usually not need to meddle with it. |
1012 | +var Timeouts []time.Duration |
1013 | + |
1014 | +var ( // for use in testing |
1015 | + quitRedialing chan bool = make(chan bool) |
1016 | +) |
1017 | + |
1018 | +// Jitter returns a random time.Duration somewhere in [-spread, spread]. |
1019 | +// |
1020 | +// This is meant as a default implementation for Dialers to use if wanted. |
1021 | +func Jitter(spread time.Duration) time.Duration { |
1022 | + if spread < 0 { |
1023 | + panic("spread must be non-negative") |
1024 | + } |
1025 | + n := int64(spread) |
1026 | + return time.Duration(rand.Int63n(2*n+1) - n) |
1027 | +} |
1028 | + |
1029 | +// AutoRedialer takes a Dialer and retries its Dial() method until it |
1030 | +// stops returning an error. It does exponential (optionally |
1031 | +// jitter'ed) backoff. |
1032 | +func AutoRedial(dialer Dialer) uint32 { |
1033 | + var timeout time.Duration |
1034 | + var dialAttempts uint32 = 0 // unsigned so it can wrap safely ... |
1035 | + var numTimeouts uint32 = uint32(len(Timeouts)) |
1036 | + for { |
1037 | + if dialer.Dial() == nil { |
1038 | + return dialAttempts + 1 |
1039 | + } |
1040 | + if dialAttempts < numTimeouts { |
1041 | + timeout = Timeouts[dialAttempts] |
1042 | + } else { |
1043 | + timeout = Timeouts[numTimeouts-1] |
1044 | + } |
1045 | + timeout += dialer.Jitter(timeout) |
1046 | + dialAttempts++ |
1047 | + select { |
1048 | + case <-quitRedialing: |
1049 | + return dialAttempts |
1050 | + case <-time.NewTimer(timeout).C: |
1051 | + } |
1052 | + } |
1053 | +} |
1054 | + |
1055 | +func init() { |
1056 | + ps := []int{1, 2, 5, 11, 19, 37, 67, 113, 191} // 3 pₙ₊₁ ≥ 5 pₙ |
1057 | + Timeouts = make([]time.Duration, len(ps)) |
1058 | + for i, n := range ps { |
1059 | + Timeouts[i] = time.Duration(n) * time.Second |
1060 | + } |
1061 | + |
1062 | + rand.Seed(time.Now().Unix()) // good enough for us (not crypto, yadda) |
1063 | +} |
1064 | |
1065 | === added file 'util/redialer_test.go' |
1066 | --- util/redialer_test.go 1970-01-01 00:00:00 +0000 |
1067 | +++ util/redialer_test.go 2014-01-27 13:02:37 +0000 |
1068 | @@ -0,0 +1,84 @@ |
1069 | +/* |
1070 | + Copyright 2013-2014 Canonical Ltd. |
1071 | + |
1072 | + This program is free software: you can redistribute it and/or modify it |
1073 | + under the terms of the GNU General Public License version 3, as published |
1074 | + by the Free Software Foundation. |
1075 | + |
1076 | + This program is distributed in the hope that it will be useful, but |
1077 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
1078 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1079 | + PURPOSE. See the GNU General Public License for more details. |
1080 | + |
1081 | + You should have received a copy of the GNU General Public License along |
1082 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
1083 | +*/ |
1084 | + |
1085 | +package util |
1086 | + |
1087 | +import ( |
1088 | + "io/ioutil" |
1089 | + . "launchpad.net/gocheck" |
1090 | + testibus "launchpad.net/ubuntu-push/bus/testing" |
1091 | + "launchpad.net/ubuntu-push/logger" |
1092 | + "launchpad.net/ubuntu-push/testing/condition" |
1093 | + "testing" |
1094 | + "time" |
1095 | +) |
1096 | + |
1097 | +// hook up gocheck |
1098 | +func TestRedialer(t *testing.T) { TestingT(t) } |
1099 | + |
1100 | +type RedialerSuite struct { |
1101 | + timeouts []time.Duration |
1102 | +} |
1103 | + |
1104 | +var nullog = logger.NewSimpleLogger(ioutil.Discard, "error") |
1105 | +var _ = Suite(&RedialerSuite{}) |
1106 | + |
1107 | +func (s *RedialerSuite) SetUpSuite(c *C) { |
1108 | + s.timeouts = Timeouts |
1109 | + Timeouts = []time.Duration{0, 0} |
1110 | +} |
1111 | + |
1112 | +func (s *RedialerSuite) TearDownSuite(c *C) { |
1113 | + Timeouts = s.timeouts |
1114 | + s.timeouts = nil |
1115 | +} |
1116 | + |
1117 | +func (s *RedialerSuite) TestWorks(c *C) { |
1118 | + endp := testibus.NewTestingEndpoint(condition.Fail2Work(3), nil) |
1119 | + // instead of bus.Dial(), we do AutoRedial(bus) |
1120 | + c.Check(AutoRedial(endp), Equals, uint32(4)) |
1121 | +} |
1122 | + |
1123 | +func (s *RedialerSuite) TestCanBeStopped(c *C) { |
1124 | + endp := testibus.NewTestingEndpoint(condition.Work(false), nil) |
1125 | + go func() { c.Check(AutoRedial(endp), Equals, uint32(1)) }() |
1126 | + quitRedialing <- true |
1127 | +} |
1128 | + |
1129 | +func (s *RedialerSuite) TestJitter(c *C) { |
1130 | + num_tries := 20 // should do the math |
1131 | + spread := time.Second // |
1132 | + has_neg := false |
1133 | + has_pos := false |
1134 | + has_zero := true |
1135 | + for i := 0; i < num_tries; i++ { |
1136 | + n := Jitter(spread) |
1137 | + if n > 0 { |
1138 | + has_pos = true |
1139 | + } else if n < 0 { |
1140 | + has_neg = true |
1141 | + } else { |
1142 | + has_zero = true |
1143 | + } |
1144 | + } |
1145 | + c.Check(has_neg, Equals, true) |
1146 | + c.Check(has_pos, Equals, true) |
1147 | + c.Check(has_zero, Equals, true) |
1148 | + |
1149 | + // a negative spread is caught in the reasonable place |
1150 | + c.Check(func() { Jitter(time.Duration(-1)) }, PanicMatches, |
1151 | + "spread must be non-negative") |
1152 | +} |
I think the random jitter could be included after 20sec already, also probably it should be optional?
./bus/connectivity/ and /bus/testing run tests twice because of double invoking TestingT