Merge lp:~ralsina/ubuntu-push/merge-automatic into lp:ubuntu-push
- merge-automatic
- Merge into trunk
Proposed by
Roberto Alsina
Status: | Merged |
---|---|
Approved by: | Roberto Alsina |
Approved revision: | no longer in the source branch. |
Merged at revision: | 121 |
Proposed branch: | lp:~ralsina/ubuntu-push/merge-automatic |
Merge into: | lp:ubuntu-push |
Diff against target: |
2235 lines (+1306/-207) 28 files modified
bus/notifications/raw.go (+1/-1) bus/notifications/raw_test.go (+13/-0) bus/polld/polld.go (+68/-0) bus/polld/polld_test.go (+86/-0) bus/powerd/powerd.go (+101/-0) bus/powerd/powerd_test.go (+161/-0) click/click.go (+13/-0) click/click_test.go (+18/-0) client/client.go (+39/-3) client/client_test.go (+56/-13) debian/changelog (+16/-1) debian/config.json (+6/-1) dependencies.tsv (+1/-1) launch_helper/kindpool.go (+1/-1) launch_helper/kindpool_test.go (+10/-3) launch_helper/legacy/legacy.go (+36/-18) launch_helper/legacy/legacy_test.go (+33/-4) poller/poller.go (+220/-0) poller/poller_test.go (+114/-0) scripts/noisy-helper.sh (+7/-0) server/acceptance/acceptanceclient.go (+15/-16) server/acceptance/cmd/acceptanceclient.go (+54/-0) server/acceptance/kit/api.go (+82/-0) server/acceptance/kit/cliloop.go (+75/-52) server/acceptance/kit/helpers.go (+46/-0) server/acceptance/suites/broadcast.go (+14/-27) server/acceptance/suites/suite.go (+5/-40) server/acceptance/suites/unicast.go (+15/-26) |
To merge this branch: | bzr merge lp:~ralsina/ubuntu-push/merge-automatic |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Push Hackers | Pending | ||
Review via email: mp+231942@code.launchpad.net |
Commit message
Description of the change
Merge changes from ubuntu-push automatic branch:
* Avoid rare race in kindpool_test.go
* Interface with account-polld's dbus api.
* Powerd integration.
* Use symbolic icon for secondary icon in notification.
* Log legacy helper failures.
To post a comment you must log in.
- 121. By PS Jenkins bot
-
* Avoid rare race in kindpool_test.go
* Interface with account-polld's dbus api.
* Powerd integration.
* Use symbolic icon for secondary icon in notification.
* Log legacy helper failures.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bus/notifications/raw.go' |
2 | --- bus/notifications/raw.go 2014-07-27 00:58:08 +0000 |
3 | +++ bus/notifications/raw.go 2014-08-25 16:38:20 +0000 |
4 | @@ -144,7 +144,7 @@ |
5 | } |
6 | |
7 | hints := make(map[string]*dbus.Variant) |
8 | - hints["x-canonical-secondary-icon"] = &dbus.Variant{app.Icon()} |
9 | + hints["x-canonical-secondary-icon"] = &dbus.Variant{app.SymbolicIcon()} |
10 | |
11 | appId := app.Original() |
12 | actions := make([]string, 2*len(card.Actions)) |
13 | |
14 | === modified file 'bus/notifications/raw_test.go' |
15 | --- bus/notifications/raw_test.go 2014-07-27 00:58:08 +0000 |
16 | +++ bus/notifications/raw_test.go 2014-08-25 16:38:20 +0000 |
17 | @@ -210,6 +210,19 @@ |
18 | c.Check(hints["x-canonical-secondary-icon"], NotNil) |
19 | } |
20 | |
21 | +func (s *RawSuite) TestPresentUsesSymbolic(c *C) { |
22 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
23 | + raw := Raw(endp, s.log) |
24 | + worked := raw.Present(s.app, "notifId", &launch_helper.Notification{Card: &launch_helper.Card{Summary: "summary", Popup: true}}) |
25 | + c.Assert(worked, Equals, true) |
26 | + callArgs := testibus.GetCallArgs(endp) |
27 | + c.Assert(callArgs, HasLen, 1) |
28 | + c.Assert(callArgs[0].Args, HasLen, 8) |
29 | + hints, ok := callArgs[0].Args[6].(map[string]*dbus.Variant) |
30 | + c.Assert(ok, Equals, true) |
31 | + c.Check(hints["x-canonical-secondary-icon"].Value.(string), Equals, "-symbolic") |
32 | +} |
33 | + |
34 | func (s *RawSuite) TestPresentNoNotificationPanics(c *C) { |
35 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1)) |
36 | raw := Raw(endp, s.log) |
37 | |
38 | === added directory 'bus/polld' |
39 | === added file 'bus/polld/polld.go' |
40 | --- bus/polld/polld.go 1970-01-01 00:00:00 +0000 |
41 | +++ bus/polld/polld.go 2014-08-25 16:38:20 +0000 |
42 | @@ -0,0 +1,68 @@ |
43 | +/* |
44 | + Copyright 2014 Canonical Ltd. |
45 | + |
46 | + This program is free software: you can redistribute it and/or modify it |
47 | + under the terms of the GNU General Public License version 3, as published |
48 | + by the Free Software Foundation. |
49 | + |
50 | + This program is distributed in the hope that it will be useful, but |
51 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
52 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
53 | + PURPOSE. See the GNU General Public License for more details. |
54 | + |
55 | + You should have received a copy of the GNU General Public License along |
56 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
57 | +*/ |
58 | + |
59 | +// Package polld wraps the account-polld dbus interface |
60 | +package polld |
61 | + |
62 | +import ( |
63 | + "errors" |
64 | + |
65 | + "launchpad.net/ubuntu-push/bus" |
66 | + "launchpad.net/ubuntu-push/logger" |
67 | +) |
68 | + |
69 | +var ( |
70 | + ErrUnconfigured = errors.New("unconfigured.") |
71 | +) |
72 | + |
73 | +// polld lives on a well-known bus.Address |
74 | +var BusAddress bus.Address = bus.Address{ |
75 | + Interface: "com.ubuntu.AccountPolld", |
76 | + Path: "/com/ubuntu/AccountPolld", |
77 | + Name: "com.ubuntu.AccountPolld", |
78 | +} |
79 | + |
80 | +type Polld interface { |
81 | + Poll() error |
82 | + WatchDones() (<-chan bool, error) |
83 | +} |
84 | + |
85 | +type polld struct { |
86 | + endp bus.Endpoint |
87 | + log logger.Logger |
88 | +} |
89 | + |
90 | +func New(endp bus.Endpoint, log logger.Logger) Polld { |
91 | + return &polld{endp, log} |
92 | +} |
93 | + |
94 | +func (p *polld) Poll() error { |
95 | + if p.endp == nil { |
96 | + return ErrUnconfigured |
97 | + } |
98 | + return p.endp.Call("Poll", nil) |
99 | +} |
100 | + |
101 | +func (p *polld) WatchDones() (<-chan bool, error) { |
102 | + if p.endp == nil { |
103 | + return nil, ErrUnconfigured |
104 | + } |
105 | + ch := make(chan bool) |
106 | + p.endp.WatchSignal("Done", func(...interface{}) { |
107 | + ch <- true |
108 | + }, func() { close(ch) }) |
109 | + return ch, nil |
110 | +} |
111 | |
112 | === added file 'bus/polld/polld_test.go' |
113 | --- bus/polld/polld_test.go 1970-01-01 00:00:00 +0000 |
114 | +++ bus/polld/polld_test.go 2014-08-25 16:38:20 +0000 |
115 | @@ -0,0 +1,86 @@ |
116 | +/* |
117 | + Copyright 2014 Canonical Ltd. |
118 | + |
119 | + This program is free software: you can redistribute it and/or modify it |
120 | + under the terms of the GNU General Public License version 3, as published |
121 | + by the Free Software Foundation. |
122 | + |
123 | + This program is distributed in the hope that it will be useful, but |
124 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
125 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
126 | + PURPOSE. See the GNU General Public License for more details. |
127 | + |
128 | + You should have received a copy of the GNU General Public License along |
129 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
130 | +*/ |
131 | + |
132 | +package polld |
133 | + |
134 | +import ( |
135 | + "testing" |
136 | + "time" |
137 | + |
138 | + . "launchpad.net/gocheck" |
139 | + |
140 | + testibus "launchpad.net/ubuntu-push/bus/testing" |
141 | + helpers "launchpad.net/ubuntu-push/testing" |
142 | + "launchpad.net/ubuntu-push/testing/condition" |
143 | +) |
144 | + |
145 | +// hook up gocheck |
146 | +func TestPolld(t *testing.T) { TestingT(t) } |
147 | + |
148 | +type PdSuite struct { |
149 | + log *helpers.TestLogger |
150 | +} |
151 | + |
152 | +var _ = Suite(&PdSuite{}) |
153 | + |
154 | +func (s *PdSuite) SetUpTest(c *C) { |
155 | + s.log = helpers.NewTestLogger(c, "debug") |
156 | +} |
157 | + |
158 | +func (s *PdSuite) TestPollWorks(c *C) { |
159 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(true)) |
160 | + pd := New(endp, s.log) |
161 | + err := pd.Poll() |
162 | + c.Assert(err, IsNil) |
163 | + args := testibus.GetCallArgs(endp) |
164 | + c.Assert(args, HasLen, 1) |
165 | + c.Check(args[0].Member, Equals, "Poll") |
166 | + c.Check(args[0].Args, IsNil) |
167 | +} |
168 | + |
169 | +func (s *PdSuite) TestPollUnconfigured(c *C) { |
170 | + c.Check(new(polld).Poll(), Equals, ErrUnconfigured) |
171 | +} |
172 | + |
173 | +func (s *PdSuite) TestPollFails(c *C) { |
174 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) |
175 | + pd := New(endp, s.log) |
176 | + c.Check(pd.Poll(), NotNil) |
177 | +} |
178 | + |
179 | +func (s *PdSuite) TestWatchDonesWorks(c *C) { |
180 | + endp := testibus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{}) |
181 | + pd := New(endp, s.log) |
182 | + ch, err := pd.WatchDones() |
183 | + c.Assert(err, IsNil) |
184 | + select { |
185 | + case b := <-ch: |
186 | + c.Check(b, Equals, true) |
187 | + case <-time.After(100 * time.Millisecond): |
188 | + c.Error("timeout waiting for bool") |
189 | + } |
190 | + select { |
191 | + case b := <-ch: |
192 | + c.Check(b, Equals, false) |
193 | + case <-time.After(100 * time.Millisecond): |
194 | + c.Error("timeout waiting for close") |
195 | + } |
196 | +} |
197 | + |
198 | +func (s *PdSuite) TestWatchDonesUnconfigured(c *C) { |
199 | + _, err := new(polld).WatchDones() |
200 | + c.Check(err, Equals, ErrUnconfigured) |
201 | +} |
202 | |
203 | === added directory 'bus/powerd' |
204 | === added file 'bus/powerd/powerd.go' |
205 | --- bus/powerd/powerd.go 1970-01-01 00:00:00 +0000 |
206 | +++ bus/powerd/powerd.go 2014-08-25 16:38:20 +0000 |
207 | @@ -0,0 +1,101 @@ |
208 | +/* |
209 | + Copyright 2014 Canonical Ltd. |
210 | + |
211 | + This program is free software: you can redistribute it and/or modify it |
212 | + under the terms of the GNU General Public License version 3, as published |
213 | + by the Free Software Foundation. |
214 | + |
215 | + This program is distributed in the hope that it will be useful, but |
216 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
217 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
218 | + PURPOSE. See the GNU General Public License for more details. |
219 | + |
220 | + You should have received a copy of the GNU General Public License along |
221 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
222 | +*/ |
223 | + |
224 | +// Package powerd is an interface to powerd via dbus. |
225 | +package powerd |
226 | + |
227 | +import ( |
228 | + "errors" |
229 | + "time" |
230 | + |
231 | + "launchpad.net/ubuntu-push/bus" |
232 | + "launchpad.net/ubuntu-push/logger" |
233 | +) |
234 | + |
235 | +// powerd lives on a well-known bus.Address |
236 | +var BusAddress bus.Address = bus.Address{ |
237 | + Interface: "com.canonical.powerd", |
238 | + Path: "/com/canonical/powerd", |
239 | + Name: "com.canonical.powerd", |
240 | +} |
241 | + |
242 | +// Powerd exposes a subset of powerd |
243 | +type Powerd interface { |
244 | + RequestWakeup(name string, wakeupTime time.Time) (string, error) |
245 | + ClearWakeup(cookie string) error |
246 | + WatchWakeups() (<-chan bool, error) |
247 | + RequestWakelock(name string) (string, error) |
248 | + ClearWakelock(cookie string) error |
249 | +} |
250 | + |
251 | +type powerd struct { |
252 | + endp bus.Endpoint |
253 | + log logger.Logger |
254 | +} |
255 | + |
256 | +var ( |
257 | + ErrUnconfigured = errors.New("unconfigured.") |
258 | +) |
259 | + |
260 | +// New builds a new Powerd that uses the provided bus.Endpoint |
261 | +func New(endp bus.Endpoint, log logger.Logger) Powerd { |
262 | + return &powerd{endp, log} |
263 | +} |
264 | + |
265 | +func (p *powerd) RequestWakeup(name string, wakeupTime time.Time) (string, error) { |
266 | + if p.endp == nil { |
267 | + return "", ErrUnconfigured |
268 | + } |
269 | + var res string |
270 | + err := p.endp.Call("requestWakeup", bus.Args(name, uint64(wakeupTime.Unix())), &res) |
271 | + return res, err |
272 | +} |
273 | + |
274 | +func (p *powerd) ClearWakeup(cookie string) error { |
275 | + if p.endp == nil { |
276 | + return ErrUnconfigured |
277 | + } |
278 | + return p.endp.Call("clearWakeup", bus.Args(cookie)) |
279 | +} |
280 | + |
281 | +func (p *powerd) WatchWakeups() (<-chan bool, error) { |
282 | + if p.endp == nil { |
283 | + return nil, ErrUnconfigured |
284 | + } |
285 | + ch := make(chan bool) |
286 | + p.endp.WatchSignal("Wakeup", func(...interface{}) { |
287 | + ch <- true |
288 | + }, func() { close(ch) }) |
289 | + return ch, nil |
290 | +} |
291 | + |
292 | +func (p *powerd) RequestWakelock(name string) (string, error) { |
293 | + // wakelocks are documented on https://wiki.ubuntu.com/powerd#API |
294 | + // (requestSysState with state=1) |
295 | + if p.endp == nil { |
296 | + return "", ErrUnconfigured |
297 | + } |
298 | + var res string |
299 | + err := p.endp.Call("requestSysState", bus.Args(name, int32(1)), &res) |
300 | + return res, err |
301 | +} |
302 | + |
303 | +func (p *powerd) ClearWakelock(cookie string) error { |
304 | + if p.endp == nil { |
305 | + return ErrUnconfigured |
306 | + } |
307 | + return p.endp.Call("clearSysState", bus.Args(cookie)) |
308 | +} |
309 | |
310 | === added file 'bus/powerd/powerd_test.go' |
311 | --- bus/powerd/powerd_test.go 1970-01-01 00:00:00 +0000 |
312 | +++ bus/powerd/powerd_test.go 2014-08-25 16:38:20 +0000 |
313 | @@ -0,0 +1,161 @@ |
314 | +/* |
315 | + Copyright 2014 Canonical Ltd. |
316 | + |
317 | + This program is free software: you can redistribute it and/or modify it |
318 | + under the terms of the GNU General Public License version 3, as published |
319 | + by the Free Software Foundation. |
320 | + |
321 | + This program is distributed in the hope that it will be useful, but |
322 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
323 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
324 | + PURPOSE. See the GNU General Public License for more details. |
325 | + |
326 | + You should have received a copy of the GNU General Public License along |
327 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
328 | +*/ |
329 | + |
330 | +package powerd |
331 | + |
332 | +import ( |
333 | + "testing" |
334 | + "time" |
335 | + |
336 | + . "launchpad.net/gocheck" |
337 | + |
338 | + testibus "launchpad.net/ubuntu-push/bus/testing" |
339 | + helpers "launchpad.net/ubuntu-push/testing" |
340 | + "launchpad.net/ubuntu-push/testing/condition" |
341 | +) |
342 | + |
343 | +// hook up gocheck |
344 | +func TestPowerd(t *testing.T) { TestingT(t) } |
345 | + |
346 | +type PdSuite struct { |
347 | + log *helpers.TestLogger |
348 | +} |
349 | + |
350 | +var _ = Suite(&PdSuite{}) |
351 | + |
352 | +func (s *PdSuite) SetUpTest(c *C) { |
353 | + s.log = helpers.NewTestLogger(c, "debug") |
354 | +} |
355 | + |
356 | +func (s *PdSuite) TestRequestWakeupWorks(c *C) { |
357 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(true), "cookie") |
358 | + pd := New(endp, s.log) |
359 | + t := time.Now().Add(5 * time.Minute) |
360 | + ck, err := pd.RequestWakeup("name", t) |
361 | + c.Assert(err, IsNil) |
362 | + c.Check(ck, Equals, "cookie") |
363 | + args := testibus.GetCallArgs(endp) |
364 | + c.Assert(args, HasLen, 1) |
365 | + c.Check(args[0].Member, Equals, "requestWakeup") |
366 | + c.Check(args[0].Args, DeepEquals, []interface{}{"name", uint64(t.Unix())}) |
367 | +} |
368 | + |
369 | +func (s *PdSuite) TestRequestWakeupUnconfigured(c *C) { |
370 | + _, err := new(powerd).RequestWakeup("name", time.Now()) |
371 | + c.Assert(err, Equals, ErrUnconfigured) |
372 | +} |
373 | + |
374 | +func (s *PdSuite) TestRequestWakeupFails(c *C) { |
375 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) |
376 | + pd := New(endp, s.log) |
377 | + t := time.Now().Add(5 * time.Minute) |
378 | + _, err := pd.RequestWakeup("name", t) |
379 | + c.Assert(err, NotNil) |
380 | +} |
381 | + |
382 | +func (s *PdSuite) TestClearWakeupWorks(c *C) { |
383 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(true)) |
384 | + pd := New(endp, s.log) |
385 | + err := pd.ClearWakeup("cookie") |
386 | + c.Assert(err, IsNil) |
387 | + args := testibus.GetCallArgs(endp) |
388 | + c.Assert(args, HasLen, 1) |
389 | + c.Check(args[0].Member, Equals, "clearWakeup") |
390 | + c.Check(args[0].Args, DeepEquals, []interface{}{"cookie"}) |
391 | +} |
392 | + |
393 | +func (s *PdSuite) TestClearWakeupUnconfigured(c *C) { |
394 | + err := new(powerd).ClearWakeup("cookie") |
395 | + c.Assert(err, Equals, ErrUnconfigured) |
396 | +} |
397 | + |
398 | +func (s *PdSuite) TestClearWakeupFails(c *C) { |
399 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) |
400 | + pd := New(endp, s.log) |
401 | + err := pd.ClearWakeup("cookie") |
402 | + c.Assert(err, NotNil) |
403 | +} |
404 | + |
405 | +func (s *PdSuite) TestRequestWakelockWorks(c *C) { |
406 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(true), "cookie") |
407 | + pd := New(endp, s.log) |
408 | + ck, err := pd.RequestWakelock("name") |
409 | + c.Assert(err, IsNil) |
410 | + c.Check(ck, Equals, "cookie") |
411 | + args := testibus.GetCallArgs(endp) |
412 | + c.Assert(args, HasLen, 1) |
413 | + // wakelocks are documented on https://wiki.ubuntu.com/powerd#API |
414 | + c.Check(args[0].Member, Equals, "requestSysState") |
415 | + c.Check(args[0].Args, DeepEquals, []interface{}{"name", int32(1)}) |
416 | +} |
417 | + |
418 | +func (s *PdSuite) TestRequestWakelockUnconfigured(c *C) { |
419 | + _, err := new(powerd).RequestWakelock("name") |
420 | + c.Assert(err, Equals, ErrUnconfigured) |
421 | +} |
422 | + |
423 | +func (s *PdSuite) TestRequestWakelockFails(c *C) { |
424 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) |
425 | + pd := New(endp, s.log) |
426 | + _, err := pd.RequestWakelock("name") |
427 | + c.Assert(err, NotNil) |
428 | +} |
429 | + |
430 | +func (s *PdSuite) TestClearWakelockWorks(c *C) { |
431 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(true)) |
432 | + pd := New(endp, s.log) |
433 | + err := pd.ClearWakelock("cookie") |
434 | + c.Assert(err, IsNil) |
435 | + args := testibus.GetCallArgs(endp) |
436 | + c.Assert(args, HasLen, 1) |
437 | + c.Check(args[0].Member, Equals, "clearSysState") |
438 | + c.Check(args[0].Args, DeepEquals, []interface{}{"cookie"}) |
439 | +} |
440 | + |
441 | +func (s *PdSuite) TestClearWakelockUnconfigured(c *C) { |
442 | + c.Check(new(powerd).ClearWakelock("cookie"), NotNil) |
443 | +} |
444 | + |
445 | +func (s *PdSuite) TestClearWakelockFails(c *C) { |
446 | + endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) |
447 | + pd := New(endp, s.log) |
448 | + err := pd.ClearWakelock("cookie") |
449 | + c.Assert(err, NotNil) |
450 | +} |
451 | + |
452 | +func (s *PdSuite) TestWatchWakeupsWorks(c *C) { |
453 | + endp := testibus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{}) |
454 | + pd := New(endp, s.log) |
455 | + ch, err := pd.WatchWakeups() |
456 | + c.Assert(err, IsNil) |
457 | + select { |
458 | + case b := <-ch: |
459 | + c.Check(b, Equals, true) |
460 | + case <-time.After(100 * time.Millisecond): |
461 | + c.Error("timeout waiting for bool") |
462 | + } |
463 | + select { |
464 | + case b := <-ch: |
465 | + c.Check(b, Equals, false) |
466 | + case <-time.After(100 * time.Millisecond): |
467 | + c.Error("timeout waiting for close") |
468 | + } |
469 | +} |
470 | + |
471 | +func (s *PdSuite) TestWatchWakeupsUnconfigured(c *C) { |
472 | + _, err := new(powerd).WatchWakeups() |
473 | + c.Check(err, Equals, ErrUnconfigured) |
474 | +} |
475 | |
476 | === modified file 'click/click.go' |
477 | --- click/click.go 2014-08-06 02:44:06 +0000 |
478 | +++ click/click.go 2014-08-25 16:38:20 +0000 |
479 | @@ -136,6 +136,19 @@ |
480 | return cappinfo.AppIconFromDesktopId(app.DesktopId()) |
481 | } |
482 | |
483 | +func _symbolic(icon string) string { |
484 | + if strings.ContainsRune(icon, '/') { |
485 | + return icon |
486 | + } |
487 | + return icon + "-symbolic" |
488 | +} |
489 | + |
490 | +var symbolic = _symbolic |
491 | + |
492 | +func (app *AppId) SymbolicIcon() string { |
493 | + return symbolic(app.Icon()) |
494 | +} |
495 | + |
496 | func (app *AppId) MarshalJSON() ([]byte, error) { |
497 | return json.Marshal(app.Original()) |
498 | } |
499 | |
500 | === modified file 'click/click_test.go' |
501 | --- click/click_test.go 2014-08-06 03:50:14 +0000 |
502 | +++ click/click_test.go 2014-08-25 16:38:20 +0000 |
503 | @@ -182,3 +182,21 @@ |
504 | c.Check(app, NotNil) |
505 | c.Check(app.Original(), Equals, "_non-existent-app") |
506 | } |
507 | + |
508 | +func (s *clickSuite) TestSymbolicAppendsSymbolicIfIconIsName(c *C) { |
509 | + symb := symbolic("foo") |
510 | + c.Check(symb, Equals, "foo-symbolic") |
511 | +} |
512 | + |
513 | +func (s *clickSuite) TestSymbolicLeavesAloneIfIconIsPath(c *C) { |
514 | + symb := symbolic("foo/bar") |
515 | + c.Check(symb, Equals, "foo/bar") |
516 | +} |
517 | + |
518 | +func (s *clickSuite) TestSymbolicIconCallsSymbolic(c *C) { |
519 | + symbolic = func(string) string { return "xyzzy" } |
520 | + defer func() { symbolic = _symbolic }() |
521 | + app, err := ParseAppId("_python3.4") |
522 | + c.Assert(err, IsNil) |
523 | + c.Check(app.SymbolicIcon(), Equals, "xyzzy") |
524 | +} |
525 | |
526 | === modified file 'client/client.go' |
527 | --- client/client.go 2014-08-12 00:32:32 +0000 |
528 | +++ client/client.go 2014-08-25 16:38:20 +0000 |
529 | @@ -44,6 +44,7 @@ |
530 | "launchpad.net/ubuntu-push/identifier" |
531 | "launchpad.net/ubuntu-push/launch_helper" |
532 | "launchpad.net/ubuntu-push/logger" |
533 | + "launchpad.net/ubuntu-push/poller" |
534 | "launchpad.net/ubuntu-push/protocol" |
535 | "launchpad.net/ubuntu-push/util" |
536 | ) |
537 | @@ -71,6 +72,12 @@ |
538 | // fallback values for simplified notification usage |
539 | FallbackVibration *launch_helper.Vibration `json:"fallback_vibration"` |
540 | FallbackSound string `json:"fallback_sound"` |
541 | + // times for the poller |
542 | + PollInterval config.ConfigTimeDuration `json:"poll_interval"` |
543 | + PollSettle config.ConfigTimeDuration `json:"poll_settle"` |
544 | + PollNetworkWait config.ConfigTimeDuration `json:"poll_net_wait"` |
545 | + PollPolldWait config.ConfigTimeDuration `json:"poll_polld_wait"` |
546 | + PollDoneWait config.ConfigTimeDuration `json:"poll_done_wait"` |
547 | } |
548 | |
549 | // PushService is the interface we use of service.PushService. |
550 | @@ -115,6 +122,7 @@ |
551 | unregisterCh chan *click.AppId |
552 | trackAddressees map[string]*click.AppId |
553 | installedChecker click.InstalledChecker |
554 | + poller poller.Poller |
555 | } |
556 | |
557 | // Creates a new Ubuntu Push Notifications client-side daemon that will use |
558 | @@ -221,6 +229,21 @@ |
559 | } |
560 | } |
561 | |
562 | +// derivePollerSetup derives the Poller setup from the client configuration bits. |
563 | +func (client *PushClient) derivePollerSetup() *poller.PollerSetup { |
564 | + return &poller.PollerSetup{ |
565 | + Times: poller.Times{ |
566 | + AlarmInterval: client.config.PollInterval.TimeDuration(), |
567 | + SessionStateSettle: client.config.PollSettle.TimeDuration(), |
568 | + NetworkWait: client.config.PollNetworkWait.TimeDuration(), |
569 | + PolldWait: client.config.PollPolldWait.TimeDuration(), |
570 | + DoneWait: client.config.PollDoneWait.TimeDuration(), |
571 | + }, |
572 | + Log: client.log, |
573 | + SessionStateGetter: client.session, |
574 | + } |
575 | +} |
576 | + |
577 | // getAuthorization gets the authorization blob to send to the server |
578 | func (client *PushClient) getAuthorization(url string) string { |
579 | client.log.Debugf("getting authorization for %s", url) |
580 | @@ -266,8 +289,8 @@ |
581 | return err |
582 | } |
583 | |
584 | -// initSession creates the session object |
585 | -func (client *PushClient) initSession() error { |
586 | +// initSessionAndPoller creates the session and the poller objects |
587 | +func (client *PushClient) initSessionAndPoller() error { |
588 | info := map[string]interface{}{ |
589 | "device": client.systemImageInfo.Device, |
590 | "channel": client.systemImageInfo.Channel, |
591 | @@ -280,6 +303,18 @@ |
592 | return err |
593 | } |
594 | client.session = sess |
595 | + client.poller = poller.New(client.derivePollerSetup()) |
596 | + return nil |
597 | +} |
598 | + |
599 | +// runPoller starts and runs the poller |
600 | +func (client *PushClient) runPoller() error { |
601 | + if err := client.poller.Start(); err != nil { |
602 | + return err |
603 | + } |
604 | + if err := client.poller.Run(); err != nil { |
605 | + return err |
606 | + } |
607 | return nil |
608 | } |
609 | |
610 | @@ -509,6 +544,7 @@ |
611 | client.startPushService, |
612 | client.startPostalService, |
613 | client.takeTheBus, |
614 | - client.initSession, |
615 | + client.initSessionAndPoller, |
616 | + client.runPoller, |
617 | ) |
618 | } |
619 | |
620 | === modified file 'client/client_test.go' |
621 | --- client/client_test.go 2014-08-12 00:32:32 +0000 |
622 | +++ client/client_test.go 2014-08-25 16:38:20 +0000 |
623 | @@ -45,6 +45,7 @@ |
624 | "launchpad.net/ubuntu-push/identifier" |
625 | idtesting "launchpad.net/ubuntu-push/identifier/testing" |
626 | "launchpad.net/ubuntu-push/launch_helper" |
627 | + "launchpad.net/ubuntu-push/poller" |
628 | "launchpad.net/ubuntu-push/protocol" |
629 | helpers "launchpad.net/ubuntu-push/testing" |
630 | "launchpad.net/ubuntu-push/testing/condition" |
631 | @@ -143,7 +144,11 @@ |
632 | |
633 | func (cs *clientSuite) SetUpSuite(c *C) { |
634 | config.IgnoreParsedFlags = true // because configure() uses <flags> |
635 | - newIdentifier = func() (identifier.Id, error) { return idtesting.Settable(), nil } |
636 | + newIdentifier = func() (identifier.Id, error) { |
637 | + id := idtesting.Settable() |
638 | + id.Set("42") // must be hex of len 32 |
639 | + return id, nil |
640 | + } |
641 | cs.timeouts = util.SwapTimeouts([]time.Duration{0}) |
642 | cs.leveldbPath = "" |
643 | } |
644 | @@ -173,6 +178,11 @@ |
645 | "session_url": "xyzzy://", |
646 | "registration_url": "reg://", |
647 | "log_level": "debug", |
648 | + "poll_interval": "5m", |
649 | + "poll_settle": "20ms", |
650 | + "poll_net_wait": "1m", |
651 | + "poll_polld_wait": "3m", |
652 | + "poll_done_wait": "5s", |
653 | } |
654 | for k, v := range overrides { |
655 | cfgMap[k] = v |
656 | @@ -502,6 +512,39 @@ |
657 | } |
658 | |
659 | /***************************************************************** |
660 | + derivePollerSetup tests |
661 | +******************************************************************/ |
662 | +func (cs *clientSuite) TestDerivePollerSetup(c *C) { |
663 | + cs.writeTestConfig(map[string]interface{}{}) |
664 | + cli := NewPushClient(cs.configPath, cs.leveldbPath) |
665 | + cli.session = new(session.ClientSession) |
666 | + err := cli.configure() |
667 | + c.Assert(err, IsNil) |
668 | + expected := &poller.PollerSetup{ |
669 | + Times: poller.Times{ |
670 | + AlarmInterval: 5 * time.Minute, |
671 | + SessionStateSettle: 20 * time.Millisecond, |
672 | + NetworkWait: time.Minute, |
673 | + PolldWait: 3 * time.Minute, |
674 | + DoneWait: 5 * time.Second, |
675 | + }, |
676 | + Log: cli.log, |
677 | + SessionStateGetter: cli.session, |
678 | + } |
679 | + // sanity check that we are looking at all fields |
680 | + vExpected := reflect.ValueOf(expected).Elem() |
681 | + nf := vExpected.NumField() |
682 | + for i := 0; i < nf; i++ { |
683 | + fv := vExpected.Field(i) |
684 | + // field isn't empty/zero |
685 | + c.Assert(fv.Interface(), Not(DeepEquals), reflect.Zero(fv.Type()).Interface(), Commentf("forgot about: %s", vExpected.Type().Field(i).Name)) |
686 | + } |
687 | + // finally compare |
688 | + setup := cli.derivePollerSetup() |
689 | + c.Check(setup, DeepEquals, expected) |
690 | +} |
691 | + |
692 | +/***************************************************************** |
693 | startService tests |
694 | ******************************************************************/ |
695 | |
696 | @@ -653,7 +696,7 @@ |
697 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
698 | cli.log = cs.log |
699 | cli.systemImageInfo = siInfoRes |
700 | - c.Assert(cli.initSession(), IsNil) |
701 | + c.Assert(cli.initSessionAndPoller(), IsNil) |
702 | cs.log.ResetCapture() |
703 | cli.hasConnectivity = true |
704 | cli.handleErr(errors.New("bananas")) |
705 | @@ -686,7 +729,7 @@ |
706 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
707 | cli.log = cs.log |
708 | cli.systemImageInfo = siInfoRes |
709 | - c.Assert(cli.initSession(), IsNil) |
710 | + c.Assert(cli.initSessionAndPoller(), IsNil) |
711 | |
712 | c.Assert(cli.hasConnectivity, Equals, false) |
713 | cli.handleConnState(true) |
714 | @@ -958,7 +1001,7 @@ |
715 | cli.systemImageInfo = siInfoRes |
716 | cli.connCh = make(chan bool, 1) |
717 | cli.connCh <- true |
718 | - c.Assert(cli.initSession(), IsNil) |
719 | + c.Assert(cli.initSessionAndPoller(), IsNil) |
720 | |
721 | ch := make(chan bool, 1) |
722 | go cli.doLoop(func(bool) { ch <- true }, nopBcast, nopUcast, nopError, nopUnregister) |
723 | @@ -969,7 +1012,7 @@ |
724 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
725 | cli.log = cs.log |
726 | cli.systemImageInfo = siInfoRes |
727 | - c.Assert(cli.initSession(), IsNil) |
728 | + c.Assert(cli.initSessionAndPoller(), IsNil) |
729 | cli.session.BroadcastCh = make(chan *session.BroadcastNotification, 1) |
730 | cli.session.BroadcastCh <- &session.BroadcastNotification{} |
731 | |
732 | @@ -982,7 +1025,7 @@ |
733 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
734 | cli.log = cs.log |
735 | cli.systemImageInfo = siInfoRes |
736 | - c.Assert(cli.initSession(), IsNil) |
737 | + c.Assert(cli.initSessionAndPoller(), IsNil) |
738 | cli.session.NotificationsCh = make(chan session.AddressedNotification, 1) |
739 | cli.session.NotificationsCh <- session.AddressedNotification{} |
740 | |
741 | @@ -995,7 +1038,7 @@ |
742 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
743 | cli.log = cs.log |
744 | cli.systemImageInfo = siInfoRes |
745 | - c.Assert(cli.initSession(), IsNil) |
746 | + c.Assert(cli.initSessionAndPoller(), IsNil) |
747 | cli.session.ErrCh = make(chan error, 1) |
748 | cli.session.ErrCh <- nil |
749 | |
750 | @@ -1008,7 +1051,7 @@ |
751 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
752 | cli.log = cs.log |
753 | cli.systemImageInfo = siInfoRes |
754 | - c.Assert(cli.initSession(), IsNil) |
755 | + c.Assert(cli.initSessionAndPoller(), IsNil) |
756 | cli.unregisterCh = make(chan *click.AppId, 1) |
757 | cli.unregisterCh <- app1 |
758 | |
759 | @@ -1060,7 +1103,7 @@ |
760 | cli.postalService = d |
761 | c.Assert(cli.startPostalService(), IsNil) |
762 | |
763 | - c.Assert(cli.initSession(), IsNil) |
764 | + c.Assert(cli.initSessionAndPoller(), IsNil) |
765 | |
766 | cli.session.BroadcastCh = make(chan *session.BroadcastNotification) |
767 | cli.session.ErrCh = make(chan error) |
768 | @@ -1141,7 +1184,7 @@ |
769 | // and now everthing is better! We have a config, |
770 | c.Check(string(cli.config.Addr), Equals, ":0") |
771 | // and a device id, |
772 | - c.Check(cli.deviceId, HasLen, 32) |
773 | + c.Check(cli.deviceId, HasLen, 40) |
774 | // and a session, |
775 | c.Check(cli.session, NotNil) |
776 | // and a bus, |
777 | @@ -1161,13 +1204,13 @@ |
778 | c.Check(err, NotNil) |
779 | } |
780 | |
781 | -func (cs *clientSuite) TestInitSessionErr(c *C) { |
782 | +func (cs *clientSuite) TestinitSessionAndPollerErr(c *C) { |
783 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
784 | cli.log = cs.log |
785 | cli.systemImageInfo = siInfoRes |
786 | - // change the cli.pem value so initSession fails |
787 | + // change the cli.pem value so initSessionAndPoller fails |
788 | cli.pem = []byte("foo") |
789 | - c.Assert(cli.initSession(), NotNil) |
790 | + c.Assert(cli.initSessionAndPoller(), NotNil) |
791 | } |
792 | |
793 | /***************************************************************** |
794 | |
795 | === modified file 'debian/changelog' |
796 | --- debian/changelog 2014-08-12 02:33:16 +0000 |
797 | +++ debian/changelog 2014-08-25 16:38:20 +0000 |
798 | @@ -1,3 +1,18 @@ |
799 | +ubuntu-push (0.62) UNRELEASED; urgency=medium |
800 | + |
801 | + [ Samuele Pedroni ] |
802 | + * Avoid rare race in kindpool_test.go |
803 | + |
804 | + [ John R. Lenton] |
805 | + * Interface with account-polld's dbus api. |
806 | + * Powerd integration. |
807 | + * Use symbolic icon for secondary icon in notification. |
808 | + |
809 | + [Roberto Alsina] |
810 | + * Log legacy helper failures. |
811 | + |
812 | + -- Roberto Alsina <ralsina@yoga> Fri, 22 Aug 2014 16:00:31 -0300 |
813 | + |
814 | ubuntu-push (0.61+14.10.20140812.4-0ubuntu1) utopic; urgency=medium |
815 | |
816 | [ Guillermo Gonzalez ] |
817 | @@ -13,7 +28,7 @@ |
818 | * Make messaging menu entries show current time instead of epoch for timestamp of 0. |
819 | * Tweak the upstart script, start after unity. |
820 | * Correctly report invalid app ids, missing apps, and package/app id mismatches as separate errors over dbus. |
821 | - |
822 | + |
823 | [Roberto Alsina] |
824 | * Check that sound paths don't go up into the tree. |
825 | * Initial draft of QML-based doc |
826 | |
827 | === modified file 'debian/config.json' |
828 | --- debian/config.json 2014-08-08 09:03:42 +0000 |
829 | +++ debian/config.json 2014-08-25 16:38:20 +0000 |
830 | @@ -14,5 +14,10 @@ |
831 | "connectivity_check_md5": "4589f42e1546aa47ca181e5d949d310b", |
832 | "log_level": "debug", |
833 | "fallback_vibration": {"pattern": [100, 100], "repeat": 2}, |
834 | - "fallback_sound": "sounds/ubuntu/notifications/Slick.ogg" |
835 | + "fallback_sound": "sounds/ubuntu/notifications/Slick.ogg", |
836 | + "poll_interval": "5m", |
837 | + "poll_settle": "20ms", |
838 | + "poll_net_wait": "1m", |
839 | + "poll_polld_wait": "3m", |
840 | + "poll_done_wait": "5s" |
841 | } |
842 | |
843 | === modified file 'dependencies.tsv' |
844 | --- dependencies.tsv 2014-07-26 07:28:59 +0000 |
845 | +++ dependencies.tsv 2014-08-25 16:38:20 +0000 |
846 | @@ -1,5 +1,5 @@ |
847 | code.google.com/p/go-uuid hg 7dda39b2e7d5e265014674c5af696ba4186679e9 11 |
848 | code.google.com/p/gosqlite hg 74691fb6f83716190870cde1b658538dd4b18eb0 15 |
849 | -launchpad.net/go-dbus/v1 bzr james@jamesh.id.au-20140530132806-hpqbkxczif6o1dpz 127 |
850 | +launchpad.net/go-dbus/v1 bzr james@jamesh.id.au-20140801110939-lzfql7fk76dt6ckd 128 |
851 | launchpad.net/go-xdg/v0 bzr john.lenton@canonical.com-20140208094800-gubd5md7cro3mtxa 10 |
852 | launchpad.net/gocheck bzr gustavo@niemeyer.net-20140225173054-xu9zlkf9kxhvow02 87 |
853 | |
854 | === modified file 'launch_helper/kindpool.go' |
855 | --- launch_helper/kindpool.go 2014-07-29 15:24:01 +0000 |
856 | +++ launch_helper/kindpool.go 2014-08-25 16:38:20 +0000 |
857 | @@ -72,7 +72,7 @@ |
858 | func DefaultLaunchers(log logger.Logger) map[string]HelperLauncher { |
859 | return map[string]HelperLauncher{ |
860 | "click": cual.New(log), |
861 | - "legacy": legacy.New(), |
862 | + "legacy": legacy.New(log), |
863 | } |
864 | } |
865 | |
866 | |
867 | === modified file 'launch_helper/kindpool_test.go' |
868 | --- launch_helper/kindpool_test.go 2014-08-08 09:03:42 +0000 |
869 | +++ launch_helper/kindpool_test.go 2014-08-25 16:38:20 +0000 |
870 | @@ -102,16 +102,22 @@ |
871 | return args |
872 | } |
873 | |
874 | +func (s *poolSuite) SetUpSuite(c *C) { |
875 | + xdgCacheHome = c.MkDir |
876 | +} |
877 | + |
878 | +func (s *poolSuite) TearDownSuite(c *C) { |
879 | + xdgCacheHome = xdg.Cache.Home |
880 | +} |
881 | + |
882 | func (s *poolSuite) SetUpTest(c *C) { |
883 | s.log = helpers.NewTestLogger(c, "debug") |
884 | s.fakeLauncher = &fakeHelperLauncher{argCh: make(chan [5]string, 10)} |
885 | s.pool = NewHelperPool(map[string]HelperLauncher{"fake": s.fakeLauncher}, s.log) |
886 | - xdgCacheHome = c.MkDir |
887 | } |
888 | |
889 | func (s *poolSuite) TearDownTest(c *C) { |
890 | s.pool = nil |
891 | - xdgCacheHome = xdg.Cache.Home |
892 | } |
893 | |
894 | func (s *poolSuite) TestDefaultLaunchers(c *C) { |
895 | @@ -379,8 +385,9 @@ |
896 | } |
897 | |
898 | func (s *poolSuite) TestGetTempFilename(c *C) { |
899 | + tmpDir := c.MkDir() |
900 | GetTempDir = func(pkgName string) (string, error) { |
901 | - return c.MkDir(), nil |
902 | + return tmpDir, nil |
903 | } |
904 | // restore it when we are done |
905 | defer func() { |
906 | |
907 | === modified file 'launch_helper/legacy/legacy.go' |
908 | --- launch_helper/legacy/legacy.go 2014-07-18 20:45:21 +0000 |
909 | +++ launch_helper/legacy/legacy.go 2014-08-25 16:38:20 +0000 |
910 | @@ -18,20 +18,23 @@ |
911 | package legacy |
912 | |
913 | import ( |
914 | + "bytes" |
915 | "os" |
916 | "os/exec" |
917 | "path/filepath" |
918 | "strconv" |
919 | |
920 | "launchpad.net/ubuntu-push/click" |
921 | + "launchpad.net/ubuntu-push/logger" |
922 | ) |
923 | |
924 | type legacyHelperLauncher struct { |
925 | + log logger.Logger |
926 | done func(string) |
927 | } |
928 | |
929 | -func New() *legacyHelperLauncher { |
930 | - return new(legacyHelperLauncher) |
931 | +func New(log logger.Logger) *legacyHelperLauncher { |
932 | + return &legacyHelperLauncher{log: log} |
933 | } |
934 | |
935 | func (lhl *legacyHelperLauncher) InstallObserver(done func(string)) error { |
936 | @@ -47,27 +50,42 @@ |
937 | |
938 | func (*legacyHelperLauncher) RemoveObserver() error { return nil } |
939 | |
940 | +type msg struct { |
941 | + id string |
942 | + err error |
943 | +} |
944 | + |
945 | func (lhl *legacyHelperLauncher) Launch(_, progname, f1, f2 string) (string, error) { |
946 | - cmd := exec.Command(progname, f1, f2) |
947 | - cmd.Stdin = nil |
948 | - cmd.Stdout = nil |
949 | - cmd.Stderr = nil |
950 | + comm := make(chan msg) |
951 | |
952 | - err := cmd.Start() |
953 | - if err != nil { |
954 | - return "", err |
955 | - } |
956 | - proc := cmd.Process |
957 | - if proc == nil { |
958 | - panic("cmd.Process is nil after successful cmd.Start()??") |
959 | - } |
960 | - id := strconv.FormatInt((int64)(proc.Pid), 36) |
961 | go func() { |
962 | - proc.Wait() |
963 | + cmd := exec.Command(progname, f1, f2) |
964 | + var stdout bytes.Buffer |
965 | + cmd.Stdout = &stdout |
966 | + var stderr bytes.Buffer |
967 | + cmd.Stderr = &stderr |
968 | + err := cmd.Start() |
969 | + if err != nil { |
970 | + comm <- msg{"", err} |
971 | + return |
972 | + } |
973 | + proc := cmd.Process |
974 | + if proc == nil { |
975 | + panic("cmd.Process is nil after successful cmd.Start()??") |
976 | + } |
977 | + id := strconv.FormatInt((int64)(proc.Pid), 36) |
978 | + comm <- msg{id, nil} |
979 | + p_err := cmd.Wait() |
980 | + if p_err != nil { |
981 | + // Helper failed or got killed, log output/errors |
982 | + lhl.log.Errorf("Legacy helper failed: %v", p_err) |
983 | + lhl.log.Errorf("Legacy helper failed. Stdout: %s", stdout) |
984 | + lhl.log.Errorf("Legacy helper failed. Stderr: %s", stderr) |
985 | + } |
986 | lhl.done(id) |
987 | }() |
988 | - |
989 | - return id, nil |
990 | + msg := <-comm |
991 | + return msg.id, msg.err |
992 | } |
993 | |
994 | func (lhl *legacyHelperLauncher) Stop(_, id string) error { |
995 | |
996 | === modified file 'launch_helper/legacy/legacy_test.go' |
997 | --- launch_helper/legacy/legacy_test.go 2014-07-18 20:45:21 +0000 |
998 | +++ launch_helper/legacy/legacy_test.go 2014-08-25 16:38:20 +0000 |
999 | @@ -32,7 +32,7 @@ |
1000 | select { |
1001 | case s := <-ch: |
1002 | return s |
1003 | - case <-time.After(time.Second): |
1004 | + case <-time.After(5 * time.Second): |
1005 | c.Fatal("timed out waiting for value") |
1006 | return "" |
1007 | } |
1008 | @@ -42,12 +42,14 @@ |
1009 | |
1010 | type legacySuite struct { |
1011 | lhl *legacyHelperLauncher |
1012 | + log *helpers.TestLogger |
1013 | } |
1014 | |
1015 | var _ = Suite(&legacySuite{}) |
1016 | |
1017 | func (ls *legacySuite) SetUpTest(c *C) { |
1018 | - ls.lhl = New() |
1019 | + ls.log = helpers.NewTestLogger(c, "info") |
1020 | + ls.lhl = New(ls.log) |
1021 | } |
1022 | |
1023 | func (ls *legacySuite) TestInstallObserver(c *C) { |
1024 | @@ -94,12 +96,39 @@ |
1025 | c.Assert(err, NotNil) |
1026 | } |
1027 | |
1028 | +func (ls *legacySuite) TestHelperFails(c *C) { |
1029 | + ch := make(chan string, 1) |
1030 | + c.Assert(ls.lhl.InstallObserver(func(id string) { ch <- id }), IsNil) |
1031 | + |
1032 | + _, err := ls.lhl.Launch("", "/bin/false", "", "") |
1033 | + c.Assert(err, IsNil) |
1034 | + |
1035 | + takeNext(ch, c) |
1036 | + c.Check(ls.log.Captured(), Matches, "(?s).*Legacy helper failed.*") |
1037 | +} |
1038 | + |
1039 | +func (ls *legacySuite) TestHelperFailsLog(c *C) { |
1040 | + ch := make(chan string, 1) |
1041 | + c.Assert(ls.lhl.InstallObserver(func(id string) { ch <- id }), IsNil) |
1042 | + |
1043 | + exe := helpers.ScriptAbsPath("noisy-helper.sh") |
1044 | + _, err := ls.lhl.Launch("", exe, "", "") |
1045 | + c.Assert(err, IsNil) |
1046 | + |
1047 | + takeNext(ch, c) |
1048 | + c.Check(ls.log.Captured(), Matches, "(?s).*BOOM-1.*") |
1049 | + c.Check(ls.log.Captured(), Matches, "(?s).*BANG-1.*") |
1050 | + c.Check(ls.log.Captured(), Matches, "(?s).*BOOM-20.*") |
1051 | + c.Check(ls.log.Captured(), Matches, "(?s).*BANG-20.*") |
1052 | +} |
1053 | + |
1054 | func (ls *legacySuite) TestStop(c *C) { |
1055 | ch := make(chan string, 1) |
1056 | c.Assert(ls.lhl.InstallObserver(func(id string) { ch <- id }), IsNil) |
1057 | |
1058 | - exe := helpers.ScriptAbsPath("slow-helper.sh") |
1059 | - id, err := ls.lhl.Launch("", exe, "", "") |
1060 | + // exe := helpers.ScriptAbsPath("slow-helper.sh") |
1061 | + id, err := ls.lhl.Launch("", "/bin/sleep", "9", "1") |
1062 | + c.Assert(err, IsNil) |
1063 | |
1064 | err = ls.lhl.Stop("", "===") |
1065 | c.Check(err, NotNil) // not a valid id |
1066 | |
1067 | === added directory 'poller' |
1068 | === added file 'poller/poller.go' |
1069 | --- poller/poller.go 1970-01-01 00:00:00 +0000 |
1070 | +++ poller/poller.go 2014-08-25 16:38:20 +0000 |
1071 | @@ -0,0 +1,220 @@ |
1072 | +/* |
1073 | + Copyright 2014 Canonical Ltd. |
1074 | + |
1075 | + This program is free software: you can redistribute it and/or modify it |
1076 | + under the terms of the GNU General Public License version 3, as published |
1077 | + by the Free Software Foundation. |
1078 | + |
1079 | + This program is distributed in the hope that it will be useful, but |
1080 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
1081 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1082 | + PURPOSE. See the GNU General Public License for more details. |
1083 | + |
1084 | + You should have received a copy of the GNU General Public License along |
1085 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
1086 | +*/ |
1087 | + |
1088 | +// Package poller implements Poller, a thing that uses (hw) alarms to |
1089 | +// wake the device up from deep sleep periodically, check for |
1090 | +// notifications, and poke polld. |
1091 | +package poller |
1092 | + |
1093 | +import ( |
1094 | + "errors" |
1095 | + "sync" |
1096 | + "time" |
1097 | + |
1098 | + "launchpad.net/ubuntu-push/bus" |
1099 | + "launchpad.net/ubuntu-push/bus/polld" |
1100 | + "launchpad.net/ubuntu-push/bus/powerd" |
1101 | + "launchpad.net/ubuntu-push/client/session" |
1102 | + "launchpad.net/ubuntu-push/logger" |
1103 | + "launchpad.net/ubuntu-push/util" |
1104 | +) |
1105 | + |
1106 | +var ( |
1107 | + ErrUnconfigured = errors.New("not configured") |
1108 | + ErrAlreadyStarted = errors.New("already started") |
1109 | + ErrNotStarted = errors.New("not started") |
1110 | +) |
1111 | + |
1112 | +type stater interface { |
1113 | + State() session.ClientSessionState |
1114 | +} |
1115 | + |
1116 | +type Times struct { |
1117 | + AlarmInterval time.Duration |
1118 | + SessionStateSettle time.Duration |
1119 | + NetworkWait time.Duration |
1120 | + PolldWait time.Duration |
1121 | + DoneWait time.Duration |
1122 | +} |
1123 | + |
1124 | +type Poller interface { |
1125 | + IsConnected() bool |
1126 | + Start() error |
1127 | + Run() error |
1128 | +} |
1129 | + |
1130 | +type PollerSetup struct { |
1131 | + Times Times |
1132 | + Log logger.Logger |
1133 | + SessionStateGetter stater |
1134 | +} |
1135 | + |
1136 | +type poller struct { |
1137 | + times Times |
1138 | + log logger.Logger |
1139 | + powerd powerd.Powerd |
1140 | + polld polld.Polld |
1141 | + cookie string |
1142 | + sessionState stater |
1143 | +} |
1144 | + |
1145 | +func New(setup *PollerSetup) Poller { |
1146 | + return &poller{ |
1147 | + times: setup.Times, |
1148 | + log: setup.Log, |
1149 | + powerd: nil, |
1150 | + polld: nil, |
1151 | + sessionState: setup.SessionStateGetter, |
1152 | + } |
1153 | +} |
1154 | + |
1155 | +func (p *poller) IsConnected() bool { |
1156 | + return p.sessionState.State() == session.Running |
1157 | +} |
1158 | + |
1159 | +func (p *poller) Start() error { |
1160 | + if p.log == nil { |
1161 | + return ErrUnconfigured |
1162 | + } |
1163 | + if p.powerd != nil || p.polld != nil { |
1164 | + return ErrAlreadyStarted |
1165 | + } |
1166 | + powerdEndp := bus.SystemBus.Endpoint(powerd.BusAddress, p.log) |
1167 | + polldEndp := bus.SessionBus.Endpoint(polld.BusAddress, p.log) |
1168 | + var wg sync.WaitGroup |
1169 | + wg.Add(2) |
1170 | + go func() { |
1171 | + n := util.NewAutoRedialer(powerdEndp).Redial() |
1172 | + p.log.Debugf("powerd dialed on try %d", n) |
1173 | + wg.Done() |
1174 | + }() |
1175 | + go func() { |
1176 | + n := util.NewAutoRedialer(polldEndp).Redial() |
1177 | + p.log.Debugf("polld dialed in on try %d", n) |
1178 | + wg.Done() |
1179 | + }() |
1180 | + wg.Wait() |
1181 | + |
1182 | + p.powerd = powerd.New(powerdEndp, p.log) |
1183 | + p.polld = polld.New(polldEndp, p.log) |
1184 | + |
1185 | + return nil |
1186 | +} |
1187 | + |
1188 | +func (p *poller) Run() error { |
1189 | + if p.log == nil { |
1190 | + return ErrUnconfigured |
1191 | + } |
1192 | + if p.powerd == nil || p.polld == nil { |
1193 | + return ErrNotStarted |
1194 | + } |
1195 | + wakeupCh, err := p.powerd.WatchWakeups() |
1196 | + if err != nil { |
1197 | + return err |
1198 | + } |
1199 | + doneCh, err := p.polld.WatchDones() |
1200 | + if err != nil { |
1201 | + return err |
1202 | + } |
1203 | + go p.run(wakeupCh, doneCh) |
1204 | + return nil |
1205 | +} |
1206 | + |
1207 | +func (p *poller) run(wakeupCh <-chan bool, doneCh <-chan bool) { |
1208 | + var lockCookie string |
1209 | + |
1210 | + for { |
1211 | + lockCookie = p.step(wakeupCh, doneCh, lockCookie) |
1212 | + } |
1213 | +} |
1214 | + |
1215 | +func (p *poller) step(wakeupCh <-chan bool, doneCh <-chan bool, lockCookie string) string { |
1216 | + |
1217 | + t := time.Now().Add(p.times.AlarmInterval).Truncate(time.Second) |
1218 | + _, err := p.powerd.RequestWakeup("ubuntu push client", t) |
1219 | + if err != nil { |
1220 | + p.log.Errorf("RequestWakeup got %v", err) |
1221 | + return lockCookie |
1222 | + } |
1223 | + p.log.Debugf("requested wakeup at %s", t) |
1224 | + if lockCookie != "" { |
1225 | + if err := p.powerd.ClearWakelock(lockCookie); err != nil { |
1226 | + p.log.Errorf("ClearWakelock(%#v) got %v", lockCookie, err) |
1227 | + } else { |
1228 | + p.log.Debugf("cleared wakelock cookie %s.", lockCookie) |
1229 | + } |
1230 | + lockCookie = "" |
1231 | + } |
1232 | + for b := range wakeupCh { |
1233 | + if !b { |
1234 | + panic("WatchWakeups channel produced a false value (??)") |
1235 | + } |
1236 | + // the channel will produce a true for every |
1237 | + // wakeup, not only the one we asked for |
1238 | + now := time.Now() |
1239 | + p.log.Debugf("got woken up; time is %s", now) |
1240 | + if !now.Before(t) { |
1241 | + break |
1242 | + } |
1243 | + } |
1244 | + lockCookie, err = p.powerd.RequestWakelock("ubuntu push client") |
1245 | + if err != nil { |
1246 | + p.log.Errorf("RequestWakelock got %v", err) |
1247 | + return lockCookie |
1248 | + } |
1249 | + p.log.Debugf("got wakelock cookie of %s", lockCookie) |
1250 | + time.Sleep(p.times.SessionStateSettle) |
1251 | + for i := 0; i < 20; i++ { |
1252 | + if p.IsConnected() { |
1253 | + break |
1254 | + } |
1255 | + time.Sleep(p.times.NetworkWait / 20) |
1256 | + } |
1257 | + if !p.IsConnected() { |
1258 | + p.log.Errorf("not connected after %s; giving up", p.times.NetworkWait) |
1259 | + } else { |
1260 | + p.log.Debugf("poking polld.") |
1261 | + // drain the doneCH |
1262 | + drain: |
1263 | + for { |
1264 | + select { |
1265 | + case <-doneCh: |
1266 | + default: |
1267 | + break drain |
1268 | + } |
1269 | + } |
1270 | + |
1271 | + if err := p.polld.Poll(); err != nil { |
1272 | + p.log.Errorf("Poll got %v", err) |
1273 | + } else { |
1274 | + p.log.Debugf("waiting for polld to signal Done.") |
1275 | + select { |
1276 | + case b := <-doneCh: |
1277 | + if !b { |
1278 | + panic("WatchDones channel produced a false value (??)") |
1279 | + } |
1280 | + p.log.Debugf("polld Done.") |
1281 | + case <-time.After(p.times.PolldWait): |
1282 | + p.log.Errorf("polld still not done after %s; giving up", p.times.PolldWait) |
1283 | + } |
1284 | + } |
1285 | + |
1286 | + // XXX check whether something was actually done before waiting |
1287 | + time.Sleep(p.times.DoneWait) |
1288 | + } |
1289 | + |
1290 | + return lockCookie |
1291 | +} |
1292 | |
1293 | === added file 'poller/poller_test.go' |
1294 | --- poller/poller_test.go 1970-01-01 00:00:00 +0000 |
1295 | +++ poller/poller_test.go 2014-08-25 16:38:20 +0000 |
1296 | @@ -0,0 +1,114 @@ |
1297 | +/* |
1298 | + Copyright 2014 Canonical Ltd. |
1299 | + |
1300 | + This program is free software: you can redistribute it and/or modify it |
1301 | + under the terms of the GNU General Public License version 3, as published |
1302 | + by the Free Software Foundation. |
1303 | + |
1304 | + This program is distributed in the hope that it will be useful, but |
1305 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
1306 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1307 | + PURPOSE. See the GNU General Public License for more details. |
1308 | + |
1309 | + You should have received a copy of the GNU General Public License along |
1310 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
1311 | +*/ |
1312 | +package poller |
1313 | + |
1314 | +import ( |
1315 | + "testing" |
1316 | + "time" |
1317 | + |
1318 | + . "launchpad.net/gocheck" |
1319 | + |
1320 | + "launchpad.net/ubuntu-push/client/session" |
1321 | + helpers "launchpad.net/ubuntu-push/testing" |
1322 | +) |
1323 | + |
1324 | +// hook up gocheck |
1325 | +func TestPoller(t *testing.T) { TestingT(t) } |
1326 | + |
1327 | +type PrSuite struct { |
1328 | + log *helpers.TestLogger |
1329 | + myd *myD |
1330 | +} |
1331 | + |
1332 | +var _ = Suite(&PrSuite{}) |
1333 | + |
1334 | +type myD struct { |
1335 | + // in/out for RequestWakeup |
1336 | + reqWakeName string |
1337 | + reqWakeTime time.Time |
1338 | + reqWakeCookie string |
1339 | + reqWakeErr error |
1340 | + // WatchWakeups |
1341 | + watchWakeCh <-chan bool |
1342 | + watchWakeErr error |
1343 | + // RequestWakelock |
1344 | + reqLockName string |
1345 | + reqLockCookie string |
1346 | + reqLockErr error |
1347 | + // ClearWakelock |
1348 | + clearLockCookie string |
1349 | + clearLockErr error |
1350 | + // Poll |
1351 | + pollErr error |
1352 | + // WatchDones |
1353 | + watchDonesCh <-chan bool |
1354 | + watchDonesErr error |
1355 | + // State |
1356 | + stateState session.ClientSessionState |
1357 | +} |
1358 | + |
1359 | +func (m *myD) RequestWakeup(name string, wakeupTime time.Time) (string, error) { |
1360 | + m.reqWakeName = name |
1361 | + m.reqWakeTime = wakeupTime |
1362 | + return m.reqWakeCookie, m.reqWakeErr |
1363 | +} |
1364 | +func (m *myD) RequestWakelock(name string) (string, error) { |
1365 | + m.reqLockName = name |
1366 | + return m.reqLockCookie, m.reqLockErr |
1367 | +} |
1368 | +func (m *myD) ClearWakelock(cookie string) error { |
1369 | + m.clearLockCookie = cookie |
1370 | + return m.clearLockErr |
1371 | +} |
1372 | +func (m *myD) ClearWakeup(cookie string) error { panic("clearwakeup called??") } |
1373 | +func (m *myD) WatchWakeups() (<-chan bool, error) { return m.watchWakeCh, m.watchWakeErr } |
1374 | +func (m *myD) Poll() error { return m.pollErr } |
1375 | +func (m *myD) WatchDones() (<-chan bool, error) { return m.watchDonesCh, m.watchDonesErr } |
1376 | +func (m *myD) State() session.ClientSessionState { return m.stateState } |
1377 | + |
1378 | +func (s *PrSuite) SetUpTest(c *C) { |
1379 | + s.log = helpers.NewTestLogger(c, "debug") |
1380 | + s.myd = &myD{} |
1381 | +} |
1382 | + |
1383 | +func (s *PrSuite) TestStep(c *C) { |
1384 | + p := &poller{ |
1385 | + times: Times{}, |
1386 | + log: s.log, |
1387 | + powerd: s.myd, |
1388 | + polld: s.myd, |
1389 | + sessionState: s.myd, |
1390 | + } |
1391 | + s.myd.reqLockCookie = "wakelock cookie" |
1392 | + s.myd.stateState = session.Running |
1393 | + // we'll get the wakeup right away |
1394 | + wakeupCh := make(chan bool, 1) |
1395 | + wakeupCh <- true |
1396 | + // we won't get the "done" signal in time ;) |
1397 | + doneCh := make(chan bool) |
1398 | + // and a channel to get the return value from a goroutine |
1399 | + ch := make(chan string) |
1400 | + // now, run |
1401 | + go func() { ch <- p.step(wakeupCh, doneCh, "old cookie") }() |
1402 | + select { |
1403 | + case s := <-ch: |
1404 | + c.Check(s, Equals, "wakelock cookie") |
1405 | + case <-time.After(time.Second): |
1406 | + c.Fatal("timeout waiting for step") |
1407 | + } |
1408 | + // check we cleared the old cookie |
1409 | + c.Check(s.myd.clearLockCookie, Equals, "old cookie") |
1410 | +} |
1411 | |
1412 | === added file 'scripts/noisy-helper.sh' |
1413 | --- scripts/noisy-helper.sh 1970-01-01 00:00:00 +0000 |
1414 | +++ scripts/noisy-helper.sh 2014-08-25 16:38:20 +0000 |
1415 | @@ -0,0 +1,7 @@ |
1416 | +#!/bin/sh |
1417 | +for a in `seq 1 100` |
1418 | +do |
1419 | +echo BOOM-$a |
1420 | +>&2 echo BANG-$a |
1421 | +done |
1422 | +exit 1 |
1423 | |
1424 | === modified file 'server/acceptance/acceptanceclient.go' |
1425 | --- server/acceptance/acceptanceclient.go 2014-06-05 09:32:43 +0000 |
1426 | +++ server/acceptance/acceptanceclient.go 2014-08-25 16:38:20 +0000 |
1427 | @@ -19,9 +19,7 @@ |
1428 | |
1429 | import ( |
1430 | "crypto/tls" |
1431 | - "crypto/x509" |
1432 | "encoding/json" |
1433 | - "errors" |
1434 | "fmt" |
1435 | "net" |
1436 | "strings" |
1437 | @@ -40,35 +38,36 @@ |
1438 | ImageChannel string |
1439 | ServerAddr string |
1440 | ExchangeTimeout time.Duration |
1441 | - CertPEMBlock []byte |
1442 | ReportPings bool |
1443 | Levels map[string]int64 |
1444 | - Insecure bool // don't verify certs |
1445 | + TLSConfig *tls.Config |
1446 | Prefix string // prefix for events |
1447 | Auth string |
1448 | // connection |
1449 | Connection net.Conn |
1450 | } |
1451 | |
1452 | -// Dial connects to a server using the configuration in the ClientSession |
1453 | -// and sets up the connection. |
1454 | +// Dial connects to a server using the configuration in the |
1455 | +// ClientSession and sets up the connection. |
1456 | func (sess *ClientSession) Dial() error { |
1457 | conn, err := net.DialTimeout("tcp", sess.ServerAddr, sess.ExchangeTimeout) |
1458 | if err != nil { |
1459 | return err |
1460 | } |
1461 | - tlsConfig := &tls.Config{} |
1462 | - if sess.CertPEMBlock != nil { |
1463 | - cp := x509.NewCertPool() |
1464 | - ok := cp.AppendCertsFromPEM(sess.CertPEMBlock) |
1465 | - if !ok { |
1466 | - return errors.New("dial: could not parse certificate") |
1467 | - } |
1468 | - tlsConfig.RootCAs = cp |
1469 | + sess.TLSWrapAndSet(conn) |
1470 | + return nil |
1471 | +} |
1472 | + |
1473 | +// TLSWrapAndSet wraps a socket connection in tls and sets it as |
1474 | +// session.Connection. For use instead of Dial(). |
1475 | +func (sess *ClientSession) TLSWrapAndSet(conn net.Conn) { |
1476 | + var tlsConfig *tls.Config |
1477 | + if sess.TLSConfig != nil { |
1478 | + tlsConfig = sess.TLSConfig |
1479 | + } else { |
1480 | + tlsConfig = &tls.Config{} |
1481 | } |
1482 | - tlsConfig.InsecureSkipVerify = sess.Insecure |
1483 | sess.Connection = tls.Client(conn, tlsConfig) |
1484 | - return nil |
1485 | } |
1486 | |
1487 | type serverMsg struct { |
1488 | |
1489 | === added file 'server/acceptance/cmd/acceptanceclient.go' |
1490 | --- server/acceptance/cmd/acceptanceclient.go 1970-01-01 00:00:00 +0000 |
1491 | +++ server/acceptance/cmd/acceptanceclient.go 2014-08-25 16:38:20 +0000 |
1492 | @@ -0,0 +1,54 @@ |
1493 | +/* |
1494 | + Copyright 2013-2014 Canonical Ltd. |
1495 | + |
1496 | + This program is free software: you can redistribute it and/or modify it |
1497 | + under the terms of the GNU General Public License version 3, as published |
1498 | + by the Free Software Foundation. |
1499 | + |
1500 | + This program is distributed in the hope that it will be useful, but |
1501 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
1502 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1503 | + PURPOSE. See the GNU General Public License for more details. |
1504 | + |
1505 | + You should have received a copy of the GNU General Public License along |
1506 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
1507 | +*/ |
1508 | + |
1509 | +// acceptanceclient command for playing. |
1510 | +package main |
1511 | + |
1512 | +import ( |
1513 | + "log" |
1514 | + "os/exec" |
1515 | + "strings" |
1516 | + |
1517 | + "launchpad.net/ubuntu-push/server/acceptance" |
1518 | + "launchpad.net/ubuntu-push/server/acceptance/kit" |
1519 | +) |
1520 | + |
1521 | +type configuration struct { |
1522 | + kit.Configuration |
1523 | + AuthHelper string `json:"auth_helper"` |
1524 | + WaitFor string `json:"wait_for"` |
1525 | +} |
1526 | + |
1527 | +func main() { |
1528 | + kit.Defaults["auth_helper"] = "" |
1529 | + kit.Defaults["wait_for"] = "" |
1530 | + cfg := &configuration{} |
1531 | + kit.CliLoop(cfg, &cfg.Configuration, func(session *acceptance.ClientSession, cfgDir string) { |
1532 | + log.Printf("with: %#v", session) |
1533 | + }, func(url string) string { |
1534 | + if cfg.AuthHelper == "" { |
1535 | + return "" |
1536 | + } |
1537 | + auth, err := exec.Command(cfg.AuthHelper, url).Output() |
1538 | + if err != nil { |
1539 | + log.Fatalf("auth helper: %v", err) |
1540 | + } |
1541 | + return strings.TrimSpace(string(auth)) |
1542 | + }, func() string { |
1543 | + return cfg.WaitFor |
1544 | + }, func() { |
1545 | + }) |
1546 | +} |
1547 | |
1548 | === added directory 'server/acceptance/kit' |
1549 | === added file 'server/acceptance/kit/api.go' |
1550 | --- server/acceptance/kit/api.go 1970-01-01 00:00:00 +0000 |
1551 | +++ server/acceptance/kit/api.go 2014-08-25 16:38:20 +0000 |
1552 | @@ -0,0 +1,82 @@ |
1553 | +/* |
1554 | + Copyright 2013-2014 Canonical Ltd. |
1555 | + |
1556 | + This program is free software: you can redistribute it and/or modify it |
1557 | + under the terms of the GNU General Public License version 3, as published |
1558 | + by the Free Software Foundation. |
1559 | + |
1560 | + This program is distributed in the hope that it will be useful, but |
1561 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
1562 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1563 | + PURPOSE. See the GNU General Public License for more details. |
1564 | + |
1565 | + You should have received a copy of the GNU General Public License along |
1566 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
1567 | +*/ |
1568 | + |
1569 | +// Package kit contains reusable building blocks for acceptance. |
1570 | +package kit |
1571 | + |
1572 | +import ( |
1573 | + "bytes" |
1574 | + "crypto/tls" |
1575 | + "encoding/json" |
1576 | + "errors" |
1577 | + "io/ioutil" |
1578 | + "net/http" |
1579 | +) |
1580 | + |
1581 | +// APIClient helps making api requests. |
1582 | +type APIClient struct { |
1583 | + ServerAPIURL string |
1584 | + // hook to adjust requests |
1585 | + MassageRequest func(req *http.Request, message interface{}) *http.Request |
1586 | + // other state |
1587 | + httpClient *http.Client |
1588 | +} |
1589 | + |
1590 | +// SetupClient sets up the http client to make requests. |
1591 | +func (api *APIClient) SetupClient(tlsConfig *tls.Config) { |
1592 | + api.httpClient = &http.Client{ |
1593 | + Transport: &http.Transport{TLSClientConfig: tlsConfig}, |
1594 | + } |
1595 | +} |
1596 | + |
1597 | +var ErrNOk = errors.New("not ok") |
1598 | + |
1599 | +// Post a API request. |
1600 | +func (api *APIClient) PostRequest(path string, message interface{}) (map[string]interface{}, error) { |
1601 | + packedMessage, err := json.Marshal(message) |
1602 | + if err != nil { |
1603 | + panic(err) |
1604 | + } |
1605 | + reader := bytes.NewReader(packedMessage) |
1606 | + |
1607 | + url := api.ServerAPIURL + path |
1608 | + request, _ := http.NewRequest("POST", url, reader) |
1609 | + request.ContentLength = int64(reader.Len()) |
1610 | + request.Header.Set("Content-Type", "application/json") |
1611 | + |
1612 | + if api.MassageRequest != nil { |
1613 | + request = api.MassageRequest(request, message) |
1614 | + } |
1615 | + |
1616 | + resp, err := api.httpClient.Do(request) |
1617 | + if err != nil { |
1618 | + return nil, err |
1619 | + } |
1620 | + defer resp.Body.Close() |
1621 | + body, err := ioutil.ReadAll(resp.Body) |
1622 | + if err != nil { |
1623 | + return nil, err |
1624 | + } |
1625 | + var res map[string]interface{} |
1626 | + err = json.Unmarshal(body, &res) |
1627 | + if err != nil { |
1628 | + return nil, err |
1629 | + } |
1630 | + if ok, _ := res["ok"].(bool); !ok { |
1631 | + return res, ErrNOk |
1632 | + } |
1633 | + return res, nil |
1634 | +} |
1635 | |
1636 | === renamed file 'server/acceptance/cmd/acceptanceclient.go' => 'server/acceptance/kit/cliloop.go' |
1637 | --- server/acceptance/cmd/acceptanceclient.go 2014-07-11 21:24:21 +0000 |
1638 | +++ server/acceptance/kit/cliloop.go 2014-08-25 16:38:20 +0000 |
1639 | @@ -14,15 +14,13 @@ |
1640 | with this program. If not, see <http://www.gnu.org/licenses/>. |
1641 | */ |
1642 | |
1643 | -// acceptanceclient command for playing. |
1644 | -package main |
1645 | +package kit |
1646 | |
1647 | import ( |
1648 | "flag" |
1649 | "fmt" |
1650 | "log" |
1651 | "os" |
1652 | - "os/exec" |
1653 | "path/filepath" |
1654 | "regexp" |
1655 | "strings" |
1656 | @@ -32,27 +30,58 @@ |
1657 | "launchpad.net/ubuntu-push/server/acceptance" |
1658 | ) |
1659 | |
1660 | -var ( |
1661 | - insecureFlag = flag.Bool("insecure", false, "disable checking of server certificate and hostname") |
1662 | - reportPingsFlag = flag.Bool("reportPings", true, "report each Ping from the server") |
1663 | - deviceModel = flag.String("model", "?", "device image model") |
1664 | - imageChannel = flag.String("imageChannel", "?", "image channel") |
1665 | -) |
1666 | - |
1667 | -type configuration struct { |
1668 | +type Configuration struct { |
1669 | // session configuration |
1670 | ExchangeTimeout config.ConfigTimeDuration `json:"exchange_timeout"` |
1671 | // server connection config |
1672 | - Addr config.ConfigHostPort |
1673 | - CertPEMFile string `json:"cert_pem_file"` |
1674 | - AuthHelper string `json:"auth_helper"` |
1675 | - RunTimeout config.ConfigTimeDuration `json:"run_timeout"` |
1676 | - WaitFor string `json:"wait_for"` |
1677 | -} |
1678 | - |
1679 | -func main() { |
1680 | + Target string `json:"target"` |
1681 | + Addr config.ConfigHostPort `json:"addr"` |
1682 | + CertPEMFile string `json:"cert_pem_file"` |
1683 | + Insecure bool `json:"insecure" help:"disable checking of server certificate and hostname"` |
1684 | + Domain string `json:"domain" help:"domain for tls connect"` |
1685 | + // run timeout |
1686 | + RunTimeout config.ConfigTimeDuration `json:"run_timeout"` |
1687 | + // flags |
1688 | + ReportPings bool `json:"reportPings" help:"report each Ping from the server"` |
1689 | + DeviceModel string `json:"model" help:"device image model"` |
1690 | + ImageChannel string `json:"imageChannel" help:"image channel"` |
1691 | +} |
1692 | + |
1693 | +func (cfg *Configuration) PickByTarget(what, productionValue, stagingValue string) (value string) { |
1694 | + switch cfg.Target { |
1695 | + case "production": |
1696 | + value = productionValue |
1697 | + case "staging": |
1698 | + value = stagingValue |
1699 | + case "": |
1700 | + log.Fatalf("either %s or target must be given", what) |
1701 | + default: |
1702 | + log.Fatalf("if specified target should be production|staging") |
1703 | + } |
1704 | + return |
1705 | +} |
1706 | + |
1707 | +// Control. |
1708 | +var ( |
1709 | + Name = "acceptanceclient" |
1710 | + Defaults = map[string]interface{}{ |
1711 | + "target": "", |
1712 | + "addr": ":0", |
1713 | + "exchange_timeout": "5s", |
1714 | + "cert_pem_file": "", |
1715 | + "insecure": false, |
1716 | + "domain": "", |
1717 | + "run_timeout": "0s", |
1718 | + "reportPings": true, |
1719 | + "model": "?", |
1720 | + "imageChannel": "?", |
1721 | + } |
1722 | +) |
1723 | + |
1724 | +// CliLoop parses command line arguments and runs a client loop. |
1725 | +func CliLoop(totalCfg interface{}, cfg *Configuration, onSetup func(sess *acceptance.ClientSession, cfgDir string), auth func(string) string, waitFor func() string, onConnect func()) { |
1726 | flag.Usage = func() { |
1727 | - fmt.Fprintf(os.Stderr, "Usage: acceptancclient [options] <device id>\n") |
1728 | + fmt.Fprintf(os.Stderr, "Usage: %s [options] <device id>\n", Name) |
1729 | flag.PrintDefaults() |
1730 | } |
1731 | missingArg := func(what string) { |
1732 | @@ -60,14 +89,7 @@ |
1733 | flag.Usage() |
1734 | os.Exit(2) |
1735 | } |
1736 | - cfg := &configuration{} |
1737 | - err := config.ReadFilesDefaults(cfg, map[string]interface{}{ |
1738 | - "exchange_timeout": "5s", |
1739 | - "cert_pem_file": "", |
1740 | - "auth_helper": "", |
1741 | - "run_timeout": "0s", |
1742 | - "wait_for": "", |
1743 | - }, "<flags>") |
1744 | + err := config.ReadFilesDefaults(totalCfg, Defaults, "<flags>") |
1745 | if err != nil { |
1746 | log.Fatalf("reading config: %v", err) |
1747 | } |
1748 | @@ -76,36 +98,34 @@ |
1749 | case narg < 1: |
1750 | missingArg("device-id") |
1751 | } |
1752 | + addr := "" |
1753 | + if cfg.Addr == ":0" { |
1754 | + addr = cfg.PickByTarget("addr", "push-delivery.ubuntu.com:443", |
1755 | + "push-delivery.staging.ubuntu.com:443") |
1756 | + } else { |
1757 | + addr = cfg.Addr.HostPort() |
1758 | + } |
1759 | session := &acceptance.ClientSession{ |
1760 | ExchangeTimeout: cfg.ExchangeTimeout.TimeDuration(), |
1761 | - ServerAddr: cfg.Addr.HostPort(), |
1762 | + ServerAddr: addr, |
1763 | DeviceId: flag.Arg(0), |
1764 | // flags |
1765 | - Model: *deviceModel, |
1766 | - ImageChannel: *imageChannel, |
1767 | - ReportPings: *reportPingsFlag, |
1768 | - Insecure: *insecureFlag, |
1769 | - } |
1770 | - log.Printf("with: %#v", session) |
1771 | - if !*insecureFlag && cfg.CertPEMFile != "" { |
1772 | - cfgDir := filepath.Dir(flag.Lookup("cfg@").Value.String()) |
1773 | - log.Printf("cert: %v relToDir: %v", cfg.CertPEMFile, cfgDir) |
1774 | - session.CertPEMBlock, err = config.LoadFile(cfg.CertPEMFile, cfgDir) |
1775 | - if err != nil { |
1776 | - log.Fatalf("reading CertPEMFile: %v", err) |
1777 | - } |
1778 | - } |
1779 | - if len(cfg.AuthHelper) != 0 { |
1780 | - auth, err := exec.Command(cfg.AuthHelper, "https://push.ubuntu.com/").Output() |
1781 | - if err != nil { |
1782 | - log.Fatalf("auth helper: %v", err) |
1783 | - } |
1784 | - session.Auth = strings.TrimSpace(string(auth)) |
1785 | - } |
1786 | + Model: cfg.DeviceModel, |
1787 | + ImageChannel: cfg.ImageChannel, |
1788 | + ReportPings: cfg.ReportPings, |
1789 | + } |
1790 | + cfgDir := filepath.Dir(flag.Lookup("cfg@").Value.String()) |
1791 | + onSetup(session, cfgDir) |
1792 | + session.TLSConfig, err = MakeTLSConfig(cfg.Domain, cfg.Insecure, cfg.CertPEMFile, cfgDir) |
1793 | + if err != nil { |
1794 | + log.Fatalf("tls config: %v", err) |
1795 | + } |
1796 | + session.Auth = auth("https://push.ubuntu.com/") |
1797 | var waitForRegexp *regexp.Regexp |
1798 | - if cfg.WaitFor != "" { |
1799 | + waitForStr := waitFor() |
1800 | + if waitForStr != "" { |
1801 | var err error |
1802 | - waitForRegexp, err = regexp.Compile(cfg.WaitFor) |
1803 | + waitForRegexp, err = regexp.Compile(waitForStr) |
1804 | if err != nil { |
1805 | log.Fatalf("wait_for regexp: %v", err) |
1806 | } |
1807 | @@ -118,6 +138,9 @@ |
1808 | go func() { |
1809 | for { |
1810 | ev := <-events |
1811 | + if strings.HasPrefix(ev, "connected") { |
1812 | + onConnect() |
1813 | + } |
1814 | if waitForRegexp != nil && waitForRegexp.MatchString(ev) { |
1815 | log.Println("<matching-event>:", ev) |
1816 | os.Exit(0) |
1817 | |
1818 | === added file 'server/acceptance/kit/helpers.go' |
1819 | --- server/acceptance/kit/helpers.go 1970-01-01 00:00:00 +0000 |
1820 | +++ server/acceptance/kit/helpers.go 2014-08-25 16:38:20 +0000 |
1821 | @@ -0,0 +1,46 @@ |
1822 | +/* |
1823 | + Copyright 2013-2014 Canonical Ltd. |
1824 | + |
1825 | + This program is free software: you can redistribute it and/or modify it |
1826 | + under the terms of the GNU General Public License version 3, as published |
1827 | + by the Free Software Foundation. |
1828 | + |
1829 | + This program is distributed in the hope that it will be useful, but |
1830 | + WITHOUT ANY WARRANTY; without even the implied warranties of |
1831 | + MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1832 | + PURPOSE. See the GNU General Public License for more details. |
1833 | + |
1834 | + You should have received a copy of the GNU General Public License along |
1835 | + with this program. If not, see <http://www.gnu.org/licenses/>. |
1836 | +*/ |
1837 | + |
1838 | +package kit |
1839 | + |
1840 | +import ( |
1841 | + "crypto/tls" |
1842 | + "crypto/x509" |
1843 | + "fmt" |
1844 | + |
1845 | + "launchpad.net/ubuntu-push/config" |
1846 | +) |
1847 | + |
1848 | +// MakeTLSConfig makes a tls.Config, optionally reading a cert from |
1849 | +// disk, possibly relative to relDir. |
1850 | +func MakeTLSConfig(domain string, insecure bool, certPEMFile string, relDir string) (*tls.Config, error) { |
1851 | + tlsConfig := &tls.Config{} |
1852 | + tlsConfig.ServerName = domain |
1853 | + tlsConfig.InsecureSkipVerify = insecure |
1854 | + if !insecure && certPEMFile != "" { |
1855 | + certPEMBlock, err := config.LoadFile(certPEMFile, relDir) |
1856 | + if err != nil { |
1857 | + return nil, fmt.Errorf("reading cert: %v", err) |
1858 | + } |
1859 | + cp := x509.NewCertPool() |
1860 | + ok := cp.AppendCertsFromPEM(certPEMBlock) |
1861 | + if !ok { |
1862 | + return nil, fmt.Errorf("could not parse certificate") |
1863 | + } |
1864 | + tlsConfig.RootCAs = cp |
1865 | + } |
1866 | + return tlsConfig, nil |
1867 | +} |
1868 | |
1869 | === modified file 'server/acceptance/suites/broadcast.go' |
1870 | --- server/acceptance/suites/broadcast.go 2014-06-25 11:00:15 +0000 |
1871 | +++ server/acceptance/suites/broadcast.go 2014-08-25 16:38:20 +0000 |
1872 | @@ -41,8 +41,7 @@ |
1873 | ExpireOn: future, |
1874 | Data: json.RawMessage(`{"img1/m1": 42}`), |
1875 | }) |
1876 | - c.Assert(err, IsNil) |
1877 | - c.Assert(got, Matches, OK) |
1878 | + c.Assert(err, IsNil, Commentf("%v", got)) |
1879 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":42}]`) |
1880 | stop() |
1881 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) |
1882 | @@ -56,14 +55,13 @@ |
1883 | ExpireOn: future, |
1884 | Data: json.RawMessage(`{"img1/m2": 10}`), |
1885 | }) |
1886 | - c.Assert(err, IsNil) |
1887 | + c.Assert(err, IsNil, Commentf("%v", got)) |
1888 | got, err = s.PostRequest("/broadcast", &api.Broadcast{ |
1889 | Channel: "system", |
1890 | ExpireOn: future, |
1891 | Data: json.RawMessage(`{"img1/m1": 20}`), |
1892 | }) |
1893 | - c.Assert(err, IsNil) |
1894 | - c.Assert(got, Matches, OK) |
1895 | + c.Assert(err, IsNil, Commentf("%v", got)) |
1896 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:2 payloads:[{"img1/m1":20}]`) |
1897 | stop() |
1898 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) |
1899 | @@ -77,8 +75,7 @@ |
1900 | ExpireOn: future, |
1901 | Data: json.RawMessage(`{"img1/m1": 1}`), |
1902 | }) |
1903 | - c.Assert(err, IsNil) |
1904 | - c.Assert(got, Matches, OK) |
1905 | + c.Assert(err, IsNil, Commentf("%v", got)) |
1906 | |
1907 | events, errCh, stop := s.StartClient(c, "DEVB", nil) |
1908 | // gettting pending on connect |
1909 | @@ -97,8 +94,7 @@ |
1910 | ExpireOn: future, |
1911 | Data: json.RawMessage(fmt.Sprintf(payloadFmt, i)), |
1912 | }) |
1913 | - c.Assert(err, IsNil) |
1914 | - c.Assert(got, Matches, OK) |
1915 | + c.Assert(err, IsNil, Commentf("%v", got)) |
1916 | } |
1917 | |
1918 | events, errCh, stop := s.StartClient(c, "DEVC", nil) |
1919 | @@ -130,8 +126,7 @@ |
1920 | ExpireOn: future, |
1921 | Data: json.RawMessage(`{"img1/m1": 42}`), |
1922 | }) |
1923 | - c.Assert(err, IsNil) |
1924 | - c.Assert(got, Matches, OK) |
1925 | + c.Assert(err, IsNil, Commentf("%v", got)) |
1926 | c.Check(NextEvent(events1, errCh1), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":42}]`) |
1927 | c.Check(NextEvent(events2, errCh2), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":42}]`) |
1928 | stop1() |
1929 | @@ -149,8 +144,7 @@ |
1930 | ExpireOn: future, |
1931 | Data: json.RawMessage(`{"img1/m1": 1}`), |
1932 | }) |
1933 | - c.Assert(err, IsNil) |
1934 | - c.Assert(got, Matches, OK) |
1935 | + c.Assert(err, IsNil, Commentf("%v", got)) |
1936 | c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":1}]`) |
1937 | stop() |
1938 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) |
1939 | @@ -161,8 +155,7 @@ |
1940 | ExpireOn: future, |
1941 | Data: json.RawMessage(`{"img1/m1": 2}`), |
1942 | }) |
1943 | - c.Assert(err, IsNil) |
1944 | - c.Assert(got, Matches, OK) |
1945 | + c.Assert(err, IsNil, Commentf("%v", got)) |
1946 | // reconnect, provide levels, get only later notification |
1947 | events, errCh, stop = s.StartClient(c, "DEVD", map[string]int64{ |
1948 | protocol.SystemChannelId: 1, |
1949 | @@ -180,15 +173,13 @@ |
1950 | ExpireOn: future, |
1951 | Data: json.RawMessage(`{"img1/m1": 1}`), |
1952 | }) |
1953 | - c.Assert(err, IsNil) |
1954 | - c.Assert(got, Matches, OK) |
1955 | + c.Assert(err, IsNil, Commentf("%v", got)) |
1956 | got, err = s.PostRequest("/broadcast", &api.Broadcast{ |
1957 | Channel: "system", |
1958 | ExpireOn: future, |
1959 | Data: json.RawMessage(`{"img1/m1": 2}`), |
1960 | }) |
1961 | - c.Assert(err, IsNil) |
1962 | - c.Assert(got, Matches, OK) |
1963 | + c.Assert(err, IsNil, Commentf("%v", got)) |
1964 | |
1965 | events, errCh, stop := s.StartClient(c, "DEVB", map[string]int64{ |
1966 | protocol.SystemChannelId: 10, |
1967 | @@ -219,15 +210,13 @@ |
1968 | ExpireOn: future, |
1969 | Data: json.RawMessage(`{"img1/m1": 1}`), |
1970 | }) |
1971 | - c.Assert(err, IsNil) |
1972 | - c.Assert(got, Matches, OK) |
1973 | + c.Assert(err, IsNil, Commentf("%v", got)) |
1974 | got, err = s.PostRequest("/broadcast", &api.Broadcast{ |
1975 | Channel: "system", |
1976 | ExpireOn: future, |
1977 | Data: json.RawMessage(`{"img1/m1": 2}`), |
1978 | }) |
1979 | - c.Assert(err, IsNil) |
1980 | - c.Assert(got, Matches, OK) |
1981 | + c.Assert(err, IsNil, Commentf("%v", got)) |
1982 | |
1983 | events, errCh, stop := s.StartClient(c, "DEVB", map[string]int64{ |
1984 | protocol.SystemChannelId: -10, |
1985 | @@ -246,15 +235,13 @@ |
1986 | ExpireOn: future, |
1987 | Data: json.RawMessage(`{"img1/m1": 1}`), |
1988 | }) |
1989 | - c.Assert(err, IsNil) |
1990 | - c.Assert(got, Matches, OK) |
1991 | + c.Assert(err, IsNil, Commentf("%v", got)) |
1992 | got, err = s.PostRequest("/broadcast", &api.Broadcast{ |
1993 | Channel: "system", |
1994 | ExpireOn: time.Now().Add(1 * time.Second).Format(time.RFC3339), |
1995 | Data: json.RawMessage(`{"img1/m1": 2}`), |
1996 | }) |
1997 | - c.Assert(err, IsNil) |
1998 | - c.Assert(got, Matches, OK) |
1999 | + c.Assert(err, IsNil, Commentf("%v", got)) |
2000 | |
2001 | time.Sleep(2 * time.Second) |
2002 | // second broadcast is expired |
2003 | |
2004 | === modified file 'server/acceptance/suites/suite.go' |
2005 | --- server/acceptance/suites/suite.go 2014-06-25 11:00:15 +0000 |
2006 | +++ server/acceptance/suites/suite.go 2014-08-25 16:38:20 +0000 |
2007 | @@ -18,13 +18,9 @@ |
2008 | package suites |
2009 | |
2010 | import ( |
2011 | - "bytes" |
2012 | - "encoding/json" |
2013 | "flag" |
2014 | "fmt" |
2015 | - "io/ioutil" |
2016 | "net" |
2017 | - "net/http" |
2018 | "os" |
2019 | "runtime" |
2020 | "time" |
2021 | @@ -32,6 +28,7 @@ |
2022 | . "launchpad.net/gocheck" |
2023 | |
2024 | "launchpad.net/ubuntu-push/server/acceptance" |
2025 | + "launchpad.net/ubuntu-push/server/acceptance/kit" |
2026 | helpers "launchpad.net/ubuntu-push/testing" |
2027 | ) |
2028 | |
2029 | @@ -84,14 +81,10 @@ |
2030 | StartServer func(c *C, s *AcceptanceSuite, handle *ServerHandle) |
2031 | // populated by StartServer |
2032 | ServerHandle |
2033 | - ServerAPIURL string |
2034 | + kit.APIClient // has ServerAPIURL |
2035 | // KillGroup should be populated by StartServer with functions |
2036 | // to kill the server process |
2037 | KillGroup map[string]func(os.Signal) |
2038 | - // hook to adjust requests |
2039 | - MassageRequest func(req *http.Request, message interface{}) *http.Request |
2040 | - // other state |
2041 | - httpClient *http.Client |
2042 | } |
2043 | |
2044 | // Start a new server for each test. |
2045 | @@ -101,7 +94,7 @@ |
2046 | c.Assert(s.ServerHandle.ServerEvents, NotNil) |
2047 | c.Assert(s.ServerHandle.ServerAddr, Not(Equals), "") |
2048 | c.Assert(s.ServerAPIURL, Not(Equals), "") |
2049 | - s.httpClient = &http.Client{} |
2050 | + s.SetupClient(nil) |
2051 | } |
2052 | |
2053 | func (s *AcceptanceSuite) TearDownTest(c *C) { |
2054 | @@ -110,45 +103,19 @@ |
2055 | } |
2056 | } |
2057 | |
2058 | -// Post a API request. |
2059 | -func (s *AcceptanceSuite) PostRequest(path string, message interface{}) (string, error) { |
2060 | - packedMessage, err := json.Marshal(message) |
2061 | - if err != nil { |
2062 | - panic(err) |
2063 | - } |
2064 | - reader := bytes.NewReader(packedMessage) |
2065 | - |
2066 | - url := s.ServerAPIURL + path |
2067 | - request, _ := http.NewRequest("POST", url, reader) |
2068 | - request.ContentLength = int64(reader.Len()) |
2069 | - request.Header.Set("Content-Type", "application/json") |
2070 | - |
2071 | - if s.MassageRequest != nil { |
2072 | - request = s.MassageRequest(request, message) |
2073 | - } |
2074 | - |
2075 | - resp, err := s.httpClient.Do(request) |
2076 | - if err != nil { |
2077 | - panic(err) |
2078 | - } |
2079 | - defer resp.Body.Close() |
2080 | - body, err := ioutil.ReadAll(resp.Body) |
2081 | - return string(body), err |
2082 | -} |
2083 | - |
2084 | func testClientSession(addr string, deviceId, model, imageChannel string, reportPings bool) *acceptance.ClientSession { |
2085 | - certPEMBlock, err := ioutil.ReadFile(helpers.SourceRelative("../ssl/testing.cert")) |
2086 | + tlsConfig, err := kit.MakeTLSConfig("", false, helpers.SourceRelative("../ssl/testing.cert"), "") |
2087 | if err != nil { |
2088 | panic(fmt.Sprintf("could not read ssl/testing.cert: %v", err)) |
2089 | } |
2090 | return &acceptance.ClientSession{ |
2091 | ExchangeTimeout: 100 * time.Millisecond, |
2092 | ServerAddr: addr, |
2093 | - CertPEMBlock: certPEMBlock, |
2094 | DeviceId: deviceId, |
2095 | Model: model, |
2096 | ImageChannel: imageChannel, |
2097 | ReportPings: reportPings, |
2098 | + TLSConfig: tlsConfig, |
2099 | } |
2100 | } |
2101 | |
2102 | @@ -198,5 +165,3 @@ |
2103 | |
2104 | // Long after the end of the tests. |
2105 | var future = time.Now().Add(9 * time.Hour).Format(time.RFC3339) |
2106 | - |
2107 | -const OK = `.*"ok":true.*` |
2108 | |
2109 | === modified file 'server/acceptance/suites/unicast.go' |
2110 | --- server/acceptance/suites/unicast.go 2014-07-14 15:23:17 +0000 |
2111 | +++ server/acceptance/suites/unicast.go 2014-08-25 16:38:20 +0000 |
2112 | @@ -23,6 +23,7 @@ |
2113 | |
2114 | . "launchpad.net/gocheck" |
2115 | |
2116 | + "launchpad.net/ubuntu-push/server/acceptance/kit" |
2117 | "launchpad.net/ubuntu-push/server/api" |
2118 | ) |
2119 | |
2120 | @@ -45,20 +46,15 @@ |
2121 | DeviceId: "DEV1", |
2122 | AppId: "app1", |
2123 | }) |
2124 | - c.Assert(err, IsNil) |
2125 | - c.Assert(res, Matches, OK) |
2126 | - var reg map[string]interface{} |
2127 | - err = json.Unmarshal([]byte(res), ®) |
2128 | - c.Assert(err, IsNil) |
2129 | + c.Assert(err, IsNil, Commentf("%v", res)) |
2130 | events, errCh, stop := s.StartClientAuth(c, "DEV1", nil, auth) |
2131 | got, err := s.PostRequest("/notify", &api.Unicast{ |
2132 | - Token: reg["token"].(string), |
2133 | + Token: res["token"].(string), |
2134 | AppId: "app1", |
2135 | ExpireOn: future, |
2136 | Data: json.RawMessage(`{"a": 42}`), |
2137 | }) |
2138 | - c.Assert(err, IsNil) |
2139 | - c.Assert(got, Matches, OK) |
2140 | + c.Assert(err, IsNil, Commentf("%v", got)) |
2141 | c.Check(NextEvent(events, errCh), Equals, `unicast app:app1 payload:{"a":42};`) |
2142 | stop() |
2143 | c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`) |
2144 | @@ -80,8 +76,7 @@ |
2145 | ExpireOn: future, |
2146 | Data: json.RawMessage(`{"to": 1}`), |
2147 | }) |
2148 | - c.Assert(err, IsNil) |
2149 | - c.Assert(got, Matches, OK) |
2150 | + c.Assert(err, IsNil, Commentf("%v", got)) |
2151 | got, err = s.PostRequest("/notify", &api.Unicast{ |
2152 | UserId: userId2, |
2153 | DeviceId: "DEV2", |
2154 | @@ -89,8 +84,7 @@ |
2155 | ExpireOn: future, |
2156 | Data: json.RawMessage(`{"to": 2}`), |
2157 | }) |
2158 | - c.Assert(err, IsNil) |
2159 | - c.Assert(got, Matches, OK) |
2160 | + c.Assert(err, IsNil, Commentf("%v", got)) |
2161 | c.Check(NextEvent(events1, errCh1), Equals, `unicast app:app1 payload:{"to":1};`) |
2162 | c.Check(NextEvent(events2, errCh2), Equals, `unicast app:app1 payload:{"to":2};`) |
2163 | stop1() |
2164 | @@ -111,8 +105,7 @@ |
2165 | ExpireOn: future, |
2166 | Data: json.RawMessage(`{"a": 42}`), |
2167 | }) |
2168 | - c.Assert(err, IsNil) |
2169 | - c.Assert(got, Matches, OK) |
2170 | + c.Assert(err, IsNil, Commentf("%v", got)) |
2171 | |
2172 | // get pending on connect |
2173 | events, errCh, stop := s.StartClientAuth(c, "DEV1", nil, auth) |
2174 | @@ -134,8 +127,7 @@ |
2175 | ExpireOn: future, |
2176 | Data: json.RawMessage(fmt.Sprintf(payloadFmt, i)), |
2177 | }) |
2178 | - c.Assert(err, IsNil) |
2179 | - c.Assert(got, Matches, OK) |
2180 | + c.Assert(err, IsNil, Commentf("%v", got)) |
2181 | } |
2182 | |
2183 | events, errCh, stop := s.StartClientAuth(c, "DEV2", nil, auth) |
2184 | @@ -168,8 +160,7 @@ |
2185 | ExpireOn: future, |
2186 | Data: json.RawMessage(fmt.Sprintf(payloadFmt, i)), |
2187 | }) |
2188 | - c.Assert(err, IsNil) |
2189 | - c.Assert(got, Matches, OK) |
2190 | + c.Assert(err, IsNil, Commentf("%v", got)) |
2191 | } |
2192 | |
2193 | got, err := s.PostRequest("/notify", &api.Unicast{ |
2194 | @@ -179,8 +170,9 @@ |
2195 | ExpireOn: future, |
2196 | Data: json.RawMessage(fmt.Sprintf(payloadFmt, MaxNotificationsPerApplication)), |
2197 | }) |
2198 | - c.Assert(err, IsNil) |
2199 | - c.Assert(got, Matches, `.*"error":"too-many-pending".*`) |
2200 | + c.Assert(err, Equals, kit.ErrNOk, Commentf("%v", got)) |
2201 | + errorStr, _ := got["error"].(string) |
2202 | + c.Assert(errorStr, Equals, "too-many-pending") |
2203 | |
2204 | // clear all pending |
2205 | got, err = s.PostRequest("/notify", &api.Unicast{ |
2206 | @@ -191,8 +183,7 @@ |
2207 | Data: json.RawMessage(fmt.Sprintf(payloadFmt, 1000)), |
2208 | ClearPending: true, |
2209 | }) |
2210 | - c.Assert(err, IsNil) |
2211 | - c.Assert(got, Matches, OK) |
2212 | + c.Assert(err, IsNil, Commentf("%v", got)) |
2213 | |
2214 | events, errCh, stop := s.StartClientAuth(c, "DEV2", nil, auth) |
2215 | // getting the 1 pending on connect |
2216 | @@ -214,8 +205,7 @@ |
2217 | Data: json.RawMessage(`{"m": 1}`), |
2218 | ReplaceTag: "tagFoo", |
2219 | }) |
2220 | - c.Assert(err, IsNil) |
2221 | - c.Assert(got, Matches, OK) |
2222 | + c.Assert(err, IsNil, Commentf("%v", got)) |
2223 | |
2224 | // replace |
2225 | got, err = s.PostRequest("/notify", &api.Unicast{ |
2226 | @@ -226,8 +216,7 @@ |
2227 | Data: json.RawMessage(`{"m": 2}`), |
2228 | ReplaceTag: "tagFoo", |
2229 | }) |
2230 | - c.Assert(err, IsNil) |
2231 | - c.Assert(got, Matches, OK) |
2232 | + c.Assert(err, IsNil, Commentf("%v", got)) |
2233 | |
2234 | events, errCh, stop := s.StartClientAuth(c, "DEV2", nil, auth) |
2235 | // getting the 1 pending on connect |