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