Merge lp:~pedronis/ubuntu-push/robust-tests-1.3 into lp:ubuntu-push
- robust-tests-1.3
- Merge into trunk
Proposed by
Samuele Pedroni
Status: | Superseded |
---|---|
Proposed branch: | lp:~pedronis/ubuntu-push/robust-tests-1.3 |
Merge into: | lp:ubuntu-push |
Diff against target: |
841 lines (+219/-90) 19 files modified
.precommit (+21/-19) PACKAGE_DEPS (+1/-0) client/service/service.go (+2/-0) client/service/service_test.go (+28/-5) client/session/session.go (+3/-3) client/session/session_test.go (+5/-5) debian/config.json (+1/-1) docs/_common.txt (+43/-0) docs/example-client/main.qml (+34/-17) docs/example-client/manifest.json (+1/-1) docs/lowlevel.txt (+2/-0) launch_helper/kindpool.go (+3/-2) launch_helper/legacy/legacy.go (+3/-4) messaging/messaging_test.go (+14/-7) server/acceptance/suites/helpers.go (+7/-6) server/api/handlers.go (+15/-1) server/api/handlers_test.go (+22/-6) server/broker/testsuite/suite.go (+11/-11) server/listener/listener_test.go (+3/-2) |
To merge this branch: | bzr merge lp:~pedronis/ubuntu-push/robust-tests-1.3 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Push Hackers | Pending | ||
Review via email: mp+242827@code.launchpad.net |
Commit message
make tests more robust in the face of 1.3
Description of the change
make tests more robust in the face of 1.3
- avoid map order issues
- -race slows down some tests so that small timeouts were problematic => increase timeout
- Reader Read can return data and EOF error together => use helpers
To post a comment you must log in.
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.precommit' |
2 | --- .precommit 2014-01-23 10:03:39 +0000 |
3 | +++ .precommit 2014-11-25 18:40:27 +0000 |
4 | @@ -5,25 +5,27 @@ |
5 | # And put this here-document in ~/.bazaar/plugins/precommit_script.py: |
6 | <<EOF |
7 | import os |
8 | -import subprocess |
9 | -from bzrlib.mutabletree import MutableTree |
10 | -from bzrlib import errors |
11 | - |
12 | -def start_commit_hook(*_): |
13 | - """This hook will execute '.precommit' script from root path of the bazaar |
14 | - branch. Commit will be canceled if precommit fails.""" |
15 | - |
16 | - # this hook only makes sense if a precommit file exist. |
17 | - if not os.path.exists(".precommit"): |
18 | - return |
19 | - try: |
20 | - subprocess.check_call(os.path.abspath(".precommit")) |
21 | - # if precommit fails (process return not zero) cancel commit. |
22 | - except subprocess.CalledProcessError: |
23 | - raise errors.BzrError("pre commit check failed.") |
24 | - |
25 | -MutableTree.hooks.install_named_hook('start_commit', start_commit_hook, |
26 | - 'Run "precommit" script on start_commit') |
27 | + |
28 | +if not os.getenv("SKIP_COMMIT_HOOK"): |
29 | + import subprocess |
30 | + from bzrlib.mutabletree import MutableTree |
31 | + from bzrlib import errors |
32 | + |
33 | + def start_commit_hook(*_): |
34 | + """This hook will execute '.precommit' script from root path of the bazaar |
35 | + branch. Commit will be canceled if precommit fails.""" |
36 | + |
37 | + # this hook only makes sense if a precommit file exist. |
38 | + if not os.path.exists(".precommit"): |
39 | + return |
40 | + try: |
41 | + subprocess.check_call(os.path.abspath(".precommit")) |
42 | + # if precommit fails (process return not zero) cancel commit. |
43 | + except subprocess.CalledProcessError: |
44 | + raise errors.BzrError("pre commit check failed (set SKIP_COMMIT_HOOK to skip).") |
45 | + |
46 | + MutableTree.hooks.install_named_hook('start_commit', start_commit_hook, |
47 | + 'Run "precommit" script on start_commit') |
48 | EOF |
49 | |
50 | make check-format # or whatever |
51 | |
52 | === modified file 'PACKAGE_DEPS' |
53 | --- PACKAGE_DEPS 2014-09-05 10:48:36 +0000 |
54 | +++ PACKAGE_DEPS 2014-11-25 18:40:27 +0000 |
55 | @@ -12,3 +12,4 @@ |
56 | libclick-0.4-dev |
57 | liburl-dispatcher1-dev |
58 | libaccounts-glib-dev |
59 | +system-image-dbus |
60 | |
61 | === modified file 'client/service/service.go' |
62 | --- client/service/service.go 2014-11-03 13:36:00 +0000 |
63 | +++ client/service/service.go 2014-11-25 18:40:27 +0000 |
64 | @@ -140,6 +140,8 @@ |
65 | case resp.StatusCode >= http.StatusInternalServerError: |
66 | // XXX retry on 503 |
67 | return nil, ErrBadServer |
68 | + case resp.StatusCode == http.StatusUnauthorized: |
69 | + return nil, ErrBadAuth |
70 | default: |
71 | return nil, ErrBadRequest |
72 | } |
73 | |
74 | === modified file 'client/service/service_test.go' |
75 | --- client/service/service_test.go 2014-08-06 09:01:59 +0000 |
76 | +++ client/service/service_test.go 2014-11-25 18:40:27 +0000 |
77 | @@ -19,6 +19,7 @@ |
78 | import ( |
79 | "encoding/json" |
80 | "fmt" |
81 | + "io" |
82 | "net/http" |
83 | "net/http/httptest" |
84 | "os" |
85 | @@ -179,7 +180,8 @@ |
86 | func (ss *serviceSuite) TestRegistrationWorks(c *C) { |
87 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
88 | buf := make([]byte, 256) |
89 | - n, e := r.Body.Read(buf) |
90 | + n := r.ContentLength |
91 | + _, e := io.ReadFull(r.Body, buf[:n]) |
92 | c.Assert(e, IsNil) |
93 | req := registrationRequest{} |
94 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
95 | @@ -240,6 +242,23 @@ |
96 | c.Check(err, ErrorMatches, "unable to request registration: .*") |
97 | } |
98 | |
99 | +func (ss *serviceSuite) TestManageRegFailsOn401(c *C) { |
100 | + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
101 | + http.Error(w, "Unauthorized", 401) |
102 | + })) |
103 | + defer ts.Close() |
104 | + setup := &PushServiceSetup{ |
105 | + DeviceId: "fake-device-id", |
106 | + RegURL: helpers.ParseURL(ts.URL), |
107 | + AuthGetter: func(string) string { return "tok" }, |
108 | + } |
109 | + svc := NewPushService(setup, ss.log) |
110 | + svc.Bus = ss.bus |
111 | + reg, err := svc.register(aPackageOnBus, []interface{}{anAppId}, nil) |
112 | + c.Check(err, Equals, ErrBadAuth) |
113 | + c.Check(reg, IsNil) |
114 | +} |
115 | + |
116 | func (ss *serviceSuite) TestManageRegFailsOn40x(c *C) { |
117 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
118 | http.Error(w, "I'm a teapot", 418) |
119 | @@ -277,7 +296,8 @@ |
120 | func (ss *serviceSuite) TestManageRegFailsOnBadJSON(c *C) { |
121 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
122 | buf := make([]byte, 256) |
123 | - n, e := r.Body.Read(buf) |
124 | + n := r.ContentLength |
125 | + _, e := io.ReadFull(r.Body, buf[:n]) |
126 | c.Assert(e, IsNil) |
127 | req := registrationRequest{} |
128 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
129 | @@ -303,7 +323,8 @@ |
130 | func (ss *serviceSuite) TestManageRegFailsOnBadJSONDocument(c *C) { |
131 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
132 | buf := make([]byte, 256) |
133 | - n, e := r.Body.Read(buf) |
134 | + n := r.ContentLength |
135 | + _, e := io.ReadFull(r.Body, buf[:n]) |
136 | c.Assert(e, IsNil) |
137 | req := registrationRequest{} |
138 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
139 | @@ -329,7 +350,8 @@ |
140 | func (ss *serviceSuite) TestDBusUnregisterWorks(c *C) { |
141 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
142 | buf := make([]byte, 256) |
143 | - n, e := r.Body.Read(buf) |
144 | + n := r.ContentLength |
145 | + _, e := io.ReadFull(r.Body, buf[:n]) |
146 | c.Assert(e, IsNil) |
147 | req := registrationRequest{} |
148 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
149 | @@ -356,7 +378,8 @@ |
150 | invoked := make(chan bool, 1) |
151 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
152 | buf := make([]byte, 256) |
153 | - n, e := r.Body.Read(buf) |
154 | + n := r.ContentLength |
155 | + _, e := io.ReadFull(r.Body, buf[:n]) |
156 | c.Assert(e, IsNil) |
157 | req := registrationRequest{} |
158 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
159 | |
160 | === modified file 'client/session/session.go' |
161 | --- client/session/session.go 2014-11-03 13:36:00 +0000 |
162 | +++ client/session/session.go 2014-11-25 18:40:27 +0000 |
163 | @@ -430,7 +430,7 @@ |
164 | return err |
165 | } |
166 | sess.clearShouldDelay() |
167 | - sess.Log.Debugf("broadcast chan:%v app:%v topLevel:%d payloads:%s", |
168 | + sess.Log.Infof("broadcast chan:%v app:%v topLevel:%d payloads:%s", |
169 | bcast.ChanId, bcast.AppId, bcast.TopLevel, bcast.Payloads) |
170 | if bcast.ChanId == protocol.SystemChannelId { |
171 | // the system channel id, the only one we care about for now |
172 | @@ -438,7 +438,7 @@ |
173 | sess.BroadcastCh <- sess.decodeBroadcast(bcast) |
174 | sess.Log.Debugf("sent bcast over") |
175 | } else { |
176 | - sess.Log.Debugf("what is this weird channel, %#v?", bcast.ChanId) |
177 | + sess.Log.Errorf("what is this weird channel, %#v?", bcast.ChanId) |
178 | } |
179 | return nil |
180 | } |
181 | @@ -468,7 +468,7 @@ |
182 | if to == nil { |
183 | continue |
184 | } |
185 | - sess.Log.Debugf("unicast app:%v msg:%s payload:%s", |
186 | + sess.Log.Infof("unicast app:%v msg:%s payload:%s", |
187 | notif.AppId, notif.MsgId, notif.Payload) |
188 | sess.Log.Debugf("sending ucast over") |
189 | sess.NotificationsCh <- AddressedNotification{to, notif} |
190 | |
191 | === modified file 'client/session/session_test.go' |
192 | --- client/session/session_test.go 2014-11-03 13:36:00 +0000 |
193 | +++ client/session/session_test.go 2014-11-25 18:40:27 +0000 |
194 | @@ -1480,7 +1480,7 @@ |
195 | } |
196 | |
197 | var ( |
198 | - dialTestTimeout = 100 * time.Millisecond |
199 | + dialTestTimeout = 300 * time.Millisecond |
200 | dialTestConf = ClientSessionConfig{ |
201 | ExchangeTimeout: dialTestTimeout, |
202 | PEM: helpers.TestCertPEMBlock, |
203 | @@ -1584,7 +1584,7 @@ |
204 | |
205 | // 2. "connect" (but on the fake protcol above! woo) |
206 | |
207 | - c.Check(takeNext(downCh), Equals, "deadline 100ms") |
208 | + c.Check(takeNext(downCh), Equals, fmt.Sprintf("deadline %v", dialTestTimeout)) |
209 | _, ok := takeNext(downCh).(protocol.ConnectMsg) |
210 | c.Check(ok, Equals, true) |
211 | upCh <- nil // no error |
212 | @@ -1597,7 +1597,7 @@ |
213 | // 3. "loop" |
214 | |
215 | // ping works, |
216 | - c.Check(takeNext(downCh), Equals, "deadline 110ms") |
217 | + c.Check(takeNext(downCh), Equals, fmt.Sprintf("deadline %v", dialTestTimeout+10*time.Millisecond)) |
218 | upCh <- protocol.PingPongMsg{Type: "ping"} |
219 | c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"}) |
220 | upCh <- nil |
221 | @@ -1613,7 +1613,7 @@ |
222 | TopLevel: 2, |
223 | Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)}, |
224 | } |
225 | - c.Check(takeNext(downCh), Equals, "deadline 110ms") |
226 | + c.Check(takeNext(downCh), Equals, fmt.Sprintf("deadline %v", dialTestTimeout+10*time.Millisecond)) |
227 | upCh <- b |
228 | c.Check(takeNext(downCh), Equals, protocol.AckMsg{"ack"}) |
229 | upCh <- nil |
230 | @@ -1625,7 +1625,7 @@ |
231 | c.Check(levels, DeepEquals, map[string]int64{"0": 2}) |
232 | |
233 | // and ping still work even after that. |
234 | - c.Check(takeNext(downCh), Equals, "deadline 110ms") |
235 | + c.Check(takeNext(downCh), Equals, fmt.Sprintf("deadline %v", dialTestTimeout+10*time.Millisecond)) |
236 | upCh <- protocol.PingPongMsg{Type: "ping"} |
237 | c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"}) |
238 | failure := errors.New("pongs") |
239 | |
240 | === modified file 'debian/config.json' |
241 | --- debian/config.json 2014-08-21 10:47:12 +0000 |
242 | +++ debian/config.json 2014-11-25 18:40:27 +0000 |
243 | @@ -12,7 +12,7 @@ |
244 | "recheck_timeout": "10m", |
245 | "connectivity_check_url": "http://start.ubuntu.com/connectivity-check.html", |
246 | "connectivity_check_md5": "4589f42e1546aa47ca181e5d949d310b", |
247 | - "log_level": "debug", |
248 | + "log_level": "info", |
249 | "fallback_vibration": {"pattern": [100, 100], "repeat": 2}, |
250 | "fallback_sound": "sounds/ubuntu/notifications/Slick.ogg", |
251 | "poll_interval": "5m", |
252 | |
253 | === modified file 'docs/_common.txt' |
254 | --- docs/_common.txt 2014-10-09 16:06:52 +0000 |
255 | +++ docs/_common.txt 2014-11-25 18:40:27 +0000 |
256 | @@ -185,3 +185,46 @@ |
257 | :clear_pending: Discards all previous pending notifications. Usually in response to getting a "too-many-pending" error. |
258 | :replace_tag: If there's a pending notification with the same tag, delete it before queuing this new one. |
259 | :data: A JSON object. |
260 | + |
261 | +Limitations of the Server API |
262 | +----------------------------- |
263 | + |
264 | +The push notification infrastructure is meant to help ensuring timely |
265 | +delivery of application notifications if the device is online or |
266 | +timely informing the device user about application notifications that |
267 | +were pending when the device comes back online. This in the face of |
268 | +applications not being allowed to be running all the time, and |
269 | +avoiding the resource cost of many applications all polling different services |
270 | +frequently. |
271 | + |
272 | +The push notification infrastructure is architected to guarantee at |
273 | +least best-effort with respect to these goals and beyond it, on the |
274 | +other end applications should not expect to be able to use and only |
275 | +rely on the push notification infrastructure to store application |
276 | +messages if they want ensure all their notification or messages are |
277 | +delivered, the infrastructure is not intended to be the only long term |
278 | +"inbox" storage for an application. |
279 | + |
280 | +To preserve overall throughput the infrastructure imposes some limits |
281 | +on applications: |
282 | + |
283 | + * message data payload is limited to 2K |
284 | + |
285 | + * when inserted all messages need to specify an expiration date after |
286 | + which they can be dropped and not delivered |
287 | + |
288 | + * an application is limited in the number of messages per token |
289 | + (application/user/device combination) that can be undelivered/pending at the |
290 | + same time (100 currently) |
291 | + |
292 | +replace_tag can be used to implement notifications for which the newest |
293 | +one replace the previous one if pending. |
294 | + |
295 | +clear_pending can be used to be deal with a pending message limit |
296 | +reached, possibly substituting the current undelivered messages with a |
297 | +more generic one. |
298 | + |
299 | +Applications using the push notification HTTP API should be robust |
300 | +against receiving 503 errors, retrying after waiting with increasing |
301 | +back-off. Later rate limits (signaled with the 429 status) may also come |
302 | +into play. |
303 | |
304 | === modified file 'docs/example-client/main.qml' |
305 | --- docs/example-client/main.qml 2014-09-10 14:38:40 +0000 |
306 | +++ docs/example-client/main.qml 2014-11-25 18:40:27 +0000 |
307 | @@ -26,9 +26,42 @@ |
308 | property alias nickEnabled: nickEdit.enabled |
309 | } |
310 | |
311 | + states: [ |
312 | + State { |
313 | + name: "no-push-token" |
314 | + when: (pushClient.token == "") |
315 | + PropertyChanges { target: nickEdit; readOnly: true} |
316 | + PropertyChanges { target: nickEdit; focus: true} |
317 | + PropertyChanges { target: messageEdit; enabled: false} |
318 | + PropertyChanges { target: loginButton; enabled: false} |
319 | + PropertyChanges { target: loginButton; text: "Login"} |
320 | + }, |
321 | + State { |
322 | + name: "push-token-not-registered" |
323 | + when: ((pushClient.token != "") && (chatClient.registered == false)) |
324 | + PropertyChanges { target: nickEdit; readOnly: false} |
325 | + PropertyChanges { target: nickEdit; text: ""} |
326 | + PropertyChanges { target: nickEdit; focus: true} |
327 | + PropertyChanges { target: messageEdit; enabled: false} |
328 | + PropertyChanges { target: loginButton; enabled: true} |
329 | + PropertyChanges { target: loginButton; text: "Login"} |
330 | + }, |
331 | + State { |
332 | + name: "registered" |
333 | + when: ((pushClient.token != "") && (chatClient.registered == true)) |
334 | + PropertyChanges { target: nickEdit; readOnly: true} |
335 | + PropertyChanges { target: nickEdit; text: "Your nick is " + chatClient.nick} |
336 | + PropertyChanges { target: messageEdit; focus: true} |
337 | + PropertyChanges { target: messageEdit; enabled: true} |
338 | + PropertyChanges { target: loginButton; enabled: true} |
339 | + PropertyChanges { target: loginButton; text: "Logout"} |
340 | + } |
341 | + ] |
342 | + |
343 | + state: "no-push-token" |
344 | + |
345 | ChatClient { |
346 | id: chatClient |
347 | - onRegisteredChanged: {nickEdit.registered()} |
348 | onError: {messageList.handle_error(msg)} |
349 | token: pushClient.token |
350 | } |
351 | @@ -44,7 +77,6 @@ |
352 | |
353 | TextField { |
354 | id: nickEdit |
355 | - focus: true |
356 | placeholderText: "Your nickname" |
357 | inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText | Qt.ImhPreferLowercase |
358 | anchors.left: parent.left |
359 | @@ -53,31 +85,17 @@ |
360 | anchors.leftMargin: units.gu(.5) |
361 | anchors.rightMargin: units.gu(1) |
362 | anchors.topMargin: units.gu(.5) |
363 | - function registered() { |
364 | - readOnly = true |
365 | - text = "Your nick is " + chatClient.nick |
366 | - messageEdit.focus = true |
367 | - messageEdit.enabled = true |
368 | - loginButton.text = "Logout" |
369 | - } |
370 | onAccepted: { loginButton.clicked() } |
371 | } |
372 | |
373 | Button { |
374 | id: loginButton |
375 | - text: chatClient.rgistered? "Logout": "Login" |
376 | anchors.top: nickEdit.top |
377 | anchors.right: parent.right |
378 | anchors.rightMargin: units.gu(.5) |
379 | onClicked: { |
380 | if (chatClient.nick) { // logout |
381 | chatClient.nick = "" |
382 | - text = "Login" |
383 | - nickEdit.enabled = true |
384 | - nickEdit.readOnly = false |
385 | - nickEdit.text = "" |
386 | - nickEdit.focus = true |
387 | - messageEdit.enabled = false |
388 | } else { // login |
389 | chatClient.nick = nickEdit.text |
390 | } |
391 | @@ -94,7 +112,6 @@ |
392 | anchors.rightMargin: units.gu(.5) |
393 | anchors.leftMargin: units.gu(.5) |
394 | placeholderText: "Your message" |
395 | - enabled: false |
396 | onAccepted: { |
397 | console.log("sending " + text) |
398 | var idx = text.indexOf(":") |
399 | |
400 | === modified file 'docs/example-client/manifest.json' |
401 | --- docs/example-client/manifest.json 2014-09-10 14:38:31 +0000 |
402 | +++ docs/example-client/manifest.json 2014-11-25 18:40:27 +0000 |
403 | @@ -15,5 +15,5 @@ |
404 | "maintainer": "Roberto Alsina <roberto.alsina@canonical.com>", |
405 | "name": "com.ubuntu.developer.ralsina.hello", |
406 | "title": "Hello", |
407 | - "version": "0.4.2" |
408 | + "version": "0.4.3" |
409 | } |
410 | |
411 | === modified file 'docs/lowlevel.txt' |
412 | --- docs/lowlevel.txt 2014-10-15 16:34:25 +0000 |
413 | +++ docs/lowlevel.txt 2014-11-25 18:40:27 +0000 |
414 | @@ -57,6 +57,8 @@ |
415 | The Register method takes as argument the APP_ID (in the example, com.ubuntu.music_music) and returns a token identifying the user |
416 | and device. For this to succeed the user **must** have an Ubuntu One account configured in the device. |
417 | |
418 | +In the case the Register method returns a "bad auth" error, the application should inform the user to generate new Ubuntu One tokens. |
419 | + |
420 | The APP_ID is as described in the `ApplicationId documentation <https://wiki.ubuntu.com/AppStore/Interfaces/ApplicationId>`__ |
421 | except that the version is treated as optional. Therefore both ``com.ubuntu.music_music`` and ``com.ubuntu.music_music_1.3.496`` |
422 | are valid. Keep in mind that while both versioned and unversioned APP_IDs are valid, they are still different and will affect |
423 | |
424 | === modified file 'launch_helper/kindpool.go' |
425 | --- launch_helper/kindpool.go 2014-08-20 12:42:51 +0000 |
426 | +++ launch_helper/kindpool.go 2014-11-25 18:40:27 +0000 |
427 | @@ -294,12 +294,13 @@ |
428 | } |
429 | payload, err := ioutil.ReadFile(args.FileOut) |
430 | if err != nil { |
431 | - pool.log.Errorf("unable to read output from helper: %v", err) |
432 | + pool.log.Errorf("unable to read output from %v helper: %v", args.AppId, err) |
433 | } else { |
434 | + pool.log.Infof("%v helper output: %#v", args.AppId, payload) |
435 | res := &HelperResult{Input: args.Input} |
436 | err = json.Unmarshal(payload, &res.HelperOutput) |
437 | if err != nil { |
438 | - pool.log.Debugf("failed to parse HelperOutput from helper output: %v", err) |
439 | + pool.log.Errorf("failed to parse HelperOutput from %v helper output: %v", args.AppId, err) |
440 | } else { |
441 | pool.chOut <- res |
442 | } |
443 | |
444 | === modified file 'launch_helper/legacy/legacy.go' |
445 | --- launch_helper/legacy/legacy.go 2014-08-21 18:03:49 +0000 |
446 | +++ launch_helper/legacy/legacy.go 2014-11-25 18:40:27 +0000 |
447 | @@ -55,7 +55,7 @@ |
448 | err error |
449 | } |
450 | |
451 | -func (lhl *legacyHelperLauncher) Launch(_, progname, f1, f2 string) (string, error) { |
452 | +func (lhl *legacyHelperLauncher) Launch(appId, progname, f1, f2 string) (string, error) { |
453 | comm := make(chan msg) |
454 | |
455 | go func() { |
456 | @@ -78,9 +78,8 @@ |
457 | p_err := cmd.Wait() |
458 | if p_err != nil { |
459 | // Helper failed or got killed, log output/errors |
460 | - lhl.log.Errorf("Legacy helper failed: %v", p_err) |
461 | - lhl.log.Errorf("Legacy helper failed. Stdout: %s", stdout) |
462 | - lhl.log.Errorf("Legacy helper failed. Stderr: %s", stderr) |
463 | + lhl.log.Errorf("Legacy helper failed: appId: %v, helper: %v, pid: %v, error: %v, stdout: %#v, stderr: %#v.", |
464 | + appId, progname, id, p_err, stdout.String(), stderr.String()) |
465 | } |
466 | lhl.done(id) |
467 | }() |
468 | |
469 | === modified file 'messaging/messaging_test.go' |
470 | --- messaging/messaging_test.go 2014-07-27 02:54:40 +0000 |
471 | +++ messaging/messaging_test.go 2014-11-25 18:40:27 +0000 |
472 | @@ -17,6 +17,7 @@ |
473 | package messaging |
474 | |
475 | import ( |
476 | + "sort" |
477 | "time" |
478 | |
479 | . "launchpad.net/gocheck" |
480 | @@ -121,6 +122,12 @@ |
481 | c.Check(payload.Actions[1], Equals, "action-1") |
482 | } |
483 | |
484 | +func (msg *MessagingSuite) checkTags(c *C, got, expected []string) { |
485 | + sort.Strings(got) |
486 | + sort.Strings(expected) |
487 | + c.Check(got, DeepEquals, expected) |
488 | +} |
489 | + |
490 | func (ms *MessagingSuite) TestTagsListsTags(c *C) { |
491 | mmu := New(ms.log) |
492 | f := func(s string) *launch_helper.Notification { |
493 | @@ -130,15 +137,15 @@ |
494 | |
495 | c.Check(mmu.Tags(ms.app), IsNil) |
496 | c.Assert(mmu.Present(ms.app, "notif1", f("one")), Equals, true) |
497 | - c.Check(mmu.Tags(ms.app), DeepEquals, []string{"one"}) |
498 | + ms.checkTags(c, mmu.Tags(ms.app), []string{"one"}) |
499 | c.Assert(mmu.Present(ms.app, "notif2", f("")), Equals, true) |
500 | - c.Check(mmu.Tags(ms.app), DeepEquals, []string{"one", ""}) |
501 | + ms.checkTags(c, mmu.Tags(ms.app), []string{"one", ""}) |
502 | // and an empty notification doesn't count |
503 | c.Assert(mmu.Present(ms.app, "notif3", &launch_helper.Notification{Tag: "X"}), Equals, false) |
504 | - c.Check(mmu.Tags(ms.app), DeepEquals, []string{"one", ""}) |
505 | + ms.checkTags(c, mmu.Tags(ms.app), []string{"one", ""}) |
506 | // and they go away if we remove one |
507 | mmu.RemoveNotification("notif1", false) |
508 | - c.Check(mmu.Tags(ms.app), DeepEquals, []string{""}) |
509 | + ms.checkTags(c, mmu.Tags(ms.app), []string{""}) |
510 | mmu.RemoveNotification("notif2", false) |
511 | c.Check(mmu.Tags(ms.app), IsNil) |
512 | } |
513 | @@ -169,9 +176,9 @@ |
514 | // app 1: "one", "two", ""; |
515 | // app 2: "one", "two"; |
516 | // app 3: "one", "" |
517 | - c.Assert(mm.Tags(app1), DeepEquals, []string{"one", "two", ""}) |
518 | - c.Assert(mm.Tags(app2), DeepEquals, []string{"one", "two"}) |
519 | - c.Assert(mm.Tags(app3), DeepEquals, []string{"one", ""}) |
520 | + ms.checkTags(c, mm.Tags(app1), []string{"one", "two", ""}) |
521 | + ms.checkTags(c, mm.Tags(app2), []string{"one", "two"}) |
522 | + ms.checkTags(c, mm.Tags(app3), []string{"one", ""}) |
523 | |
524 | // clearing a non-existent tag does nothing |
525 | c.Check(mm.Clear(app1, "foo"), Equals, 0) |
526 | |
527 | === modified file 'server/acceptance/suites/helpers.go' |
528 | --- server/acceptance/suites/helpers.go 2014-07-08 15:08:52 +0000 |
529 | +++ server/acceptance/suites/helpers.go 2014-11-25 18:40:27 +0000 |
530 | @@ -127,6 +127,11 @@ |
531 | c.Log(info) |
532 | continue |
533 | } |
534 | + if strings.HasPrefix(info, "DEBUG ") && !strings.HasPrefix(info, "DEBUG session(") { |
535 | + // skip non session DEBUG logs |
536 | + c.Log(info) |
537 | + continue |
538 | + } |
539 | logs <- info |
540 | } |
541 | }() |
542 | @@ -136,16 +141,12 @@ |
543 | const ( |
544 | DevListeningOnPat = "INFO listening for devices on " |
545 | HTTPListeningOnPat = "INFO listening for http on " |
546 | - debugPrefix = "DEBUG " |
547 | ) |
548 | |
549 | -// ExtractListeningAddr goes over logs ignoring DEBUG lines |
550 | -// until a line starting with pat and returns the rest of that line. |
551 | +// ExtractListeningAddr goes over logs until a line starting with pat |
552 | +// and returns the rest of that line. |
553 | func ExtractListeningAddr(c *C, logs <-chan string, pat string) string { |
554 | for line := range logs { |
555 | - if strings.HasPrefix(line, debugPrefix) { |
556 | - continue |
557 | - } |
558 | if !strings.HasPrefix(line, pat) { |
559 | c.Fatalf("matching %v: %v", pat, line) |
560 | } |
561 | |
562 | === modified file 'server/api/handlers.go' |
563 | --- server/api/handlers.go 2014-07-14 15:23:17 +0000 |
564 | +++ server/api/handlers.go 2014-11-25 18:40:27 +0000 |
565 | @@ -23,6 +23,7 @@ |
566 | "encoding/json" |
567 | "fmt" |
568 | "io" |
569 | + "mime" |
570 | "net/http" |
571 | "time" |
572 | |
573 | @@ -98,6 +99,12 @@ |
574 | "Wrong request method, should be POST", |
575 | nil, |
576 | } |
577 | + ErrWrongRequestMethodGET = &APIError{ |
578 | + http.StatusMethodNotAllowed, |
579 | + invalidRequest, |
580 | + "Wrong request method, should be GET", |
581 | + nil, |
582 | + } |
583 | ErrMalformedJSONObject = &APIError{ |
584 | http.StatusBadRequest, |
585 | invalidRequest, |
586 | @@ -268,7 +275,8 @@ |
587 | if err := checkContentLength(request, maxBodySize); err != nil { |
588 | return err |
589 | } |
590 | - if request.Header.Get("Content-Type") != JSONMediaType { |
591 | + mediaType, _, err := mime.ParseMediaType(request.Header.Get("Content-Type")) |
592 | + if err != nil || mediaType != JSONMediaType { |
593 | return ErrWrongContentType |
594 | } |
595 | return nil |
596 | @@ -452,14 +460,17 @@ |
597 | if err != nil { |
598 | switch err { |
599 | case store.ErrUnknownToken: |
600 | + ctx.logger.Debugf("notify: %v %v unknown", ucast.AppId, ucast.Token) |
601 | return nil, ErrUnknownToken |
602 | case store.ErrUnauthorized: |
603 | + ctx.logger.Debugf("notify: %v %v unauthorized", ucast.AppId, ucast.Token) |
604 | return nil, ErrUnauthorized |
605 | default: |
606 | ctx.logger.Errorf("could not resolve token: %v", err) |
607 | return nil, ErrCouldNotResolveToken |
608 | } |
609 | } |
610 | + ctx.logger.Debugf("notify: %v %v -> %v", ucast.AppId, ucast.Token, chanId) |
611 | |
612 | _, notifs, meta, err := sto.GetChannelUnfiltered(chanId) |
613 | if err != nil { |
614 | @@ -491,6 +502,7 @@ |
615 | if ucast.ClearPending { |
616 | scrubCriteria = []string{ucast.AppId} |
617 | } else if forApp >= ctx.storage.GetMaxNotificationsPerApplication() { |
618 | + ctx.logger.Debugf("notify: %v %v too many pending", ucast.AppId, chanId) |
619 | return nil, apiErrorWithExtra(ErrTooManyPendingNotifications, |
620 | &last.Payload) |
621 | } else if replaceable > 0 { |
622 | @@ -518,6 +530,8 @@ |
623 | } |
624 | |
625 | ctx.broker.Unicast(chanId) |
626 | + |
627 | + ctx.logger.Debugf("notify: ok %v %v id:%v clear:%v replace:%v expired:%v", ucast.AppId, chanId, msgId, ucast.ClearPending, replaceable, expired) |
628 | return nil, nil |
629 | } |
630 | |
631 | |
632 | === modified file 'server/api/handlers_test.go' |
633 | --- server/api/handlers_test.go 2014-07-15 15:03:49 +0000 |
634 | +++ server/api/handlers_test.go 2014-11-25 18:40:27 +0000 |
635 | @@ -362,7 +362,7 @@ |
636 | } |
637 | sto := store.NewInMemoryPendingStore() |
638 | bsend := &checkBrokerSending{store: sto} |
639 | - ctx := &context{testStoreAccess(nil), bsend, nil} |
640 | + ctx := &context{testStoreAccess(nil), bsend, s.testlog} |
641 | payload := json.RawMessage(`{"a": 1}`) |
642 | res, apiErr := doUnicast(ctx, sto, &Unicast{ |
643 | UserId: "user1", |
644 | @@ -487,7 +487,7 @@ |
645 | sto.AppendToUnicastChannel(chanId, "app1", n, "m4", expire) |
646 | |
647 | bsend := &checkBrokerSending{store: sto} |
648 | - ctx := &context{testStoreAccess(nil), bsend, nil} |
649 | + ctx := &context{testStoreAccess(nil), bsend, s.testlog} |
650 | payload := json.RawMessage(`{"a": 1}`) |
651 | res, apiErr := doUnicast(ctx, sto, &Unicast{ |
652 | UserId: "user1", |
653 | @@ -528,7 +528,7 @@ |
654 | sto.AppendToUnicastChannel(chanId, "app1", n, "m1", expire) |
655 | |
656 | bsend := &checkBrokerSending{store: sto} |
657 | - ctx := &context{testStoreAccess(nil), bsend, nil} |
658 | + ctx := &context{testStoreAccess(nil), bsend, s.testlog} |
659 | payload := json.RawMessage(`{"a": 1}`) |
660 | res, apiErr := doUnicast(ctx, sto, &Unicast{ |
661 | UserId: "user1", |
662 | @@ -634,7 +634,7 @@ |
663 | sto.AppendToUnicastChannel(chanId, "app1", n, "m4", expire) |
664 | |
665 | bsend := &checkBrokerSending{store: sto} |
666 | - ctx := &context{testStoreAccess(nil), bsend, nil} |
667 | + ctx := &context{testStoreAccess(nil), bsend, s.testlog} |
668 | payload := json.RawMessage(`{"a": 1}`) |
669 | res, apiErr := doUnicast(ctx, sto, &Unicast{ |
670 | UserId: "user1", |
671 | @@ -933,6 +933,22 @@ |
672 | checkError(c, response, ErrWrongContentType) |
673 | } |
674 | |
675 | +func (s *handlersSuite) TestContentTypeWithCharset(c *C) { |
676 | + testServer := httptest.NewServer(&JSONPostHandler{}) |
677 | + defer testServer.Close() |
678 | + |
679 | + dataString := `{"foo":"bar"}` |
680 | + |
681 | + request := newPostRequest("/", &Broadcast{ |
682 | + Channel: "some-channel", |
683 | + ExpireOn: future, |
684 | + Data: json.RawMessage([]byte(dataString)), |
685 | + }, testServer) |
686 | + request.Header.Set("Content-Type", "application/json; charset=UTF-8") |
687 | + result := checkRequestAsPost(request, 1024) |
688 | + c.Assert(result, IsNil) |
689 | +} |
690 | + |
691 | func (s *handlersSuite) TestCannotBroadcastNonPostMessages(c *C) { |
692 | testServer := httptest.NewServer(&JSONPostHandler{}) |
693 | defer testServer.Close() |
694 | @@ -965,7 +981,7 @@ |
695 | return sto, nil |
696 | }) |
697 | bsend := testBrokerSending{make(chan store.InternalChannelId, 1)} |
698 | - testServer := httptest.NewServer(MakeHandlersMux(storage, bsend, nil)) |
699 | + testServer := httptest.NewServer(MakeHandlersMux(storage, bsend, s.testlog)) |
700 | defer testServer.Close() |
701 | |
702 | payload := json.RawMessage(`{"foo":"bar"}`) |
703 | @@ -1049,7 +1065,7 @@ |
704 | return sto, nil |
705 | }) |
706 | bsend := testBrokerSending{make(chan store.InternalChannelId, 1)} |
707 | - testServer := httptest.NewServer(MakeHandlersMux(storage, bsend, nil)) |
708 | + testServer := httptest.NewServer(MakeHandlersMux(storage, bsend, s.testlog)) |
709 | defer testServer.Close() |
710 | |
711 | request := newPostRequest("/register", &Registration{ |
712 | |
713 | === modified file 'server/broker/testsuite/suite.go' |
714 | --- server/broker/testsuite/suite.go 2014-09-25 11:16:38 +0000 |
715 | +++ server/broker/testsuite/suite.go 2014-11-25 18:40:27 +0000 |
716 | @@ -66,12 +66,12 @@ |
717 | |
718 | func (s *CommonBrokerSuite) TestSanity(c *C) { |
719 | sto := store.NewInMemoryPendingStore() |
720 | - b := s.MakeBroker(sto, testBrokerConfig, nil) |
721 | + b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
722 | c.Check(s.RevealSession(b, "FOO"), IsNil) |
723 | } |
724 | |
725 | func (s *CommonBrokerSuite) TestStartStop(c *C) { |
726 | - b := s.MakeBroker(nil, testBrokerConfig, nil) |
727 | + b := s.MakeBroker(nil, testBrokerConfig, s.testlog) |
728 | b.Start() |
729 | c.Check(b.Running(), Equals, true) |
730 | b.Start() |
731 | @@ -82,7 +82,7 @@ |
732 | |
733 | func (s *CommonBrokerSuite) TestRegistration(c *C) { |
734 | sto := store.NewInMemoryPendingStore() |
735 | - b := s.MakeBroker(sto, testBrokerConfig, nil) |
736 | + b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
737 | b.Start() |
738 | defer b.Stop() |
739 | sess, err := b.Register(&protocol.ConnectMsg{ |
740 | @@ -112,7 +112,7 @@ |
741 | |
742 | func (s *CommonBrokerSuite) TestRegistrationBrokenLevels(c *C) { |
743 | sto := store.NewInMemoryPendingStore() |
744 | - b := s.MakeBroker(sto, testBrokerConfig, nil) |
745 | + b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
746 | b.Start() |
747 | defer b.Stop() |
748 | _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1", Levels: map[string]int64{"z": 5}}, s.MakeTracker("s1")) |
749 | @@ -121,7 +121,7 @@ |
750 | |
751 | func (s *CommonBrokerSuite) TestRegistrationInfoErrors(c *C) { |
752 | sto := store.NewInMemoryPendingStore() |
753 | - b := s.MakeBroker(sto, testBrokerConfig, nil) |
754 | + b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
755 | b.Start() |
756 | defer b.Stop() |
757 | info := map[string]interface{}{ |
758 | @@ -140,7 +140,7 @@ |
759 | notification1 := json.RawMessage(`{"m": "M"}`) |
760 | muchLater := time.Now().Add(10 * time.Minute) |
761 | sto.AppendToChannel(store.SystemInternalChannelId, notification1, muchLater) |
762 | - b := s.MakeBroker(sto, testBrokerConfig, nil) |
763 | + b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
764 | b.Start() |
765 | defer b.Stop() |
766 | sess, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, s.MakeTracker("s1")) |
767 | @@ -166,7 +166,7 @@ |
768 | |
769 | func (s *CommonBrokerSuite) TestRegistrationLastWins(c *C) { |
770 | sto := store.NewInMemoryPendingStore() |
771 | - b := s.MakeBroker(sto, testBrokerConfig, nil) |
772 | + b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
773 | b.Start() |
774 | defer b.Stop() |
775 | sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, s.MakeTracker("s1")) |
776 | @@ -197,7 +197,7 @@ |
777 | sto := store.NewInMemoryPendingStore() |
778 | notification1 := json.RawMessage(`{"m": "M"}`) |
779 | decoded1 := map[string]interface{}{"m": "M"} |
780 | - b := s.MakeBroker(sto, testBrokerConfig, nil) |
781 | + b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
782 | b.Start() |
783 | defer b.Stop() |
784 | sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"}, s.MakeTracker("s1")) |
785 | @@ -278,7 +278,7 @@ |
786 | notification2 := json.RawMessage(`{"m": "M2"}`) |
787 | chanId1 := store.UnicastInternalChannelId("dev1", "dev1") |
788 | chanId2 := store.UnicastInternalChannelId("dev2", "dev2") |
789 | - b := s.MakeBroker(sto, testBrokerConfig, nil) |
790 | + b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
791 | b.Start() |
792 | defer b.Stop() |
793 | sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev1"}, s.MakeTracker("s1")) |
794 | @@ -312,7 +312,7 @@ |
795 | sto := store.NewInMemoryPendingStore() |
796 | notification1 := json.RawMessage(`{"m": "M1"}`) |
797 | chanId1 := store.UnicastInternalChannelId("dev3", "dev3") |
798 | - b := s.MakeBroker(sto, testBrokerConfig, nil) |
799 | + b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
800 | b.Start() |
801 | defer b.Stop() |
802 | sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev3"}, s.MakeTracker("s1")) |
803 | @@ -354,7 +354,7 @@ |
804 | |
805 | func (s *CommonBrokerSuite) TestSessionFeed(c *C) { |
806 | sto := store.NewInMemoryPendingStore() |
807 | - b := s.MakeBroker(sto, testBrokerConfig, nil) |
808 | + b := s.MakeBroker(sto, testBrokerConfig, s.testlog) |
809 | b.Start() |
810 | defer b.Stop() |
811 | sess1, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev3"}, s.MakeTracker("s1")) |
812 | |
813 | === modified file 'server/listener/listener_test.go' |
814 | --- server/listener/listener_test.go 2014-09-25 11:20:00 +0000 |
815 | +++ server/listener/listener_test.go 2014-11-25 18:40:27 +0000 |
816 | @@ -18,6 +18,7 @@ |
817 | |
818 | import ( |
819 | "crypto/tls" |
820 | + "io" |
821 | "net" |
822 | "os/exec" |
823 | "regexp" |
824 | @@ -112,7 +113,7 @@ |
825 | conn.SetDeadline(time.Now().Add(10 * time.Second)) |
826 | var buf [1]byte |
827 | for { |
828 | - _, err := conn.Read(buf[:]) |
829 | + _, err := io.ReadFull(conn, buf[:]) |
830 | if err != nil { |
831 | return err |
832 | } |
833 | @@ -137,7 +138,7 @@ |
834 | |
835 | func testReadByte(c *C, conn net.Conn, expected uint32) { |
836 | var buf [1]byte |
837 | - _, err := conn.Read(buf[:]) |
838 | + _, err := io.ReadFull(conn, buf[:]) |
839 | c.Check(err, IsNil) |
840 | c.Check(buf[0], Equals, byte(expected)) |
841 | } |