Merge lp:~pedronis/ubuntu-push/automatic-bug-fixes-into-krillin-rtm into lp:ubuntu-push/krillin-rtm
- automatic-bug-fixes-into-krillin-rtm
- Merge into krillin-rtm
Proposed by
Samuele Pedroni
Status: | Merged |
---|---|
Approved by: | Roberto Alsina |
Approved revision: | 143 |
Merged at revision: | 143 |
Proposed branch: | lp:~pedronis/ubuntu-push/automatic-bug-fixes-into-krillin-rtm |
Merge into: | lp:ubuntu-push/krillin-rtm |
Diff against target: |
954 lines (+624/-59) 9 files modified
bus/networkmanager/networkmanager.go (+65/-12) bus/networkmanager/networkmanager_test.go (+109/-1) bus/urfkill/urfkill.go (+94/-0) bus/urfkill/urfkill.go_test.go (+121/-0) client/client.go (+2/-1) client/client_test.go (+1/-0) debian/config.json (+2/-1) poller/poller.go (+159/-33) poller/poller_test.go (+71/-11) |
To merge this branch: | bzr merge lp:~pedronis/ubuntu-push/automatic-bug-fixes-into-krillin-rtm |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Roberto Alsina (community) | Approve | ||
Review via email: mp+254714@code.launchpad.net |
Commit message
[Bret Barker]
* add a hacky busy sleep loop to workaround go's sleep not
accounting for suspended time (lp:1435109).
[Samuele Pedroni]
* stop waking up for polling if in flight-mode and wireless not
enabled (lp:1437135).
Description of the change
To post a comment you must log in.
Revision history for this message
Roberto Alsina (ralsina) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bus/networkmanager/networkmanager.go' |
2 | --- bus/networkmanager/networkmanager.go 2015-03-26 15:57:45 +0000 |
3 | +++ bus/networkmanager/networkmanager.go 2015-03-31 09:33:32 +0000 |
4 | @@ -1,5 +1,5 @@ |
5 | /* |
6 | - Copyright 2013-2014 Canonical Ltd. |
7 | + Copyright 2013-2015 Canonical Ltd. |
8 | |
9 | This program is free software: you can redistribute it and/or modify it |
10 | under the terms of the GNU General Public License version 3, as published |
11 | @@ -14,9 +14,10 @@ |
12 | with this program. If not, see <http://www.gnu.org/licenses/>. |
13 | */ |
14 | |
15 | -// Package networkmanager wraps a couple of NetworkManager's DBus API points: |
16 | -// the org.freedesktop.NetworkManager.state call, and listening for the |
17 | -// StateChange signal. |
18 | +// Package networkmanager wraps a couple of NetworkManager's DBus API |
19 | +// points: the org.freedesktop.NetworkManager.state call, and |
20 | +// listening for the StateChange signal, similarly for the primary |
21 | +// connection and wireless enabled state. |
22 | package networkmanager |
23 | |
24 | import ( |
25 | @@ -47,8 +48,14 @@ |
26 | // primary connection. |
27 | GetPrimaryConnection() string |
28 | // WatchPrimaryConnection listens for changes of NetworkManager's |
29 | - // Primary Connection, and sends it out over the channel returned. |
30 | + // Primary Connection, and sends them out over the channel returned. |
31 | WatchPrimaryConnection() (<-chan string, bus.Cancellable, error) |
32 | + // GetWirelessEnabled fetches and returns NetworkManager's |
33 | + // wireless state. |
34 | + GetWirelessEnabled() bool |
35 | + // WatchWirelessEnabled listens for changes of NetworkManager's |
36 | + // wireless state, and sends them out over the channel returned. |
37 | + WatchWirelessEnabled() (<-chan bool, bus.Cancellable, error) |
38 | } |
39 | |
40 | type networkManager struct { |
41 | @@ -71,7 +78,7 @@ |
42 | func (nm *networkManager) GetState() State { |
43 | s, err := nm.bus.GetProperty("state") |
44 | if err != nil { |
45 | - nm.log.Errorf("failed gettting current state: %s", err) |
46 | + nm.log.Errorf("failed getting current state: %s", err) |
47 | nm.log.Debugf("defaulting state to Unknown") |
48 | return Unknown |
49 | } |
50 | @@ -108,16 +115,16 @@ |
51 | } |
52 | |
53 | func (nm *networkManager) GetPrimaryConnection() string { |
54 | - s, err := nm.bus.GetProperty("PrimaryConnection") |
55 | + got, err := nm.bus.GetProperty("PrimaryConnection") |
56 | if err != nil { |
57 | - nm.log.Errorf("failed gettting current primary connection: %s", err) |
58 | - nm.log.Debugf("defaulting primary connection to empty") |
59 | + nm.log.Errorf("failed getting current PrimaryConnection: %s", err) |
60 | + nm.log.Debugf("defaulting PrimaryConnection to empty") |
61 | return "" |
62 | } |
63 | |
64 | - v, ok := s.(dbus.ObjectPath) |
65 | + v, ok := got.(dbus.ObjectPath) |
66 | if !ok { |
67 | - nm.log.Errorf("got weird PrimaryConnection: %#v", s) |
68 | + nm.log.Errorf("got weird PrimaryConnection: %#v", got) |
69 | return "" |
70 | } |
71 | |
72 | @@ -142,7 +149,7 @@ |
73 | nm.log.Errorf("got weird PrimaryConnection via PropertiesChanged: %#v", v) |
74 | return |
75 | } |
76 | - nm.log.Debugf("got primary connection: %s", con) |
77 | + nm.log.Debugf("got PrimaryConnection change: %s", con) |
78 | ch <- string(con) |
79 | }, func() { close(ch) }) |
80 | if err != nil { |
81 | @@ -152,3 +159,49 @@ |
82 | |
83 | return ch, w, nil |
84 | } |
85 | + |
86 | +func (nm *networkManager) GetWirelessEnabled() bool { |
87 | + got, err := nm.bus.GetProperty("WirelessEnabled") |
88 | + if err != nil { |
89 | + nm.log.Errorf("failed getting WirelessEnabled: %s", err) |
90 | + nm.log.Debugf("defaulting WirelessEnabled to true") |
91 | + return true |
92 | + } |
93 | + |
94 | + v, ok := got.(bool) |
95 | + if !ok { |
96 | + nm.log.Errorf("got weird WirelessEnabled: %#v", got) |
97 | + return true |
98 | + } |
99 | + |
100 | + return v |
101 | +} |
102 | + |
103 | +func (nm *networkManager) WatchWirelessEnabled() (<-chan bool, bus.Cancellable, error) { |
104 | + ch := make(chan bool) |
105 | + w, err := nm.bus.WatchSignal("PropertiesChanged", |
106 | + func(ppsi ...interface{}) { |
107 | + pps, ok := ppsi[0].(map[string]dbus.Variant) |
108 | + if !ok { |
109 | + nm.log.Errorf("got weird PropertiesChanged: %#v", ppsi[0]) |
110 | + return |
111 | + } |
112 | + v, ok := pps["WirelessEnabled"] |
113 | + if !ok { |
114 | + return |
115 | + } |
116 | + en, ok := v.Value.(bool) |
117 | + if !ok { |
118 | + nm.log.Errorf("got weird WirelessEnabled via PropertiesChanged: %#v", v) |
119 | + return |
120 | + } |
121 | + nm.log.Debugf("got WirelessEnabled change: %v", en) |
122 | + ch <- en |
123 | + }, func() { close(ch) }) |
124 | + if err != nil { |
125 | + nm.log.Debugf("failed to set up the watch: %s", err) |
126 | + return nil, nil, err |
127 | + } |
128 | + |
129 | + return ch, w, nil |
130 | +} |
131 | |
132 | === modified file 'bus/networkmanager/networkmanager_test.go' |
133 | --- bus/networkmanager/networkmanager_test.go 2015-02-26 19:36:57 +0000 |
134 | +++ bus/networkmanager/networkmanager_test.go 2015-03-31 09:33:32 +0000 |
135 | @@ -1,5 +1,5 @@ |
136 | /* |
137 | - Copyright 2013-2014 Canonical Ltd. |
138 | + Copyright 2013-2015 Canonical Ltd. |
139 | |
140 | This program is free software: you can redistribute it and/or modify it |
141 | under the terms of the GNU General Public License version 3, as published |
142 | @@ -232,3 +232,111 @@ |
143 | c.Check(ok, Equals, true) |
144 | c.Check(v, Equals, "42") |
145 | } |
146 | + |
147 | +// GetWirelessEnabled returns the right state when everything works |
148 | +func (s *NMSuite) TestGetWirelessEnabled(c *C) { |
149 | + nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(true), false), s.log) |
150 | + en := nm.GetWirelessEnabled() |
151 | + c.Check(en, Equals, false) |
152 | +} |
153 | + |
154 | +// GetWirelessEnabled returns the right state when dbus fails |
155 | +func (s *NMSuite) TestGetWirelessEnabledFail(c *C) { |
156 | + nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(false)), s.log) |
157 | + en := nm.GetWirelessEnabled() |
158 | + c.Check(en, Equals, true) |
159 | +} |
160 | + |
161 | +// GetWirelessEnabled returns the right state when dbus works but delivers rubbish values |
162 | +func (s *NMSuite) TestGetWirelessEnabledRubbishValues(c *C) { |
163 | + nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(true), "broken"), s.log) |
164 | + en := nm.GetWirelessEnabled() |
165 | + c.Check(en, Equals, true) |
166 | +} |
167 | + |
168 | +// GetWirelessEnabled returns the right state when dbus works but delivers a rubbish structure |
169 | +func (s *NMSuite) TestGetWirelessEnabledRubbishStructure(c *C) { |
170 | + nm := New(testingbus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{}), s.log) |
171 | + en := nm.GetWirelessEnabled() |
172 | + c.Check(en, Equals, true) |
173 | +} |
174 | + |
175 | +func mkWirelessEnMap(en bool) map[string]dbus.Variant { |
176 | + m := make(map[string]dbus.Variant) |
177 | + m["WirelessEnabled"] = dbus.Variant{en} |
178 | + return m |
179 | +} |
180 | + |
181 | +// WatchWirelessEnabled sends a stream of wireless enabled states over the channel |
182 | +func (s *NMSuite) TestWatchWirelessEnabled(c *C) { |
183 | + tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), |
184 | + mkWirelessEnMap(true), |
185 | + mkWirelessEnMap(false), |
186 | + mkWirelessEnMap(true), |
187 | + ) |
188 | + nm := New(tc, s.log) |
189 | + ch, w, err := nm.WatchWirelessEnabled() |
190 | + c.Assert(err, IsNil) |
191 | + defer w.Cancel() |
192 | + l := []bool{<-ch, <-ch, <-ch} |
193 | + c.Check(l, DeepEquals, []bool{true, false, true}) |
194 | +} |
195 | + |
196 | +// WatchWirelessEnabled returns on error if the dbus call fails |
197 | +func (s *NMSuite) TestWatchWirelessEnabledFails(c *C) { |
198 | + nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(false)), s.log) |
199 | + _, _, err := nm.WatchWirelessEnabled() |
200 | + c.Check(err, NotNil) |
201 | +} |
202 | + |
203 | +// WatchWirelessEnabled calls close on its channel when the watch bails |
204 | +func (s *NMSuite) TestWatchWirelessEnabledClosesOnWatchBail(c *C) { |
205 | + tc := testingbus.NewTestingEndpoint(nil, condition.Work(true)) |
206 | + nm := New(tc, s.log) |
207 | + ch, w, err := nm.WatchWirelessEnabled() |
208 | + c.Assert(err, IsNil) |
209 | + defer w.Cancel() |
210 | + _, ok := <-ch |
211 | + c.Check(ok, Equals, false) |
212 | +} |
213 | + |
214 | +// WatchWirelessEnabled survives rubbish values |
215 | +func (s *NMSuite) TestWatchWirelessEnabledSurvivesRubbishValues(c *C) { |
216 | + tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), "gorp") |
217 | + nm := New(tc, s.log) |
218 | + ch, w, err := nm.WatchWirelessEnabled() |
219 | + c.Assert(err, IsNil) |
220 | + defer w.Cancel() |
221 | + _, ok := <-ch |
222 | + c.Check(ok, Equals, false) |
223 | +} |
224 | + |
225 | +// WatchWirelessEnabled ignores non-WirelessEnabled PropertyChanged |
226 | +func (s *NMSuite) TestWatchWirelessEnabledIgnoresIrrelephant(c *C) { |
227 | + tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), |
228 | + map[string]dbus.Variant{"foo": dbus.Variant{}}, |
229 | + map[string]dbus.Variant{"WirelessEnabled": dbus.Variant{true}}, |
230 | + ) |
231 | + nm := New(tc, s.log) |
232 | + ch, w, err := nm.WatchWirelessEnabled() |
233 | + c.Assert(err, IsNil) |
234 | + defer w.Cancel() |
235 | + v, ok := <-ch |
236 | + c.Check(ok, Equals, true) |
237 | + c.Check(v, Equals, true) |
238 | +} |
239 | + |
240 | +// WatchWirelessEnabled ignores rubbish WirelessEnabled |
241 | +func (s *NMSuite) TestWatchWirelessEnabledIgnoresRubbishValues(c *C) { |
242 | + tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), |
243 | + map[string]dbus.Variant{"WirelessEnabled": dbus.Variant{-12}}, |
244 | + map[string]dbus.Variant{"WirelessEnabled": dbus.Variant{false}}, |
245 | + ) |
246 | + nm := New(tc, s.log) |
247 | + ch, w, err := nm.WatchWirelessEnabled() |
248 | + c.Assert(err, IsNil) |
249 | + defer w.Cancel() |
250 | + v, ok := <-ch |
251 | + c.Check(ok, Equals, true) |
252 | + c.Check(v, Equals, false) |
253 | +} |
254 | |
255 | === added directory 'bus/urfkill' |
256 | === added file 'bus/urfkill/urfkill.go' |
257 | --- bus/urfkill/urfkill.go 1970-01-01 00:00:00 +0000 |
258 | +++ bus/urfkill/urfkill.go 2015-03-31 09:33:32 +0000 |
259 | @@ -0,0 +1,94 @@ |
260 | +/* |
261 | + Copyright 2015 Canonical Ltd. |
262 | + |
263 | + This program is free software: you can redistribute it and/or modify it |
264 | + under the terms of the GNU General Public License version 3, as published |
265 | + by the Free Software Foundation. |
266 | + |
267 | + This program is distributed in the hope that it will be useful, but |
268 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
269 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
270 | + PURPOSE. See the GNU General Public License for more details. |
271 | + |
272 | + You should have received a copy of the GNU General Public License along |
273 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
274 | +*/ |
275 | + |
276 | +// Package urfkill wraps a couple of URfkill's DBus API points to |
277 | +// watch for flight mode state changes. |
278 | +package urfkill |
279 | + |
280 | +import ( |
281 | + //"launchpad.net/go-dbus/v1" |
282 | + |
283 | + "launchpad.net/ubuntu-push/bus" |
284 | + "launchpad.net/ubuntu-push/logger" |
285 | +) |
286 | + |
287 | +// URfkill lives on a well-knwon bus.Address |
288 | +var BusAddress bus.Address = bus.Address{ |
289 | + Interface: "org.freedesktop.URfkill", |
290 | + Path: "/org/freedesktop/URfkill", |
291 | + Name: "org.freedesktop.URfkill", |
292 | +} |
293 | + |
294 | +/***************************************************************** |
295 | + * URfkill (and its implementation) |
296 | + */ |
297 | + |
298 | +type URfkill interface { |
299 | + // IsFlightMode returns flight mode state. |
300 | + IsFlightMode() bool |
301 | + // WatchFlightMode listens for changes to URfkill's flight |
302 | + // mode state, and sends them out over the channel returned. |
303 | + WatchFlightMode() (<-chan bool, bus.Cancellable, error) |
304 | +} |
305 | + |
306 | +type uRfkill struct { |
307 | + bus bus.Endpoint |
308 | + log logger.Logger |
309 | +} |
310 | + |
311 | +// New returns a new URfkill that'll use the provided bus.Endpoint |
312 | +func New(endp bus.Endpoint, log logger.Logger) URfkill { |
313 | + return &uRfkill{endp, log} |
314 | +} |
315 | + |
316 | +// ensure uRfkill implements URfkill |
317 | +var _ URfkill = &uRfkill{} |
318 | + |
319 | +/* |
320 | + public methods |
321 | +*/ |
322 | + |
323 | +func (ur *uRfkill) IsFlightMode() bool { |
324 | + var res bool |
325 | + err := ur.bus.Call("IsFlightMode", bus.Args(), &res) |
326 | + if err != nil { |
327 | + ur.log.Errorf("failed getting flight-mode state: %s", err) |
328 | + ur.log.Debugf("defaulting flight-mode state to false") |
329 | + return false |
330 | + } |
331 | + return res |
332 | +} |
333 | + |
334 | +func (ur *uRfkill) WatchFlightMode() (<-chan bool, bus.Cancellable, error) { |
335 | + ch := make(chan bool) |
336 | + w, err := ur.bus.WatchSignal("FlightModeChanged", |
337 | + func(ns ...interface{}) { |
338 | + stbool, ok := ns[0].(bool) |
339 | + if !ok { |
340 | + ur.log.Errorf("got weird flight-mode state: %#v", ns[0]) |
341 | + return |
342 | + } |
343 | + ur.log.Debugf("got flight-mode change: %v", stbool) |
344 | + ch <- stbool |
345 | + }, |
346 | + func() { close(ch) }) |
347 | + if err != nil { |
348 | + ur.log.Debugf("Failed to set up the watch: %s", err) |
349 | + return nil, nil, err |
350 | + } |
351 | + |
352 | + return ch, w, nil |
353 | +} |
354 | |
355 | === added file 'bus/urfkill/urfkill.go_test.go' |
356 | --- bus/urfkill/urfkill.go_test.go 1970-01-01 00:00:00 +0000 |
357 | +++ bus/urfkill/urfkill.go_test.go 2015-03-31 09:33:32 +0000 |
358 | @@ -0,0 +1,121 @@ |
359 | +/* |
360 | + Copyright 2015 Canonical Ltd. |
361 | + |
362 | + This program is free software: you can redistribute it and/or modify it |
363 | + under the terms of the GNU General Public License version 3, as published |
364 | + by the Free Software Foundation. |
365 | + |
366 | + This program is distributed in the hope that it will be useful, but |
367 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
368 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
369 | + PURPOSE. See the GNU General Public License for more details. |
370 | + |
371 | + You should have received a copy of the GNU General Public License along |
372 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
373 | +*/ |
374 | + |
375 | +package urfkill |
376 | + |
377 | +import ( |
378 | + "testing" |
379 | + |
380 | + //"launchpad.net/go-dbus/v1" |
381 | + . "launchpad.net/gocheck" |
382 | + |
383 | + testingbus "launchpad.net/ubuntu-push/bus/testing" |
384 | + "launchpad.net/ubuntu-push/logger" |
385 | + helpers "launchpad.net/ubuntu-push/testing" |
386 | + "launchpad.net/ubuntu-push/testing/condition" |
387 | +) |
388 | + |
389 | +// hook up gocheck |
390 | +func Test(t *testing.T) { TestingT(t) } |
391 | + |
392 | +type URSuite struct { |
393 | + log logger.Logger |
394 | +} |
395 | + |
396 | +var _ = Suite(&URSuite{}) |
397 | + |
398 | +func (s *URSuite) SetUpTest(c *C) { |
399 | + s.log = helpers.NewTestLogger(c, "debug") |
400 | +} |
401 | + |
402 | +func (s *URSuite) TestNew(c *C) { |
403 | + ur := New(testingbus.NewTestingEndpoint(nil, condition.Work(true)), s.log) |
404 | + c.Check(ur, NotNil) |
405 | +} |
406 | + |
407 | +// IsFlightMode returns the right state when everything works |
408 | +func (s *URSuite) TestIsFlightMode(c *C) { |
409 | + endp := testingbus.NewTestingEndpoint(nil, condition.Work(true), true) |
410 | + ur := New(endp, s.log) |
411 | + state := ur.IsFlightMode() |
412 | + c.Check(state, Equals, true) |
413 | + callArgs := testingbus.GetCallArgs(endp) |
414 | + c.Assert(callArgs, HasLen, 1) |
415 | + c.Assert(callArgs[0].Member, Equals, "IsFlightMode") |
416 | + c.Assert(callArgs[0].Args, HasLen, 0) |
417 | +} |
418 | + |
419 | +// IsFlightMode returns the right state when dbus fails |
420 | +func (s *URSuite) TestIsFlightModeFail(c *C) { |
421 | + ur := New(testingbus.NewTestingEndpoint(nil, condition.Work(false)), s.log) |
422 | + state := ur.IsFlightMode() |
423 | + c.Check(state, Equals, false) |
424 | +} |
425 | + |
426 | +// IsFlightMode returns the right state when dbus works but delivers |
427 | +// rubbish values |
428 | +func (s *URSuite) TestIsFlightModeRubbishValues(c *C) { |
429 | + ur := New(testingbus.NewTestingEndpoint(nil, condition.Work(true), "broken"), s.log) |
430 | + state := ur.IsFlightMode() |
431 | + c.Check(state, Equals, false) |
432 | +} |
433 | + |
434 | +// IsFlightMode returns the right state when dbus works but delivers a rubbish structure |
435 | +func (s *URSuite) TestIsFlightModeRubbishStructure(c *C) { |
436 | + ur := New(testingbus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{}), s.log) |
437 | + state := ur.IsFlightMode() |
438 | + c.Check(state, Equals, false) |
439 | +} |
440 | + |
441 | +// WatchFightMode sends a stream of states over the channel |
442 | +func (s *URSuite) TestWatchFlightMode(c *C) { |
443 | + tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), false, true, false) |
444 | + ur := New(tc, s.log) |
445 | + ch, w, err := ur.WatchFlightMode() |
446 | + c.Assert(err, IsNil) |
447 | + defer w.Cancel() |
448 | + l := []bool{<-ch, <-ch, <-ch} |
449 | + c.Check(l, DeepEquals, []bool{false, true, false}) |
450 | +} |
451 | + |
452 | +// WatchFlightMode returns on error if the dbus call fails |
453 | +func (s *URSuite) TestWatchFlightModeFails(c *C) { |
454 | + ur := New(testingbus.NewTestingEndpoint(nil, condition.Work(false)), s.log) |
455 | + _, _, err := ur.WatchFlightMode() |
456 | + c.Check(err, NotNil) |
457 | +} |
458 | + |
459 | +// WatchFlightMode calls close on its channel when the watch bails |
460 | +func (s *URSuite) TestWatchFlightModeClosesOnWatchBail(c *C) { |
461 | + tc := testingbus.NewTestingEndpoint(nil, condition.Work(true)) |
462 | + ur := New(tc, s.log) |
463 | + ch, w, err := ur.WatchFlightMode() |
464 | + c.Assert(err, IsNil) |
465 | + defer w.Cancel() |
466 | + _, ok := <-ch |
467 | + c.Check(ok, Equals, false) |
468 | +} |
469 | + |
470 | +// WatchFlightMode survives rubbish values |
471 | +func (s *URSuite) TestWatchFlightModeSurvivesRubbishValues(c *C) { |
472 | + tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), "gorp") |
473 | + ur := New(tc, s.log) |
474 | + ch, w, err := ur.WatchFlightMode() |
475 | + c.Assert(err, IsNil) |
476 | + defer w.Cancel() |
477 | + _, ok := <-ch |
478 | + c.Check(ok, Equals, false) |
479 | +} |
480 | |
481 | === modified file 'client/client.go' |
482 | --- client/client.go 2015-03-26 15:57:45 +0000 |
483 | +++ client/client.go 2015-03-31 09:33:32 +0000 |
484 | @@ -79,6 +79,7 @@ |
485 | PollNetworkWait config.ConfigTimeDuration `json:"poll_net_wait"` |
486 | PollPolldWait config.ConfigTimeDuration `json:"poll_polld_wait"` |
487 | PollDoneWait config.ConfigTimeDuration `json:"poll_done_wait"` |
488 | + PollBusyWait config.ConfigTimeDuration `json:"poll_busy_wait"` |
489 | } |
490 | |
491 | // PushService is the interface we use of service.PushService. |
492 | @@ -246,6 +247,7 @@ |
493 | NetworkWait: client.config.PollNetworkWait.TimeDuration(), |
494 | PolldWait: client.config.PollPolldWait.TimeDuration(), |
495 | DoneWait: client.config.PollDoneWait.TimeDuration(), |
496 | + BusyWait: client.config.PollBusyWait.TimeDuration(), |
497 | }, |
498 | Log: client.log, |
499 | SessionStateGetter: client.session, |
500 | @@ -285,7 +287,6 @@ |
501 | |
502 | // takeTheBus starts the connection(s) to D-Bus and sets up associated event channels |
503 | func (client *PushClient) takeTheBus() error { |
504 | - fmt.Println("FOO") |
505 | cs := connectivity.New(client.connectivityEndp, |
506 | client.config.ConnectivityConfig, client.log) |
507 | go cs.Track(client.connCh) |
508 | |
509 | === modified file 'client/client_test.go' |
510 | --- client/client_test.go 2015-03-26 15:57:45 +0000 |
511 | +++ client/client_test.go 2015-03-31 09:33:32 +0000 |
512 | @@ -185,6 +185,7 @@ |
513 | "poll_net_wait": "1m", |
514 | "poll_polld_wait": "3m", |
515 | "poll_done_wait": "5s", |
516 | + "poll_busy_wait": "0s", |
517 | } |
518 | for k, v := range overrides { |
519 | cfgMap[k] = v |
520 | |
521 | === modified file 'debian/config.json' |
522 | --- debian/config.json 2015-03-26 15:57:45 +0000 |
523 | +++ debian/config.json 2015-03-31 09:33:32 +0000 |
524 | @@ -19,5 +19,6 @@ |
525 | "poll_settle": "20ms", |
526 | "poll_net_wait": "1m", |
527 | "poll_polld_wait": "3m", |
528 | - "poll_done_wait": "5s" |
529 | + "poll_done_wait": "5s", |
530 | + "poll_busy_wait": "1s" |
531 | } |
532 | |
533 | === modified file 'poller/poller.go' |
534 | --- poller/poller.go 2015-03-26 15:57:45 +0000 |
535 | +++ poller/poller.go 2015-03-31 09:33:32 +0000 |
536 | @@ -1,5 +1,5 @@ |
537 | /* |
538 | - Copyright 2014 Canonical Ltd. |
539 | + Copyright 2014-2015 Canonical Ltd. |
540 | |
541 | This program is free software: you can redistribute it and/or modify it |
542 | under the terms of the GNU General Public License version 3, as published |
543 | @@ -25,8 +25,10 @@ |
544 | "time" |
545 | |
546 | "launchpad.net/ubuntu-push/bus" |
547 | + "launchpad.net/ubuntu-push/bus/networkmanager" |
548 | "launchpad.net/ubuntu-push/bus/polld" |
549 | "launchpad.net/ubuntu-push/bus/powerd" |
550 | + "launchpad.net/ubuntu-push/bus/urfkill" |
551 | "launchpad.net/ubuntu-push/client/session" |
552 | "launchpad.net/ubuntu-push/logger" |
553 | "launchpad.net/ubuntu-push/util" |
554 | @@ -48,6 +50,7 @@ |
555 | NetworkWait time.Duration |
556 | PolldWait time.Duration |
557 | DoneWait time.Duration |
558 | + BusyWait time.Duration |
559 | } |
560 | |
561 | type Poller interface { |
562 | @@ -63,21 +66,29 @@ |
563 | } |
564 | |
565 | type poller struct { |
566 | - times Times |
567 | - log logger.Logger |
568 | - powerd powerd.Powerd |
569 | - polld polld.Polld |
570 | - cookie string |
571 | - sessionState stater |
572 | + times Times |
573 | + log logger.Logger |
574 | + nm networkmanager.NetworkManager |
575 | + powerd powerd.Powerd |
576 | + polld polld.Polld |
577 | + urfkill urfkill.URfkill |
578 | + cookie string |
579 | + sessionState stater |
580 | + requestWakeupCh chan struct{} |
581 | + requestedWakeupErrCh chan error |
582 | + holdsWakeLockCh chan bool |
583 | } |
584 | |
585 | func New(setup *PollerSetup) Poller { |
586 | return &poller{ |
587 | - times: setup.Times, |
588 | - log: setup.Log, |
589 | - powerd: nil, |
590 | - polld: nil, |
591 | - sessionState: setup.SessionStateGetter, |
592 | + times: setup.Times, |
593 | + log: setup.Log, |
594 | + powerd: nil, |
595 | + polld: nil, |
596 | + sessionState: setup.SessionStateGetter, |
597 | + requestWakeupCh: make(chan struct{}), |
598 | + requestedWakeupErrCh: make(chan error), |
599 | + holdsWakeLockCh: make(chan bool), |
600 | } |
601 | } |
602 | |
603 | @@ -92,10 +103,17 @@ |
604 | if p.powerd != nil || p.polld != nil { |
605 | return ErrAlreadyStarted |
606 | } |
607 | + nmEndp := bus.SystemBus.Endpoint(networkmanager.BusAddress, p.log) |
608 | powerdEndp := bus.SystemBus.Endpoint(powerd.BusAddress, p.log) |
609 | polldEndp := bus.SessionBus.Endpoint(polld.BusAddress, p.log) |
610 | + urEndp := bus.SystemBus.Endpoint(urfkill.BusAddress, p.log) |
611 | var wg sync.WaitGroup |
612 | - wg.Add(2) |
613 | + wg.Add(4) |
614 | + go func() { |
615 | + n := util.NewAutoRedialer(nmEndp).Redial() |
616 | + p.log.Debugf("NetworkManager dialed on try %d", n) |
617 | + wg.Done() |
618 | + }() |
619 | go func() { |
620 | n := util.NewAutoRedialer(powerdEndp).Redial() |
621 | p.log.Debugf("powerd dialed on try %d", n) |
622 | @@ -106,10 +124,31 @@ |
623 | p.log.Debugf("polld dialed in on try %d", n) |
624 | wg.Done() |
625 | }() |
626 | + go func() { |
627 | + n := util.NewAutoRedialer(urEndp).Redial() |
628 | + p.log.Debugf("URfkill dialed on try %d", n) |
629 | + wg.Done() |
630 | + }() |
631 | wg.Wait() |
632 | |
633 | + p.nm = networkmanager.New(nmEndp, p.log) |
634 | p.powerd = powerd.New(powerdEndp, p.log) |
635 | p.polld = polld.New(polldEndp, p.log) |
636 | + p.urfkill = urfkill.New(urEndp, p.log) |
637 | + |
638 | + // busy sleep loop to workaround go's timer/sleep |
639 | + // not accounting for time when the system is suspended |
640 | + // see https://bugs.launchpad.net/ubuntu/+source/ubuntu-push/+bug/1435109 |
641 | + if p.times.BusyWait > 0 { |
642 | + p.log.Debugf("starting busy loop with %s interval", p.times.BusyWait) |
643 | + go func() { |
644 | + for { |
645 | + time.Sleep(p.times.BusyWait) |
646 | + } |
647 | + }() |
648 | + } else { |
649 | + p.log.Debugf("skipping busy loop") |
650 | + } |
651 | |
652 | return nil |
653 | } |
654 | @@ -118,7 +157,7 @@ |
655 | if p.log == nil { |
656 | return ErrUnconfigured |
657 | } |
658 | - if p.powerd == nil || p.polld == nil { |
659 | + if p.nm == nil || p.powerd == nil || p.polld == nil || p.urfkill == nil { |
660 | return ErrNotStarted |
661 | } |
662 | wakeupCh, err := p.powerd.WatchWakeups() |
663 | @@ -129,10 +168,110 @@ |
664 | if err != nil { |
665 | return err |
666 | } |
667 | - go p.run(wakeupCh, doneCh) |
668 | + flightMode := p.urfkill.IsFlightMode() |
669 | + wirelessEnabled := p.nm.GetWirelessEnabled() |
670 | + flightModeCh, _, err := p.urfkill.WatchFlightMode() |
671 | + if err != nil { |
672 | + return err |
673 | + } |
674 | + wirelessEnabledCh, _, err := p.nm.WatchWirelessEnabled() |
675 | + if err != nil { |
676 | + return err |
677 | + } |
678 | + |
679 | + filteredWakeUpCh := make(chan bool) |
680 | + go p.control(wakeupCh, filteredWakeUpCh, flightMode, flightModeCh, wirelessEnabled, wirelessEnabledCh) |
681 | + go p.run(filteredWakeUpCh, doneCh) |
682 | return nil |
683 | } |
684 | |
685 | +func (p *poller) doRequestWakeup(delta time.Duration) (time.Time, string, error) { |
686 | + t := time.Now().Add(delta).Truncate(time.Second) |
687 | + cookie, err := p.powerd.RequestWakeup("ubuntu push client", t) |
688 | + if err == nil { |
689 | + p.log.Debugf("requested wakeup at %s", t) |
690 | + } else { |
691 | + p.log.Errorf("RequestWakeup got %v", err) |
692 | + t = time.Time{} |
693 | + cookie = "" |
694 | + } |
695 | + return t, cookie, err |
696 | +} |
697 | + |
698 | +func (p *poller) control(wakeupCh <-chan bool, filteredWakeUpCh chan<- bool, flightMode bool, flightModeCh <-chan bool, wirelessEnabled bool, wirelessEnabledCh <-chan bool) { |
699 | + dontPoll := flightMode && !wirelessEnabled |
700 | + var t time.Time |
701 | + cookie := "" |
702 | + holdsWakeLock := false |
703 | + for { |
704 | + select { |
705 | + case holdsWakeLock = <-p.holdsWakeLockCh: |
706 | + case <-p.requestWakeupCh: |
707 | + if !t.IsZero() || dontPoll { |
708 | + // earlier wakeup or we shouldn't be polling |
709 | + // => don't request wakeup |
710 | + if dontPoll { |
711 | + p.log.Debugf("skip requesting wakeup") |
712 | + } |
713 | + p.requestedWakeupErrCh <- nil |
714 | + break |
715 | + } |
716 | + var err error |
717 | + t, cookie, err = p.doRequestWakeup(p.times.AlarmInterval) |
718 | + p.requestedWakeupErrCh <- err |
719 | + case b := <-wakeupCh: |
720 | + // seems we get here also on clear wakeup, oh well |
721 | + if !b { |
722 | + panic("WatchWakeups channel produced a false value (??)") |
723 | + } |
724 | + // the channel will produce a true for every |
725 | + // wakeup, not only the one we asked for |
726 | + now := time.Now() |
727 | + if t.IsZero() { |
728 | + p.log.Debugf("got woken up; time is %s", now) |
729 | + } else { |
730 | + p.log.Debugf("got woken up; time is %s (𝛥: %s)", now, now.Sub(t)) |
731 | + if !now.Before(t) { |
732 | + t = time.Time{} |
733 | + filteredWakeUpCh <- true |
734 | + } |
735 | + } |
736 | + case flightMode = <-flightModeCh: |
737 | + case wirelessEnabled = <-wirelessEnabledCh: |
738 | + } |
739 | + newDontPoll := flightMode && !wirelessEnabled |
740 | + p.log.Debugf("control: flightMode:%v wirelessEnabled:%v prevDontPoll:%v dontPoll:%v wakeupReq:%v holdsWakeLock:%v", flightMode, wirelessEnabled, dontPoll, newDontPoll, !t.IsZero(), holdsWakeLock) |
741 | + if newDontPoll != dontPoll { |
742 | + if dontPoll = newDontPoll; dontPoll { |
743 | + if !t.IsZero() { |
744 | + err := p.powerd.ClearWakeup(cookie) |
745 | + if err == nil { |
746 | + // cleared |
747 | + t = time.Time{} |
748 | + p.log.Debugf("cleared wakeup") |
749 | + } else { |
750 | + p.log.Errorf("ClearWakeup got %v", err) |
751 | + } |
752 | + } |
753 | + } else { |
754 | + if t.IsZero() && !holdsWakeLock { |
755 | + // reschedule soon |
756 | + t, cookie, _ = p.doRequestWakeup(p.times.NetworkWait / 20) |
757 | + } |
758 | + } |
759 | + } |
760 | + } |
761 | +} |
762 | + |
763 | +func (p *poller) requestWakeup() error { |
764 | + p.requestWakeupCh <- struct{}{} |
765 | + return <-p.requestedWakeupErrCh |
766 | +} |
767 | + |
768 | +func (p *poller) holdsWakeLock(has bool) { |
769 | + p.holdsWakeLockCh <- has |
770 | +} |
771 | + |
772 | func (p *poller) run(wakeupCh <-chan bool, doneCh <-chan bool) { |
773 | var lockCookie string |
774 | |
775 | @@ -143,15 +282,13 @@ |
776 | |
777 | func (p *poller) step(wakeupCh <-chan bool, doneCh <-chan bool, lockCookie string) string { |
778 | |
779 | - t := time.Now().Add(p.times.AlarmInterval).Truncate(time.Second) |
780 | - _, err := p.powerd.RequestWakeup("ubuntu push client", t) |
781 | + err := p.requestWakeup() |
782 | if err != nil { |
783 | - p.log.Errorf("RequestWakeup got %v", err) |
784 | // Don't do this too quickly. Pretend we are just skipping one wakeup |
785 | time.Sleep(p.times.AlarmInterval) |
786 | return lockCookie |
787 | } |
788 | - p.log.Debugf("requested wakeup at %s", t) |
789 | + p.holdsWakeLock(false) |
790 | if lockCookie != "" { |
791 | if err := p.powerd.ClearWakelock(lockCookie); err != nil { |
792 | p.log.Errorf("ClearWakelock(%#v) got %v", lockCookie, err) |
793 | @@ -160,26 +297,15 @@ |
794 | } |
795 | lockCookie = "" |
796 | } |
797 | - for b := range wakeupCh { |
798 | - if !b { |
799 | - panic("WatchWakeups channel produced a false value (??)") |
800 | - } |
801 | - // the channel will produce a true for every |
802 | - // wakeup, not only the one we asked for |
803 | - now := time.Now() |
804 | - p.log.Debugf("got woken up; time is %s (𝛥: %s)", now, now.Sub(t)) |
805 | - if !now.Before(t) { |
806 | - break |
807 | - } |
808 | - } |
809 | + <-wakeupCh |
810 | lockCookie, err = p.powerd.RequestWakelock("ubuntu push client") |
811 | if err != nil { |
812 | p.log.Errorf("RequestWakelock got %v", err) |
813 | return lockCookie |
814 | } |
815 | + p.holdsWakeLock(true) |
816 | p.log.Debugf("got wakelock cookie of %s, checking conn state", lockCookie) |
817 | - // XXX killed as part of bug #1435109 troubleshooting, remove cfg if remains unused |
818 | - // time.Sleep(p.times.SessionStateSettle) |
819 | + time.Sleep(p.times.SessionStateSettle) |
820 | for i := 0; i < 20; i++ { |
821 | if p.IsConnected() { |
822 | p.log.Debugf("iter %02d: connected", i) |
823 | |
824 | === modified file 'poller/poller_test.go' |
825 | --- poller/poller_test.go 2014-08-21 10:56:12 +0000 |
826 | +++ poller/poller_test.go 2015-03-31 09:33:32 +0000 |
827 | @@ -1,5 +1,5 @@ |
828 | /* |
829 | - Copyright 2014 Canonical Ltd. |
830 | + Copyright 2014-2015 Canonical Ltd. |
831 | |
832 | This program is free software: you can redistribute it and/or modify it |
833 | under the terms of the GNU General Public License version 3, as published |
834 | @@ -42,7 +42,7 @@ |
835 | reqWakeCookie string |
836 | reqWakeErr error |
837 | // WatchWakeups |
838 | - watchWakeCh <-chan bool |
839 | + watchWakeCh chan bool |
840 | watchWakeErr error |
841 | // RequestWakelock |
842 | reqLockName string |
843 | @@ -63,6 +63,9 @@ |
844 | func (m *myD) RequestWakeup(name string, wakeupTime time.Time) (string, error) { |
845 | m.reqWakeName = name |
846 | m.reqWakeTime = wakeupTime |
847 | + time.AfterFunc(100*time.Millisecond, func() { |
848 | + m.watchWakeCh <- true |
849 | + }) |
850 | return m.reqWakeCookie, m.reqWakeErr |
851 | } |
852 | func (m *myD) RequestWakelock(name string) (string, error) { |
853 | @@ -73,7 +76,10 @@ |
854 | m.clearLockCookie = cookie |
855 | return m.clearLockErr |
856 | } |
857 | -func (m *myD) ClearWakeup(cookie string) error { panic("clearwakeup called??") } |
858 | +func (m *myD) ClearWakeup(cookie string) error { |
859 | + m.watchWakeCh <- false |
860 | + return nil |
861 | +} |
862 | func (m *myD) WatchWakeups() (<-chan bool, error) { return m.watchWakeCh, m.watchWakeErr } |
863 | func (m *myD) Poll() error { return m.pollErr } |
864 | func (m *myD) WatchDones() (<-chan bool, error) { return m.watchDonesCh, m.watchDonesErr } |
865 | @@ -86,23 +92,27 @@ |
866 | |
867 | func (s *PrSuite) TestStep(c *C) { |
868 | p := &poller{ |
869 | - times: Times{}, |
870 | - log: s.log, |
871 | - powerd: s.myd, |
872 | - polld: s.myd, |
873 | - sessionState: s.myd, |
874 | + times: Times{}, |
875 | + log: s.log, |
876 | + powerd: s.myd, |
877 | + polld: s.myd, |
878 | + sessionState: s.myd, |
879 | + requestWakeupCh: make(chan struct{}), |
880 | + requestedWakeupErrCh: make(chan error), |
881 | + holdsWakeLockCh: make(chan bool), |
882 | } |
883 | s.myd.reqLockCookie = "wakelock cookie" |
884 | s.myd.stateState = session.Running |
885 | - // we'll get the wakeup right away |
886 | wakeupCh := make(chan bool, 1) |
887 | - wakeupCh <- true |
888 | + s.myd.watchWakeCh = wakeupCh |
889 | // we won't get the "done" signal in time ;) |
890 | doneCh := make(chan bool) |
891 | // and a channel to get the return value from a goroutine |
892 | ch := make(chan string) |
893 | // now, run |
894 | - go func() { ch <- p.step(wakeupCh, doneCh, "old cookie") }() |
895 | + filteredWakeUpCh := make(chan bool) |
896 | + go p.control(wakeupCh, filteredWakeUpCh, false, nil, true, nil) |
897 | + go func() { ch <- p.step(filteredWakeUpCh, doneCh, "old cookie") }() |
898 | select { |
899 | case s := <-ch: |
900 | c.Check(s, Equals, "wakelock cookie") |
901 | @@ -112,3 +122,53 @@ |
902 | // check we cleared the old cookie |
903 | c.Check(s.myd.clearLockCookie, Equals, "old cookie") |
904 | } |
905 | + |
906 | +func (s *PrSuite) TestControl(c *C) { |
907 | + p := &poller{ |
908 | + times: Times{}, |
909 | + log: s.log, |
910 | + powerd: s.myd, |
911 | + polld: s.myd, |
912 | + sessionState: s.myd, |
913 | + requestWakeupCh: make(chan struct{}), |
914 | + requestedWakeupErrCh: make(chan error), |
915 | + holdsWakeLockCh: make(chan bool), |
916 | + } |
917 | + wakeUpCh := make(chan bool) |
918 | + filteredWakeUpCh := make(chan bool) |
919 | + s.myd.watchWakeCh = make(chan bool, 1) |
920 | + flightModeCh := make(chan bool) |
921 | + wirelessModeCh := make(chan bool) |
922 | + go p.control(wakeUpCh, filteredWakeUpCh, false, flightModeCh, true, wirelessModeCh) |
923 | + |
924 | + // works |
925 | + err := p.requestWakeup() |
926 | + c.Assert(err, IsNil) |
927 | + c.Check(<-s.myd.watchWakeCh, Equals, true) |
928 | + |
929 | + // there's a wakeup already |
930 | + err = p.requestWakeup() |
931 | + c.Assert(err, IsNil) |
932 | + c.Check(s.myd.watchWakeCh, HasLen, 0) |
933 | + |
934 | + // wakeup happens |
935 | + wakeUpCh <- true |
936 | + <-filteredWakeUpCh |
937 | + |
938 | + // flight mode |
939 | + flightModeCh <- true |
940 | + wirelessModeCh <- false |
941 | + err = p.requestWakeup() |
942 | + c.Assert(err, IsNil) |
943 | + c.Check(s.myd.watchWakeCh, HasLen, 0) |
944 | + |
945 | + // wireless on |
946 | + wirelessModeCh <- true |
947 | + c.Check(<-s.myd.watchWakeCh, Equals, true) |
948 | + |
949 | + // wireless off |
950 | + wirelessModeCh <- false |
951 | + // pending wakeup was cleared |
952 | + c.Check(<-s.myd.watchWakeCh, Equals, false) |
953 | + |
954 | +} |