Merge lp:~pedronis/ubuntu-push/automatic-into-krillin-rtm into lp:ubuntu-push/krillin-rtm
- automatic-into-krillin-rtm
- Merge into krillin-rtm
Status: | Merged |
---|---|
Approved by: | Roberto Alsina |
Approved revision: | 142 |
Merged at revision: | 141 |
Proposed branch: | lp:~pedronis/ubuntu-push/automatic-into-krillin-rtm |
Merge into: | lp:ubuntu-push/krillin-rtm |
Diff against target: |
6757 lines (+2552/-853) 72 files modified
.precommit (+21/-19) PACKAGE_DEPS (+1/-0) bus/accounts/accounts.go (+310/-0) bus/accounts/accounts_test.go (+271/-0) bus/connectivity/connectivity.go (+91/-32) bus/connectivity/connectivity_test.go (+129/-56) bus/connectivity/webchecker.go (+12/-3) bus/connectivity/webchecker_test.go (+9/-3) bus/endpoint.go (+10/-5) bus/haptic/haptic.go (+9/-2) bus/haptic/haptic_test.go (+35/-6) bus/networkmanager/networkmanager.go (+10/-10) bus/networkmanager/networkmanager_test.go (+23/-15) bus/notifications/raw.go (+1/-1) bus/notifications/raw_test.go (+7/-4) bus/testing/testing_endpoint.go (+85/-29) bus/testing/testing_endpoint_test.go (+38/-18) click/cappinfo/cappinfo.go (+31/-0) click/cclick/cclick.go (+1/-0) click/click.go (+4/-0) click/click_test.go (+14/-0) client/client.go (+22/-48) client/client_test.go (+69/-108) client/service/postal.go (+12/-2) client/service/postal_test.go (+17/-0) client/service/service.go (+2/-0) client/service/service_test.go (+28/-5) client/session/seenstate/seenstate.go (+6/-1) client/session/seenstate/sqlseenstate.go (+5/-0) client/session/seenstate/sqlseenstate_test.go (+9/-0) client/session/session.go (+208/-82) client/session/session_test.go (+364/-178) docs/Makefile (+4/-0) docs/_common.txt (+58/-44) docs/example-client/components/ChatClient.qml (+2/-2) docs/example-client/helloHelper-apparmor.json (+1/-0) docs/example-client/main.qml (+47/-18) docs/example-client/manifest.json (+2/-2) docs/example-server/app.js (+31/-13) docs/example-server/config/config.js (+1/-1) docs/example-server/index.html (+2/-0) docs/example-server/notify-form.html (+61/-6) docs/example-server/test/app_test.js (+128/-32) docs/highlevel.txt (+2/-2) docs/lowlevel.txt (+4/-2) launch_helper/kindpool_test.go (+1/-1) logger/logger.go (+12/-7) logger/logger_test.go (+12/-0) messaging/messaging_test.go (+14/-7) poller/poller.go (+8/-2) server/acceptance/kit/api.go (+14/-3) server/acceptance/suites/helpers.go (+8/-7) server/acceptance/suites/suite.go (+2/-1) server/api/handlers.go (+15/-1) server/api/handlers_test.go (+22/-6) server/broker/broker.go (+6/-1) server/broker/simple/simple.go (+1/-1) server/broker/simple/suite_test.go (+10/-0) server/broker/testsuite/suite.go (+31/-29) server/dev/server.go (+3/-1) server/listener/listener.go (+12/-2) server/listener/listener_test.go (+33/-6) server/runner_devices.go (+2/-2) server/runner_test.go (+5/-2) server/session/session.go (+3/-3) server/session/session_test.go (+7/-6) server/tlsconfig.go (+12/-0) sounds/sounds.go (+15/-2) sounds/sounds_test.go (+44/-3) testing/helpers.go (+9/-0) util/redialer.go (+45/-11) util/redialer_states.gv (+9/-0) |
To merge this branch: | bzr merge lp:~pedronis/ubuntu-push/automatic-into-krillin-rtm |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Roberto Alsina (community) | Approve | ||
Review via email: mp+254285@code.launchpad.net |
Commit message
[Bret Barker, Samuele Pedroni]
* Partial fix of lp:1390663
- Remove SessionStateSettle sleep on wake, + more debug logging.
- Don't hold a lock for a long time on handleErrConn, trigger
autoRedial on Error more actively.
[John Lenton]
* Refactor code maintaining session (better fix for lp:1390663)
* Prune the XDG path from the beginning of accounts-set sound files.
* Use accounts' settings from sound and haptic.
* Add an explicit check and log message for nil error on webcheck's
CopyN.
* Move logging to info; improve logging of legacy helper errors;
switch some logs to error from debug.
[Bret Barker]
* Add SIGQUIT handler to spit out stack dumps; more logging
tweaks. [client, server]
* Log line nums, enabled when logLevel = debug.
[Samuele Pedroni]
* Unit test improvements
* Workaround gc issue with 1.3 and 32 bits.
[Roberto Ralsina]
* Example and docs improvements.
[ Guillermo Gonzalez ]
* When The server reply 401 on /register, make the DBus call to Register
return ErrBadAuth instead of ErrBadRequest.
* Fix click hook for legacy apps
* Add ClearCookie method to the session and call it from handleAccountsC
* click.AppId.
and then fallback to icon+"-symbolic"
Description of the change
Roberto Alsina (ralsina) : | # |
Preview Diff
1 | === modified file '.precommit' | |||
2 | --- .precommit 2014-01-23 10:03:39 +0000 | |||
3 | +++ .precommit 2015-03-26 16:42:21 +0000 | |||
4 | @@ -5,25 +5,27 @@ | |||
5 | 5 | # And put this here-document in ~/.bazaar/plugins/precommit_script.py: | 5 | # And put this here-document in ~/.bazaar/plugins/precommit_script.py: |
6 | 6 | <<EOF | 6 | <<EOF |
7 | 7 | import os | 7 | import os |
27 | 8 | import subprocess | 8 | |
28 | 9 | from bzrlib.mutabletree import MutableTree | 9 | if not os.getenv("SKIP_COMMIT_HOOK"): |
29 | 10 | from bzrlib import errors | 10 | import subprocess |
30 | 11 | 11 | from bzrlib.mutabletree import MutableTree | |
31 | 12 | def start_commit_hook(*_): | 12 | from bzrlib import errors |
32 | 13 | """This hook will execute '.precommit' script from root path of the bazaar | 13 | |
33 | 14 | branch. Commit will be canceled if precommit fails.""" | 14 | def start_commit_hook(*_): |
34 | 15 | 15 | """This hook will execute '.precommit' script from root path of the bazaar | |
35 | 16 | # this hook only makes sense if a precommit file exist. | 16 | branch. Commit will be canceled if precommit fails.""" |
36 | 17 | if not os.path.exists(".precommit"): | 17 | |
37 | 18 | return | 18 | # this hook only makes sense if a precommit file exist. |
38 | 19 | try: | 19 | if not os.path.exists(".precommit"): |
39 | 20 | subprocess.check_call(os.path.abspath(".precommit")) | 20 | return |
40 | 21 | # if precommit fails (process return not zero) cancel commit. | 21 | try: |
41 | 22 | except subprocess.CalledProcessError: | 22 | subprocess.check_call(os.path.abspath(".precommit")) |
42 | 23 | raise errors.BzrError("pre commit check failed.") | 23 | # if precommit fails (process return not zero) cancel commit. |
43 | 24 | 24 | except subprocess.CalledProcessError: | |
44 | 25 | MutableTree.hooks.install_named_hook('start_commit', start_commit_hook, | 25 | raise errors.BzrError("pre commit check failed (set SKIP_COMMIT_HOOK to skip).") |
45 | 26 | 'Run "precommit" script on start_commit') | 26 | |
46 | 27 | MutableTree.hooks.install_named_hook('start_commit', start_commit_hook, | ||
47 | 28 | 'Run "precommit" script on start_commit') | ||
48 | 27 | EOF | 29 | EOF |
49 | 28 | 30 | ||
50 | 29 | make check-format # or whatever | 31 | make check-format # or whatever |
51 | 30 | 32 | ||
52 | === modified file 'PACKAGE_DEPS' | |||
53 | --- PACKAGE_DEPS 2014-09-05 10:48:36 +0000 | |||
54 | +++ PACKAGE_DEPS 2015-03-26 16:42:21 +0000 | |||
55 | @@ -12,3 +12,4 @@ | |||
56 | 12 | libclick-0.4-dev | 12 | libclick-0.4-dev |
57 | 13 | liburl-dispatcher1-dev | 13 | liburl-dispatcher1-dev |
58 | 14 | libaccounts-glib-dev | 14 | libaccounts-glib-dev |
59 | 15 | system-image-dbus | ||
60 | 15 | 16 | ||
61 | === added directory 'bus/accounts' | |||
62 | === added file 'bus/accounts/accounts.go' | |||
63 | --- bus/accounts/accounts.go 1970-01-01 00:00:00 +0000 | |||
64 | +++ bus/accounts/accounts.go 2015-03-26 16:42:21 +0000 | |||
65 | @@ -0,0 +1,310 @@ | |||
66 | 1 | /* | ||
67 | 2 | Copyright 2013-2015 Canonical Ltd. | ||
68 | 3 | |||
69 | 4 | This program is free software: you can redistribute it and/or modify it | ||
70 | 5 | under the terms of the GNU General Public License version 3, as published | ||
71 | 6 | by the Free Software Foundation. | ||
72 | 7 | |||
73 | 8 | This program is distributed in the hope that it will be useful, but | ||
74 | 9 | WITHOUT ANY WARRANTY; without even the implied warranties of | ||
75 | 10 | MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
76 | 11 | PURPOSE. See the GNU General Public License for more details. | ||
77 | 12 | |||
78 | 13 | You should have received a copy of the GNU General Public License along | ||
79 | 14 | with this program. If not, see <http://www.gnu.org/licenses/>. | ||
80 | 15 | */ | ||
81 | 16 | // accounts exposes some properties that're stored in org.freedesktop.Accounts | ||
82 | 17 | // (specifically, the ones that we need are all under | ||
83 | 18 | // com.ubuntu.touch.AccountsService.Sound). | ||
84 | 19 | package accounts | ||
85 | 20 | |||
86 | 21 | import ( | ||
87 | 22 | "fmt" | ||
88 | 23 | "os/user" | ||
89 | 24 | "strings" | ||
90 | 25 | "sync" | ||
91 | 26 | |||
92 | 27 | "launchpad.net/go-dbus/v1" | ||
93 | 28 | "launchpad.net/go-xdg/v0" | ||
94 | 29 | |||
95 | 30 | "launchpad.net/ubuntu-push/bus" | ||
96 | 31 | "launchpad.net/ubuntu-push/logger" | ||
97 | 32 | ) | ||
98 | 33 | |||
99 | 34 | // accounts lives on a well-known bus.Address. | ||
100 | 35 | // | ||
101 | 36 | // Note this one isn't it: the interface is for dbus.properties, and the path | ||
102 | 37 | // is missing the UID. | ||
103 | 38 | var BusAddress bus.Address = bus.Address{ | ||
104 | 39 | Interface: "org.freedesktop.DBus.Properties", | ||
105 | 40 | Path: "/org/freedesktop/Accounts/User", | ||
106 | 41 | Name: "org.freedesktop.Accounts", | ||
107 | 42 | } | ||
108 | 43 | |||
109 | 44 | const accountsSoundIface = "com.ubuntu.touch.AccountsService.Sound" | ||
110 | 45 | |||
111 | 46 | type Accounts interface { | ||
112 | 47 | // Start() sets up the asynchronous updating of properties, and does the first update. | ||
113 | 48 | Start() error | ||
114 | 49 | // Cancel() stops the asynchronous updating of properties. | ||
115 | 50 | Cancel() error | ||
116 | 51 | // SilentMode() tells you whether the device is in silent mode. | ||
117 | 52 | SilentMode() bool | ||
118 | 53 | // Vibrate() tells you whether the device is allowed to vibrate. | ||
119 | 54 | Vibrate() bool | ||
120 | 55 | // MessageSoundFile() tells you the default sound filename. | ||
121 | 56 | MessageSoundFile() string | ||
122 | 57 | String() string | ||
123 | 58 | } | ||
124 | 59 | |||
125 | 60 | // Accounts tracks the relevant bits of configuration. Nothing directly | ||
126 | 61 | // accessible because it is updated asynchronously, so use the accessors. | ||
127 | 62 | type accounts struct { | ||
128 | 63 | endp bus.Endpoint | ||
129 | 64 | log logger.Logger | ||
130 | 65 | silent bool | ||
131 | 66 | vibrate bool | ||
132 | 67 | vibrateSilentMode bool | ||
133 | 68 | messageSound string | ||
134 | 69 | cancellable bus.Cancellable | ||
135 | 70 | lck sync.Mutex | ||
136 | 71 | updaters map[string]func(dbus.Variant) | ||
137 | 72 | } | ||
138 | 73 | |||
139 | 74 | // sets up a new Accounts structure, ready to be Start()ed. | ||
140 | 75 | func New(endp bus.Endpoint, log logger.Logger) Accounts { | ||
141 | 76 | a := &accounts{ | ||
142 | 77 | endp: endp, | ||
143 | 78 | log: log, | ||
144 | 79 | } | ||
145 | 80 | |||
146 | 81 | a.updaters = map[string]func(dbus.Variant){ | ||
147 | 82 | "SilentMode": a.updateSilentMode, | ||
148 | 83 | "IncomingMessageVibrate": a.updateVibrate, | ||
149 | 84 | "IncomingMessageVibrateSilentMode": a.updateVibrateSilentMode, | ||
150 | 85 | "IncomingMessageSound": a.updateMessageSound, | ||
151 | 86 | } | ||
152 | 87 | |||
153 | 88 | return a | ||
154 | 89 | } | ||
155 | 90 | |||
156 | 91 | // sets up the asynchronous updating of properties, and does the first update. | ||
157 | 92 | func (a *accounts) Start() error { | ||
158 | 93 | err := a.startWatch() | ||
159 | 94 | if err != nil { | ||
160 | 95 | return err | ||
161 | 96 | } | ||
162 | 97 | a.update() | ||
163 | 98 | return nil | ||
164 | 99 | } | ||
165 | 100 | |||
166 | 101 | // does sets up the watch on the PropertiesChanged signal. Separate from Start | ||
167 | 102 | // because it holds a lock. | ||
168 | 103 | func (a *accounts) startWatch() error { | ||
169 | 104 | cancellable, err := a.endp.WatchSignal("PropertiesChanged", a.propsHandler, a.bailoutHandler) | ||
170 | 105 | if err != nil { | ||
171 | 106 | a.log.Errorf("unable to watch for property changes: %v", err) | ||
172 | 107 | return err | ||
173 | 108 | } | ||
174 | 109 | |||
175 | 110 | a.lck.Lock() | ||
176 | 111 | defer a.lck.Unlock() | ||
177 | 112 | if a.cancellable != nil { | ||
178 | 113 | panic("tried to start Accounts twice?") | ||
179 | 114 | } | ||
180 | 115 | a.cancellable = cancellable | ||
181 | 116 | |||
182 | 117 | return nil | ||
183 | 118 | } | ||
184 | 119 | |||
185 | 120 | // cancel the asynchronous updating of properties. | ||
186 | 121 | func (a *accounts) Cancel() error { | ||
187 | 122 | return a.cancellable.Cancel() | ||
188 | 123 | } | ||
189 | 124 | |||
190 | 125 | // slightly shorter than %#v | ||
191 | 126 | func (a *accounts) String() string { | ||
192 | 127 | return fmt.Sprintf("&accounts{silent: %t, vibrate: %t, vibratesilent: %t, messageSound: %q}", | ||
193 | 128 | a.silent, a.vibrate, a.vibrateSilentMode, a.messageSound) | ||
194 | 129 | } | ||
195 | 130 | |||
196 | 131 | // merely log that the watch loop has bailed; not much we can do. | ||
197 | 132 | func (a *accounts) bailoutHandler() { | ||
198 | 133 | a.log.Debugf("loop bailed out") | ||
199 | 134 | } | ||
200 | 135 | |||
201 | 136 | // handle PropertiesChanged, which is described in | ||
202 | 137 | // http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties | ||
203 | 138 | func (a *accounts) propsHandler(ns ...interface{}) { | ||
204 | 139 | if len(ns) != 3 { | ||
205 | 140 | a.log.Errorf("PropertiesChanged delivered %d things instead of 3.", len(ns)) | ||
206 | 141 | return | ||
207 | 142 | } | ||
208 | 143 | |||
209 | 144 | iface, ok := ns[0].(string) | ||
210 | 145 | if !ok { | ||
211 | 146 | a.log.Errorf("PropertiesChanged 1st param not a string: %#v.", ns[0]) | ||
212 | 147 | return | ||
213 | 148 | } | ||
214 | 149 | if iface != accountsSoundIface { | ||
215 | 150 | a.log.Debugf("PropertiesChanged for %#v, ignoring.", iface) | ||
216 | 151 | return | ||
217 | 152 | } | ||
218 | 153 | changed, ok := ns[1].(map[interface{}]interface{}) | ||
219 | 154 | if !ok { | ||
220 | 155 | a.log.Errorf("PropertiesChanged 2nd param not a map: %#v.", ns[1]) | ||
221 | 156 | return | ||
222 | 157 | } | ||
223 | 158 | if len(changed) != 0 { | ||
224 | 159 | // not seen in the wild, but easy to implement properly (ie | ||
225 | 160 | // using the values we're given) if it starts to | ||
226 | 161 | // happen. Meanwhile just do a full update. | ||
227 | 162 | a.log.Infof("PropertiesChanged provided 'changed'; reverting to full update.") | ||
228 | 163 | a.update() | ||
229 | 164 | return | ||
230 | 165 | } | ||
231 | 166 | invalid, ok := ns[2].([]interface{}) | ||
232 | 167 | if !ok { | ||
233 | 168 | a.log.Errorf("PropertiesChanged 3rd param not a list of properties: %#v.", ns[2]) | ||
234 | 169 | return | ||
235 | 170 | } | ||
236 | 171 | a.log.Debugf("props changed: %#v.", invalid) | ||
237 | 172 | switch len(invalid) { | ||
238 | 173 | case 0: | ||
239 | 174 | // nothing to do? | ||
240 | 175 | a.log.Debugf("PropertiesChanged 3rd param is empty; doing nothing.") | ||
241 | 176 | case 1: | ||
242 | 177 | // the common case right now | ||
243 | 178 | k, ok := invalid[0].(string) | ||
244 | 179 | if !ok { | ||
245 | 180 | a.log.Errorf("PropertiesChanged 3rd param's only entry not a string: %#v.", invalid[0]) | ||
246 | 181 | return | ||
247 | 182 | } | ||
248 | 183 | updater, ok := a.updaters[k] | ||
249 | 184 | if ok { | ||
250 | 185 | var v dbus.Variant | ||
251 | 186 | err := a.endp.Call("Get", []interface{}{accountsSoundIface, k}, &v) | ||
252 | 187 | if err != nil { | ||
253 | 188 | a.log.Errorf("when calling Get for %s: %v", k, err) | ||
254 | 189 | return | ||
255 | 190 | } | ||
256 | 191 | a.log.Debugf("Get for %s got %#v.", k, v) | ||
257 | 192 | // updaters must be called with the lock held | ||
258 | 193 | a.lck.Lock() | ||
259 | 194 | defer a.lck.Unlock() | ||
260 | 195 | updater(v) | ||
261 | 196 | a.log.Debugf("updated %s.", k) | ||
262 | 197 | } | ||
263 | 198 | default: | ||
264 | 199 | // not seen in the wild, but we probably want to drop to a | ||
265 | 200 | // full update if getting more than one change anyway. | ||
266 | 201 | a.log.Infof("PropertiesChanged provided more than one 'invalid'; reverting to full update.") | ||
267 | 202 | a.update() | ||
268 | 203 | } | ||
269 | 204 | } | ||
270 | 205 | |||
271 | 206 | func (a *accounts) updateSilentMode(vsilent dbus.Variant) { | ||
272 | 207 | silent, ok := vsilent.Value.(bool) | ||
273 | 208 | if !ok { | ||
274 | 209 | a.log.Errorf("SilentMode needed a bool.") | ||
275 | 210 | return | ||
276 | 211 | } | ||
277 | 212 | |||
278 | 213 | a.silent = silent | ||
279 | 214 | } | ||
280 | 215 | |||
281 | 216 | func (a *accounts) updateVibrate(vvibrate dbus.Variant) { | ||
282 | 217 | vibrate, ok := vvibrate.Value.(bool) | ||
283 | 218 | if !ok { | ||
284 | 219 | a.log.Errorf("IncomingMessageVibrate needed a bool.") | ||
285 | 220 | return | ||
286 | 221 | } | ||
287 | 222 | |||
288 | 223 | a.vibrate = vibrate | ||
289 | 224 | } | ||
290 | 225 | |||
291 | 226 | func (a *accounts) updateVibrateSilentMode(vvibrateSilentMode dbus.Variant) { | ||
292 | 227 | vibrateSilentMode, ok := vvibrateSilentMode.Value.(bool) | ||
293 | 228 | if !ok { | ||
294 | 229 | a.log.Errorf("IncomingMessageVibrateSilentMode needed a bool.") | ||
295 | 230 | return | ||
296 | 231 | } | ||
297 | 232 | |||
298 | 233 | a.vibrateSilentMode = vibrateSilentMode | ||
299 | 234 | } | ||
300 | 235 | |||
301 | 236 | func (a *accounts) updateMessageSound(vsnd dbus.Variant) { | ||
302 | 237 | snd, ok := vsnd.Value.(string) | ||
303 | 238 | if !ok { | ||
304 | 239 | a.log.Errorf("IncomingMessageSound needed a string.") | ||
305 | 240 | return | ||
306 | 241 | } | ||
307 | 242 | |||
308 | 243 | for _, dir := range xdg.Data.Dirs()[1:] { | ||
309 | 244 | if dir[len(dir)-1] != '/' { | ||
310 | 245 | dir += "/" | ||
311 | 246 | } | ||
312 | 247 | if strings.HasPrefix(snd, dir) { | ||
313 | 248 | snd = snd[len(dir):] | ||
314 | 249 | break | ||
315 | 250 | } | ||
316 | 251 | } | ||
317 | 252 | |||
318 | 253 | a.messageSound = snd | ||
319 | 254 | } | ||
320 | 255 | |||
321 | 256 | func (a *accounts) update() { | ||
322 | 257 | props := make(map[string]dbus.Variant) | ||
323 | 258 | err := a.endp.Call("GetAll", []interface{}{accountsSoundIface}, &props) | ||
324 | 259 | if err != nil { | ||
325 | 260 | a.log.Errorf("when calling GetAll: %v", err) | ||
326 | 261 | return | ||
327 | 262 | } | ||
328 | 263 | a.log.Debugf("GetAll got: %#v", props) | ||
329 | 264 | |||
330 | 265 | a.lck.Lock() | ||
331 | 266 | defer a.lck.Unlock() | ||
332 | 267 | |||
333 | 268 | for name, updater := range a.updaters { | ||
334 | 269 | updater(props[name]) | ||
335 | 270 | } | ||
336 | 271 | } | ||
337 | 272 | |||
338 | 273 | // is the device in silent mode? | ||
339 | 274 | func (a *accounts) SilentMode() bool { | ||
340 | 275 | a.lck.Lock() | ||
341 | 276 | defer a.lck.Unlock() | ||
342 | 277 | |||
343 | 278 | return a.silent | ||
344 | 279 | } | ||
345 | 280 | |||
346 | 281 | // should notifications vibrate? | ||
347 | 282 | func (a *accounts) Vibrate() bool { | ||
348 | 283 | a.lck.Lock() | ||
349 | 284 | defer a.lck.Unlock() | ||
350 | 285 | |||
351 | 286 | if a.silent { | ||
352 | 287 | return a.vibrateSilentMode | ||
353 | 288 | } else { | ||
354 | 289 | return a.vibrate | ||
355 | 290 | } | ||
356 | 291 | } | ||
357 | 292 | |||
358 | 293 | // what is the default sound file? | ||
359 | 294 | func (a *accounts) MessageSoundFile() string { | ||
360 | 295 | a.lck.Lock() | ||
361 | 296 | defer a.lck.Unlock() | ||
362 | 297 | |||
363 | 298 | return a.messageSound | ||
364 | 299 | } | ||
365 | 300 | |||
366 | 301 | // the BusAddress should actually end with the UID of the user in question; | ||
367 | 302 | // here we do what's needed to get that. | ||
368 | 303 | func init() { | ||
369 | 304 | u, err := user.Current() | ||
370 | 305 | if err != nil { | ||
371 | 306 | panic(err) | ||
372 | 307 | } | ||
373 | 308 | |||
374 | 309 | BusAddress.Path += u.Uid | ||
375 | 310 | } | ||
376 | 0 | 311 | ||
377 | === added file 'bus/accounts/accounts_test.go' | |||
378 | --- bus/accounts/accounts_test.go 1970-01-01 00:00:00 +0000 | |||
379 | +++ bus/accounts/accounts_test.go 2015-03-26 16:42:21 +0000 | |||
380 | @@ -0,0 +1,271 @@ | |||
381 | 1 | /* | ||
382 | 2 | Copyright 2013-2015 Canonical Ltd. | ||
383 | 3 | |||
384 | 4 | This program is free software: you can redistribute it and/or modify it | ||
385 | 5 | under the terms of the GNU General Public License version 3, as published | ||
386 | 6 | by the Free Software Foundation. | ||
387 | 7 | |||
388 | 8 | This program is distributed in the hope that it will be useful, but | ||
389 | 9 | WITHOUT ANY WARRANTY; without even the implied warranties of | ||
390 | 10 | MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
391 | 11 | PURPOSE. See the GNU General Public License for more details. | ||
392 | 12 | |||
393 | 13 | You should have received a copy of the GNU General Public License along | ||
394 | 14 | with this program. If not, see <http://www.gnu.org/licenses/>. | ||
395 | 15 | */ | ||
396 | 16 | |||
397 | 17 | package accounts | ||
398 | 18 | |||
399 | 19 | import ( | ||
400 | 20 | "errors" | ||
401 | 21 | "testing" | ||
402 | 22 | |||
403 | 23 | "launchpad.net/go-dbus/v1" | ||
404 | 24 | . "launchpad.net/gocheck" | ||
405 | 25 | |||
406 | 26 | testibus "launchpad.net/ubuntu-push/bus/testing" | ||
407 | 27 | helpers "launchpad.net/ubuntu-push/testing" | ||
408 | 28 | "launchpad.net/ubuntu-push/testing/condition" | ||
409 | 29 | ) | ||
410 | 30 | |||
411 | 31 | // hook up gocheck | ||
412 | 32 | func TestAcc(t *testing.T) { TestingT(t) } | ||
413 | 33 | |||
414 | 34 | type AccSuite struct { | ||
415 | 35 | log *helpers.TestLogger | ||
416 | 36 | } | ||
417 | 37 | |||
418 | 38 | var _ = Suite(&AccSuite{}) | ||
419 | 39 | |||
420 | 40 | type TestCancellable struct { | ||
421 | 41 | canceled bool | ||
422 | 42 | err error | ||
423 | 43 | } | ||
424 | 44 | |||
425 | 45 | func (t *TestCancellable) Cancel() error { | ||
426 | 46 | t.canceled = true | ||
427 | 47 | return t.err | ||
428 | 48 | } | ||
429 | 49 | |||
430 | 50 | func (s *AccSuite) SetUpTest(c *C) { | ||
431 | 51 | s.log = helpers.NewTestLogger(c, "debug") | ||
432 | 52 | } | ||
433 | 53 | |||
434 | 54 | func (s *AccSuite) TestBusAddressPathUidLoaded(c *C) { | ||
435 | 55 | c.Check(BusAddress.Path, Matches, `.*\d+`) | ||
436 | 56 | } | ||
437 | 57 | |||
438 | 58 | func (s *AccSuite) TestCancelCancelsCancellable(c *C) { | ||
439 | 59 | err := errors.New("cancel error") | ||
440 | 60 | t := &TestCancellable{err: err} | ||
441 | 61 | a := New(nil, s.log).(*accounts) | ||
442 | 62 | a.cancellable = t | ||
443 | 63 | |||
444 | 64 | c.Check(a.Cancel(), Equals, err) | ||
445 | 65 | c.Check(t.canceled, Equals, true) | ||
446 | 66 | } | ||
447 | 67 | |||
448 | 68 | func (s *AccSuite) TestStartReportsWatchError(c *C) { | ||
449 | 69 | endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) | ||
450 | 70 | a := New(endp, s.log).(*accounts) | ||
451 | 71 | c.Assert(a, NotNil) | ||
452 | 72 | |||
453 | 73 | err := a.Start() | ||
454 | 74 | c.Check(err, NotNil) | ||
455 | 75 | } | ||
456 | 76 | |||
457 | 77 | func (s *AccSuite) TestStartSetsCancellable(c *C) { | ||
458 | 78 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true), true) | ||
459 | 79 | a := New(endp, s.log).(*accounts) | ||
460 | 80 | c.Assert(a, NotNil) | ||
461 | 81 | |||
462 | 82 | c.Check(a.cancellable, IsNil) | ||
463 | 83 | err := a.Start() | ||
464 | 84 | c.Check(err, IsNil) | ||
465 | 85 | c.Check(a.cancellable, NotNil) | ||
466 | 86 | a.Cancel() | ||
467 | 87 | } | ||
468 | 88 | |||
469 | 89 | func (s *AccSuite) TestStartPanicsIfCalledTwice(c *C) { | ||
470 | 90 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true), true, true) | ||
471 | 91 | a := New(endp, s.log).(*accounts) | ||
472 | 92 | c.Assert(a, NotNil) | ||
473 | 93 | |||
474 | 94 | c.Check(a.cancellable, IsNil) | ||
475 | 95 | err := a.Start() | ||
476 | 96 | c.Check(err, IsNil) | ||
477 | 97 | c.Check(func() { a.startWatch() }, PanicMatches, `.* twice\?`) | ||
478 | 98 | a.Cancel() | ||
479 | 99 | } | ||
480 | 100 | |||
481 | 101 | func (s *AccSuite) TestUpdateCallsUpdaters(c *C) { | ||
482 | 102 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true), | ||
483 | 103 | map[string]dbus.Variant{"x": dbus.Variant{"hello"}}) | ||
484 | 104 | a := New(endp, s.log).(*accounts) | ||
485 | 105 | c.Assert(a, NotNil) | ||
486 | 106 | var x dbus.Variant | ||
487 | 107 | a.updaters = map[string]func(dbus.Variant){ | ||
488 | 108 | "x": func(v dbus.Variant) { x = v }, | ||
489 | 109 | } | ||
490 | 110 | a.update() | ||
491 | 111 | |||
492 | 112 | c.Check(x.Value, Equals, "hello") | ||
493 | 113 | } | ||
494 | 114 | |||
495 | 115 | func (s *AccSuite) TestUpdateSilentModeBails(c *C) { | ||
496 | 116 | a := New(nil, s.log).(*accounts) | ||
497 | 117 | a.updateSilentMode(dbus.Variant{"rubbish"}) | ||
498 | 118 | c.Check(s.log.Captured(), Matches, `(?ms)ERROR SilentMode needed a bool.`) | ||
499 | 119 | } | ||
500 | 120 | |||
501 | 121 | func (s *AccSuite) TestUpdateSilentModeWorks(c *C) { | ||
502 | 122 | a := New(nil, s.log).(*accounts) | ||
503 | 123 | c.Check(a.silent, Equals, false) | ||
504 | 124 | a.updateSilentMode(dbus.Variant{true}) | ||
505 | 125 | c.Check(a.silent, Equals, true) | ||
506 | 126 | } | ||
507 | 127 | |||
508 | 128 | func (s *AccSuite) TestUpdateVibrateBails(c *C) { | ||
509 | 129 | a := New(nil, s.log).(*accounts) | ||
510 | 130 | a.updateVibrate(dbus.Variant{"rubbish"}) | ||
511 | 131 | c.Check(s.log.Captured(), Matches, `(?ms)ERROR IncomingMessageVibrate needed a bool.`) | ||
512 | 132 | } | ||
513 | 133 | |||
514 | 134 | func (s *AccSuite) TestUpdateVibrateWorks(c *C) { | ||
515 | 135 | a := New(nil, s.log).(*accounts) | ||
516 | 136 | c.Check(a.vibrate, Equals, false) | ||
517 | 137 | a.updateVibrate(dbus.Variant{true}) | ||
518 | 138 | c.Check(a.vibrate, Equals, true) | ||
519 | 139 | } | ||
520 | 140 | |||
521 | 141 | func (s *AccSuite) TestUpdateVibrateSilentModeBails(c *C) { | ||
522 | 142 | a := New(nil, s.log).(*accounts) | ||
523 | 143 | a.updateVibrateSilentMode(dbus.Variant{"rubbish"}) | ||
524 | 144 | c.Check(s.log.Captured(), Matches, `(?ms)ERROR IncomingMessageVibrateSilentMode needed a bool.`) | ||
525 | 145 | } | ||
526 | 146 | |||
527 | 147 | func (s *AccSuite) TestUpdateVibrateSilentModeWorks(c *C) { | ||
528 | 148 | a := New(nil, s.log).(*accounts) | ||
529 | 149 | c.Check(a.vibrateSilentMode, Equals, false) | ||
530 | 150 | a.updateVibrateSilentMode(dbus.Variant{true}) | ||
531 | 151 | c.Check(a.vibrateSilentMode, Equals, true) | ||
532 | 152 | } | ||
533 | 153 | |||
534 | 154 | func (s *AccSuite) TestUpdateMessageSoundBails(c *C) { | ||
535 | 155 | a := New(nil, s.log).(*accounts) | ||
536 | 156 | a.updateMessageSound(dbus.Variant{42}) | ||
537 | 157 | c.Check(s.log.Captured(), Matches, `(?ms)ERROR IncomingMessageSound needed a string.`) | ||
538 | 158 | } | ||
539 | 159 | |||
540 | 160 | func (s *AccSuite) TestUpdateMessageSoundWorks(c *C) { | ||
541 | 161 | a := New(nil, s.log).(*accounts) | ||
542 | 162 | c.Check(a.messageSound, Equals, "") | ||
543 | 163 | a.updateMessageSound(dbus.Variant{"xyzzy"}) | ||
544 | 164 | c.Check(a.messageSound, Equals, "xyzzy") | ||
545 | 165 | } | ||
546 | 166 | |||
547 | 167 | func (s *AccSuite) TestUpdateMessageSoundPrunesXDG(c *C) { | ||
548 | 168 | a := New(nil, s.log).(*accounts) | ||
549 | 169 | a.updateMessageSound(dbus.Variant{"/usr/share/xyzzy"}) | ||
550 | 170 | c.Check(a.messageSound, Equals, "xyzzy") | ||
551 | 171 | } | ||
552 | 172 | |||
553 | 173 | func (s *AccSuite) TestPropsHandler(c *C) { | ||
554 | 174 | endp := testibus.NewTestingEndpoint(nil, condition.Work(false)) | ||
555 | 175 | |||
556 | 176 | // testing a series of bad args for propsHandler: none, | ||
557 | 177 | New(endp, s.log).(*accounts).propsHandler() | ||
558 | 178 | c.Check(s.log.Captured(), Matches, `(?ms).*ERROR PropertiesChanged delivered 0 things.*`) | ||
559 | 179 | s.log.ResetCapture() | ||
560 | 180 | |||
561 | 181 | // bad type for all, | ||
562 | 182 | New(endp, s.log).(*accounts).propsHandler(nil, nil, nil) | ||
563 | 183 | c.Check(s.log.Captured(), Matches, `(?ms).*ERROR PropertiesChanged 1st param not a string.*`) | ||
564 | 184 | s.log.ResetCapture() | ||
565 | 185 | |||
566 | 186 | // wrong interface, | ||
567 | 187 | New(endp, s.log).(*accounts).propsHandler("xyzzy", nil, nil) | ||
568 | 188 | c.Check(s.log.Captured(), Matches, `(?ms).*DEBUG PropertiesChanged for "xyzzy", ignoring\..*`) | ||
569 | 189 | s.log.ResetCapture() | ||
570 | 190 | |||
571 | 191 | // bad type for 2nd and 3rd, | ||
572 | 192 | New(endp, s.log).(*accounts).propsHandler(accountsSoundIface, nil, nil) | ||
573 | 193 | c.Check(s.log.Captured(), Matches, `(?ms).*ERROR PropertiesChanged 2nd param not a map.*`) | ||
574 | 194 | s.log.ResetCapture() | ||
575 | 195 | |||
576 | 196 | // not-seen-in-the-wild 'changed' argument (first non-error outcome), | ||
577 | 197 | New(endp, s.log).(*accounts).propsHandler(accountsSoundIface, map[interface{}]interface{}{"x": "y"}, nil) | ||
578 | 198 | // tracking the update() via the GetAll call it generates (which will fail because of the testibus of Work(false) above) | ||
579 | 199 | c.Check(s.log.Captured(), Matches, `(?ms).*INFO PropertiesChanged provided 'changed'.*ERROR when calling GetAll.*`) | ||
580 | 200 | s.log.ResetCapture() | ||
581 | 201 | |||
582 | 202 | // bad type for 3rd (with empty 2nd), | ||
583 | 203 | New(endp, s.log).(*accounts).propsHandler(accountsSoundIface, map[interface{}]interface{}{}, nil) | ||
584 | 204 | c.Check(s.log.Captured(), Matches, `(?ms).*ERROR PropertiesChanged 3rd param not a list of properties.*`) | ||
585 | 205 | s.log.ResetCapture() | ||
586 | 206 | |||
587 | 207 | // bad type for elements of 3rd, | ||
588 | 208 | New(endp, s.log).(*accounts).propsHandler(accountsSoundIface, map[interface{}]interface{}{}, []interface{}{42}) | ||
589 | 209 | c.Check(s.log.Captured(), Matches, `(?ms).*ERROR PropertiesChanged 3rd param's only entry not a string.*`) | ||
590 | 210 | s.log.ResetCapture() | ||
591 | 211 | |||
592 | 212 | // empty 3rd (not an error; hard to test "do ), | ||
593 | 213 | New(endp, s.log).(*accounts).propsHandler(accountsSoundIface, map[interface{}]interface{}{}, []interface{}{}) | ||
594 | 214 | c.Check(s.log.Captured(), Matches, `(?ms).*DEBUG PropertiesChanged 3rd param is empty.*`) | ||
595 | 215 | s.log.ResetCapture() | ||
596 | 216 | |||
597 | 217 | // more than one 2rd (also not an error; again looking at the GetAll failure to confirm update() got called), | ||
598 | 218 | New(endp, s.log).(*accounts).propsHandler(accountsSoundIface, map[interface{}]interface{}{}, []interface{}{"hi", "there"}) | ||
599 | 219 | c.Check(s.log.Captured(), Matches, `(?ms).*INFO.* reverting to full update.*ERROR when calling GetAll.*`) | ||
600 | 220 | s.log.ResetCapture() | ||
601 | 221 | |||
602 | 222 | // bus trouble for a single entry in the 3rd, | ||
603 | 223 | New(endp, s.log).(*accounts).propsHandler(accountsSoundIface, map[interface{}]interface{}{}, []interface{}{"SilentMode"}) | ||
604 | 224 | c.Check(s.log.Captured(), Matches, `(?ms).*ERROR when calling Get for SilentMode.*`) | ||
605 | 225 | s.log.ResetCapture() | ||
606 | 226 | |||
607 | 227 | // and finally, the common case: a single entry in the 3rd param, that gets updated individually. | ||
608 | 228 | xOuter := dbus.Variant{"x"} | ||
609 | 229 | a := New(testibus.NewTestingEndpoint(nil, condition.Work(true), xOuter), s.log).(*accounts) | ||
610 | 230 | called := false | ||
611 | 231 | a.updaters = map[string]func(dbus.Variant){"xyzzy": func(x dbus.Variant) { | ||
612 | 232 | c.Check(x, Equals, xOuter) | ||
613 | 233 | called = true | ||
614 | 234 | }} | ||
615 | 235 | a.propsHandler(accountsSoundIface, map[interface{}]interface{}{}, []interface{}{"xyzzy"}) | ||
616 | 236 | c.Check(called, Equals, true) | ||
617 | 237 | } | ||
618 | 238 | |||
619 | 239 | func (s *AccSuite) TestSilentMode(c *C) { | ||
620 | 240 | a := New(nil, s.log).(*accounts) | ||
621 | 241 | c.Check(a.SilentMode(), Equals, false) | ||
622 | 242 | a.silent = true | ||
623 | 243 | c.Check(a.SilentMode(), Equals, true) | ||
624 | 244 | } | ||
625 | 245 | |||
626 | 246 | func (s *AccSuite) TestVibrate(c *C) { | ||
627 | 247 | a := New(nil, s.log).(*accounts) | ||
628 | 248 | c.Check(a.Vibrate(), Equals, false) | ||
629 | 249 | a.vibrate = true | ||
630 | 250 | c.Check(a.Vibrate(), Equals, true) | ||
631 | 251 | a.silent = true | ||
632 | 252 | c.Check(a.Vibrate(), Equals, false) | ||
633 | 253 | a.vibrateSilentMode = true | ||
634 | 254 | c.Check(a.Vibrate(), Equals, true) | ||
635 | 255 | a.vibrate = false | ||
636 | 256 | c.Check(a.Vibrate(), Equals, true) | ||
637 | 257 | } | ||
638 | 258 | |||
639 | 259 | func (s *AccSuite) TestMessageSoundFile(c *C) { | ||
640 | 260 | a := New(nil, s.log).(*accounts) | ||
641 | 261 | c.Check(a.MessageSoundFile(), Equals, "") | ||
642 | 262 | a.messageSound = "xyzzy" | ||
643 | 263 | c.Check(a.MessageSoundFile(), Equals, "xyzzy") | ||
644 | 264 | } | ||
645 | 265 | |||
646 | 266 | func (s *AccSuite) TestString(c *C) { | ||
647 | 267 | a := New(nil, s.log).(*accounts) | ||
648 | 268 | a.vibrate = true | ||
649 | 269 | a.messageSound = "x" | ||
650 | 270 | c.Check(a.String(), Equals, `&accounts{silent: false, vibrate: true, vibratesilent: false, messageSound: "x"}`) | ||
651 | 271 | } | ||
652 | 0 | 272 | ||
653 | === modified file 'bus/connectivity/connectivity.go' | |||
654 | --- bus/connectivity/connectivity.go 2015-01-22 17:34:18 +0000 | |||
655 | +++ bus/connectivity/connectivity.go 2015-03-26 16:42:21 +0000 | |||
656 | @@ -1,5 +1,5 @@ | |||
657 | 1 | /* | 1 | /* |
659 | 2 | Copyright 2013-2014 Canonical Ltd. | 2 | Copyright 2013-2015 Canonical Ltd. |
660 | 3 | 3 | ||
661 | 4 | This program is free software: you can redistribute it and/or modify it | 4 | This program is free software: you can redistribute it and/or modify it |
662 | 5 | under the terms of the GNU General Public License version 3, as published | 5 | under the terms of the GNU General Public License version 3, as published |
663 | @@ -24,12 +24,14 @@ | |||
664 | 24 | 24 | ||
665 | 25 | import ( | 25 | import ( |
666 | 26 | "errors" | 26 | "errors" |
667 | 27 | "sync" | ||
668 | 28 | "time" | ||
669 | 29 | |||
670 | 27 | "launchpad.net/ubuntu-push/bus" | 30 | "launchpad.net/ubuntu-push/bus" |
671 | 28 | "launchpad.net/ubuntu-push/bus/networkmanager" | 31 | "launchpad.net/ubuntu-push/bus/networkmanager" |
672 | 29 | "launchpad.net/ubuntu-push/config" | 32 | "launchpad.net/ubuntu-push/config" |
673 | 30 | "launchpad.net/ubuntu-push/logger" | 33 | "launchpad.net/ubuntu-push/logger" |
674 | 31 | "launchpad.net/ubuntu-push/util" | 34 | "launchpad.net/ubuntu-push/util" |
675 | 32 | "time" | ||
676 | 33 | ) | 35 | ) |
677 | 34 | 36 | ||
678 | 35 | // The configuration for ConnectedState, intended to be populated from a config file. | 37 | // The configuration for ConnectedState, intended to be populated from a config file. |
679 | @@ -45,23 +47,56 @@ | |||
680 | 45 | ConnectivityCheckMD5 string `json:"connectivity_check_md5"` | 47 | ConnectivityCheckMD5 string `json:"connectivity_check_md5"` |
681 | 46 | } | 48 | } |
682 | 47 | 49 | ||
684 | 48 | type connectedState struct { | 50 | // ConnectedState helps tracking connectivity. |
685 | 51 | type ConnectedState struct { | ||
686 | 49 | networkStateCh <-chan networkmanager.State | 52 | networkStateCh <-chan networkmanager.State |
687 | 50 | networkConCh <-chan string | 53 | networkConCh <-chan string |
688 | 51 | config ConnectivityConfig | 54 | config ConnectivityConfig |
689 | 52 | log logger.Logger | 55 | log logger.Logger |
690 | 53 | endp bus.Endpoint | 56 | endp bus.Endpoint |
691 | 54 | connAttempts uint32 | 57 | connAttempts uint32 |
693 | 55 | webget func(ch chan<- bool) | 58 | webchk Webchecker |
694 | 56 | webgetCh chan bool | 59 | webgetCh chan bool |
695 | 57 | currentState networkmanager.State | 60 | currentState networkmanager.State |
696 | 58 | lastSent bool | 61 | lastSent bool |
697 | 59 | timer *time.Timer | 62 | timer *time.Timer |
698 | 63 | doneLck sync.Mutex | ||
699 | 64 | done chan struct{} | ||
700 | 65 | canceled bool | ||
701 | 66 | stateWatch bus.Cancellable | ||
702 | 67 | conWatch bus.Cancellable | ||
703 | 68 | } | ||
704 | 69 | |||
705 | 70 | // New makes a ConnectedState for connectivity tracking. | ||
706 | 71 | // | ||
707 | 72 | // The endpoint need not be dialed; Track() will Dial() and | ||
708 | 73 | // Close() it as it sees fit. | ||
709 | 74 | func New(endp bus.Endpoint, config ConnectivityConfig, log logger.Logger) *ConnectedState { | ||
710 | 75 | wg := NewWebchecker(config.ConnectivityCheckURL, config.ConnectivityCheckMD5, 10*time.Second, log) | ||
711 | 76 | return &ConnectedState{ | ||
712 | 77 | config: config, | ||
713 | 78 | log: log, | ||
714 | 79 | endp: endp, | ||
715 | 80 | webchk: wg, | ||
716 | 81 | done: make(chan struct{}), | ||
717 | 82 | } | ||
718 | 83 | } | ||
719 | 84 | |||
720 | 85 | // cancel watches if any | ||
721 | 86 | func (cs *ConnectedState) reset() { | ||
722 | 87 | if cs.stateWatch != nil { | ||
723 | 88 | cs.stateWatch.Cancel() | ||
724 | 89 | cs.stateWatch = nil | ||
725 | 90 | } | ||
726 | 91 | if cs.conWatch != nil { | ||
727 | 92 | cs.conWatch.Cancel() | ||
728 | 93 | cs.conWatch = nil | ||
729 | 94 | } | ||
730 | 60 | } | 95 | } |
731 | 61 | 96 | ||
732 | 62 | // start connects to the bus, gets the initial NetworkManager state, and sets | 97 | // start connects to the bus, gets the initial NetworkManager state, and sets |
733 | 63 | // up the watch. | 98 | // up the watch. |
735 | 64 | func (cs *connectedState) start() networkmanager.State { | 99 | func (cs *ConnectedState) start() networkmanager.State { |
736 | 65 | var initial networkmanager.State | 100 | var initial networkmanager.State |
737 | 66 | var stateCh <-chan networkmanager.State | 101 | var stateCh <-chan networkmanager.State |
738 | 67 | var primary string | 102 | var primary string |
739 | @@ -72,8 +107,9 @@ | |||
740 | 72 | cs.connAttempts += ar.Redial() | 107 | cs.connAttempts += ar.Redial() |
741 | 73 | nm := networkmanager.New(cs.endp, cs.log) | 108 | nm := networkmanager.New(cs.endp, cs.log) |
742 | 74 | 109 | ||
743 | 110 | cs.reset() | ||
744 | 75 | // set up the watch | 111 | // set up the watch |
746 | 76 | stateCh, err = nm.WatchState() | 112 | stateCh, cs.stateWatch, err = nm.WatchState() |
747 | 77 | if err != nil { | 113 | if err != nil { |
748 | 78 | cs.log.Debugf("failed to set up the state watch: %s", err) | 114 | cs.log.Debugf("failed to set up the state watch: %s", err) |
749 | 79 | goto Continue | 115 | goto Continue |
750 | @@ -87,15 +123,15 @@ | |||
751 | 87 | } | 123 | } |
752 | 88 | cs.log.Debugf("got initial state of %s", initial) | 124 | cs.log.Debugf("got initial state of %s", initial) |
753 | 89 | 125 | ||
754 | 126 | conCh, cs.conWatch, err = nm.WatchPrimaryConnection() | ||
755 | 127 | if err != nil { | ||
756 | 128 | cs.log.Debugf("failed to set up the connection watch: %s", err) | ||
757 | 129 | goto Continue | ||
758 | 130 | } | ||
759 | 131 | |||
760 | 90 | primary = nm.GetPrimaryConnection() | 132 | primary = nm.GetPrimaryConnection() |
761 | 91 | cs.log.Debugf("primary connection starts as %#v", primary) | 133 | cs.log.Debugf("primary connection starts as %#v", primary) |
762 | 92 | 134 | ||
763 | 93 | conCh, err = nm.WatchPrimaryConnection() | ||
764 | 94 | if err != nil { | ||
765 | 95 | cs.log.Debugf("failed to set up the connection watch: %s", err) | ||
766 | 96 | goto Continue | ||
767 | 97 | } | ||
768 | 98 | |||
769 | 99 | cs.networkStateCh = stateCh | 135 | cs.networkStateCh = stateCh |
770 | 100 | cs.networkConCh = conCh | 136 | cs.networkConCh = conCh |
771 | 101 | 137 | ||
772 | @@ -107,9 +143,11 @@ | |||
773 | 107 | } | 143 | } |
774 | 108 | } | 144 | } |
775 | 109 | 145 | ||
777 | 110 | // connectedStateStep takes one step forwards in the “am I connected?” | 146 | var errCanceled = errors.New("canceled") |
778 | 147 | |||
779 | 148 | // step takes one step forwards in the “am I connected?” | ||
780 | 111 | // answering state machine. | 149 | // answering state machine. |
782 | 112 | func (cs *connectedState) connectedStateStep() (bool, error) { | 150 | func (cs *ConnectedState) step() (bool, error) { |
783 | 113 | stabilizingTimeout := cs.config.StabilizingTimeout.Duration | 151 | stabilizingTimeout := cs.config.StabilizingTimeout.Duration |
784 | 114 | recheckTimeout := cs.config.RecheckTimeout.Duration | 152 | recheckTimeout := cs.config.RecheckTimeout.Duration |
785 | 115 | log := cs.log | 153 | log := cs.log |
786 | @@ -117,6 +155,8 @@ | |||
787 | 117 | Loop: | 155 | Loop: |
788 | 118 | for { | 156 | for { |
789 | 119 | select { | 157 | select { |
790 | 158 | case <-cs.done: | ||
791 | 159 | return false, errCanceled | ||
792 | 120 | case <-cs.networkConCh: | 160 | case <-cs.networkConCh: |
793 | 121 | cs.webgetCh = nil | 161 | cs.webgetCh = nil |
794 | 122 | cs.timer.Reset(stabilizingTimeout) | 162 | cs.timer.Reset(stabilizingTimeout) |
795 | @@ -155,8 +195,13 @@ | |||
796 | 155 | case <-cs.timer.C: | 195 | case <-cs.timer.C: |
797 | 156 | if cs.currentState == networkmanager.ConnectedGlobal { | 196 | if cs.currentState == networkmanager.ConnectedGlobal { |
798 | 157 | log.Debugf("connectivity: timer signal, state: ConnectedGlobal, checking...") | 197 | log.Debugf("connectivity: timer signal, state: ConnectedGlobal, checking...") |
801 | 158 | cs.webgetCh = make(chan bool) | 198 | // use a buffered channel, otherwise |
802 | 159 | go cs.webget(cs.webgetCh) | 199 | // we may leak webcheckers that cannot |
803 | 200 | // send their result because we have | ||
804 | 201 | // cleared webgetCh and wont receive | ||
805 | 202 | // on it | ||
806 | 203 | cs.webgetCh = make(chan bool, 1) | ||
807 | 204 | go cs.webchk.Webcheck(cs.webgetCh) | ||
808 | 160 | } | 205 | } |
809 | 161 | 206 | ||
810 | 162 | case connected := <-cs.webgetCh: | 207 | case connected := <-cs.webgetCh: |
811 | @@ -173,35 +218,49 @@ | |||
812 | 173 | return cs.lastSent, nil | 218 | return cs.lastSent, nil |
813 | 174 | } | 219 | } |
814 | 175 | 220 | ||
816 | 176 | // ConnectedState sends the initial NetworkManager state and changes to it | 221 | // Track sends the initial NetworkManager state and changes to it |
817 | 177 | // over the "out" channel. Sends "false" as soon as it detects trouble, "true" | 222 | // over the "out" channel. Sends "false" as soon as it detects trouble, "true" |
818 | 178 | // after checking actual connectivity. | 223 | // after checking actual connectivity. |
819 | 179 | // | 224 | // |
830 | 180 | // The endpoint need not be dialed; connectivity will Dial() and Close() | 225 | func (cs *ConnectedState) Track(out chan<- bool) { |
821 | 181 | // it as it sees fit. | ||
822 | 182 | func ConnectedState(endp bus.Endpoint, config ConnectivityConfig, log logger.Logger, out chan<- bool) { | ||
823 | 183 | wg := NewWebchecker(config.ConnectivityCheckURL, config.ConnectivityCheckMD5, 10*time.Second, log) | ||
824 | 184 | cs := &connectedState{ | ||
825 | 185 | config: config, | ||
826 | 186 | log: log, | ||
827 | 187 | endp: endp, | ||
828 | 188 | webget: wg.Webcheck, | ||
829 | 189 | } | ||
831 | 190 | 226 | ||
832 | 191 | Start: | 227 | Start: |
835 | 192 | log.Debugf("sending initial 'disconnected'.") | 228 | cs.log.Debugf("sending initial 'disconnected'.") |
836 | 193 | out <- false | 229 | select { |
837 | 230 | case <-cs.done: | ||
838 | 231 | return | ||
839 | 232 | case out <- false: | ||
840 | 233 | } | ||
841 | 194 | cs.lastSent = false | 234 | cs.lastSent = false |
842 | 195 | cs.currentState = cs.start() | 235 | cs.currentState = cs.start() |
843 | 236 | defer cs.reset() | ||
844 | 196 | cs.timer = time.NewTimer(cs.config.StabilizingTimeout.Duration) | 237 | cs.timer = time.NewTimer(cs.config.StabilizingTimeout.Duration) |
845 | 197 | 238 | ||
846 | 198 | for { | 239 | for { |
848 | 199 | v, err := cs.connectedStateStep() | 240 | v, err := cs.step() |
849 | 241 | if err == errCanceled { | ||
850 | 242 | return | ||
851 | 243 | } | ||
852 | 200 | if err != nil { | 244 | if err != nil { |
853 | 201 | // tear it all down and start over | 245 | // tear it all down and start over |
855 | 202 | log.Errorf("%s", err) | 246 | cs.log.Errorf("%s", err) |
856 | 203 | goto Start | 247 | goto Start |
857 | 204 | } | 248 | } |
859 | 205 | out <- v | 249 | select { |
860 | 250 | case <-cs.done: | ||
861 | 251 | return | ||
862 | 252 | case out <- v: | ||
863 | 253 | } | ||
864 | 254 | } | ||
865 | 255 | } | ||
866 | 256 | |||
867 | 257 | // Cancel stops the ConnectedState machinary. | ||
868 | 258 | func (cs *ConnectedState) Cancel() { | ||
869 | 259 | cs.doneLck.Lock() | ||
870 | 260 | defer cs.doneLck.Unlock() | ||
871 | 261 | if !cs.canceled { | ||
872 | 262 | cs.canceled = true | ||
873 | 263 | close(cs.done) | ||
874 | 264 | cs.webchk.Close() | ||
875 | 206 | } | 265 | } |
876 | 207 | } | 266 | } |
877 | 208 | 267 | ||
878 | === modified file 'bus/connectivity/connectivity_test.go' | |||
879 | --- bus/connectivity/connectivity_test.go 2015-01-22 17:34:18 +0000 | |||
880 | +++ bus/connectivity/connectivity_test.go 2015-03-26 16:42:21 +0000 | |||
881 | @@ -1,5 +1,5 @@ | |||
882 | 1 | /* | 1 | /* |
884 | 2 | Copyright 2013-2014 Canonical Ltd. | 2 | Copyright 2013-2015 Canonical Ltd. |
885 | 3 | 3 | ||
886 | 4 | This program is free software: you can redistribute it and/or modify it | 4 | This program is free software: you can redistribute it and/or modify it |
887 | 5 | under the terms of the GNU General Public License version 3, as published | 5 | under the terms of the GNU General Public License version 3, as published |
888 | @@ -58,22 +58,37 @@ | |||
889 | 58 | s.log = helpers.NewTestLogger(c, "debug") | 58 | s.log = helpers.NewTestLogger(c, "debug") |
890 | 59 | } | 59 | } |
891 | 60 | 60 | ||
892 | 61 | var ( | ||
893 | 62 | helloCon = dbus.ObjectPath("hello") | ||
894 | 63 | helloConProps = map[string]dbus.Variant{"PrimaryConnection": dbus.Variant{helloCon}} | ||
895 | 64 | ) | ||
896 | 65 | |||
897 | 61 | /* | 66 | /* |
899 | 62 | tests for connectedState's Start() method | 67 | tests for ConnectedState's Start() method |
900 | 63 | */ | 68 | */ |
901 | 64 | 69 | ||
902 | 65 | // when given a working config and bus, Start() will work | 70 | // when given a working config and bus, Start() will work |
903 | 66 | func (s *ConnSuite) TestStartWorks(c *C) { | 71 | func (s *ConnSuite) TestStartWorks(c *C) { |
906 | 67 | endp := testingbus.NewTestingEndpoint(condition.Work(true), condition.Work(true), uint32(networkmanager.Connecting)) | 72 | endp := testingbus.NewTestingEndpoint(condition.Work(true), condition.Work(true), uint32(networkmanager.Connecting), helloCon) |
907 | 68 | cs := connectedState{config: ConnectivityConfig{}, log: s.log, endp: endp} | 73 | cs := ConnectedState{config: ConnectivityConfig{}, log: s.log, endp: endp} |
908 | 74 | |||
909 | 75 | nopTicker := make(chan []interface{}) | ||
910 | 76 | testingbus.SetWatchSource(endp, "StateChanged", nopTicker) | ||
911 | 77 | testingbus.SetWatchSource(endp, "PropertiesChanged", nopTicker) | ||
912 | 78 | defer close(nopTicker) | ||
913 | 69 | 79 | ||
914 | 70 | c.Check(cs.start(), Equals, networkmanager.Connecting) | 80 | c.Check(cs.start(), Equals, networkmanager.Connecting) |
915 | 71 | } | 81 | } |
916 | 72 | 82 | ||
917 | 73 | // if the bus fails a couple of times, we're still OK | 83 | // if the bus fails a couple of times, we're still OK |
918 | 74 | func (s *ConnSuite) TestStartRetriesConnect(c *C) { | 84 | func (s *ConnSuite) TestStartRetriesConnect(c *C) { |
921 | 75 | endp := testingbus.NewTestingEndpoint(condition.Fail2Work(2), condition.Work(true), uint32(networkmanager.Connecting)) | 85 | endp := testingbus.NewTestingEndpoint(condition.Fail2Work(2), condition.Work(true), uint32(networkmanager.Connecting), helloCon) |
922 | 76 | cs := connectedState{config: ConnectivityConfig{}, log: s.log, endp: endp} | 86 | cs := ConnectedState{config: ConnectivityConfig{}, log: s.log, endp: endp} |
923 | 87 | |||
924 | 88 | nopTicker := make(chan []interface{}) | ||
925 | 89 | testingbus.SetWatchSource(endp, "StateChanged", nopTicker) | ||
926 | 90 | testingbus.SetWatchSource(endp, "PropertiesChanged", nopTicker) | ||
927 | 91 | defer close(nopTicker) | ||
928 | 77 | 92 | ||
929 | 78 | c.Check(cs.start(), Equals, networkmanager.Connecting) | 93 | c.Check(cs.start(), Equals, networkmanager.Connecting) |
930 | 79 | c.Check(cs.connAttempts, Equals, uint32(3)) // 1 more than the Fail2Work | 94 | c.Check(cs.connAttempts, Equals, uint32(3)) // 1 more than the Fail2Work |
931 | @@ -81,8 +96,13 @@ | |||
932 | 81 | 96 | ||
933 | 82 | // when the calls to NetworkManager fails for a bit, we're still OK | 97 | // when the calls to NetworkManager fails for a bit, we're still OK |
934 | 83 | func (s *ConnSuite) TestStartRetriesCall(c *C) { | 98 | func (s *ConnSuite) TestStartRetriesCall(c *C) { |
937 | 84 | endp := testingbus.NewTestingEndpoint(condition.Work(true), condition.Fail2Work(5), uint32(networkmanager.Connecting)) | 99 | endp := testingbus.NewTestingEndpoint(condition.Work(true), condition.Fail2Work(5), uint32(networkmanager.Connecting), helloCon) |
938 | 85 | cs := connectedState{config: ConnectivityConfig{}, log: s.log, endp: endp} | 100 | cs := ConnectedState{config: ConnectivityConfig{}, log: s.log, endp: endp} |
939 | 101 | |||
940 | 102 | nopTicker := make(chan []interface{}) | ||
941 | 103 | testingbus.SetWatchSource(endp, "StateChanged", nopTicker) | ||
942 | 104 | testingbus.SetWatchSource(endp, "PropertiesChanged", nopTicker) | ||
943 | 105 | defer close(nopTicker) | ||
944 | 86 | 106 | ||
945 | 87 | c.Check(cs.start(), Equals, networkmanager.Connecting) | 107 | c.Check(cs.start(), Equals, networkmanager.Connecting) |
946 | 88 | 108 | ||
947 | @@ -91,11 +111,19 @@ | |||
948 | 91 | 111 | ||
949 | 92 | // when some of the calls to NetworkManager fails for a bit, we're still OK | 112 | // when some of the calls to NetworkManager fails for a bit, we're still OK |
950 | 93 | func (s *ConnSuite) TestStartRetriesCall2(c *C) { | 113 | func (s *ConnSuite) TestStartRetriesCall2(c *C) { |
952 | 94 | cond := condition.Chain(3, condition.Work(true), 1, condition.Work(false), | 114 | cond := condition.Chain(1, condition.Work(true), 1, condition.Work(false), |
953 | 95 | 1, condition.Work(true)) | 115 | 1, condition.Work(true)) |
954 | 96 | 116 | ||
957 | 97 | endp := testingbus.NewTestingEndpoint(condition.Work(true), cond, uint32(networkmanager.Connecting)) | 117 | endp := testingbus.NewTestingEndpoint(condition.Work(true), cond, |
958 | 98 | cs := connectedState{config: ConnectivityConfig{}, log: s.log, endp: endp} | 118 | uint32(networkmanager.Connecting), helloCon, |
959 | 119 | uint32(networkmanager.Connecting), helloCon, | ||
960 | 120 | ) | ||
961 | 121 | cs := ConnectedState{config: ConnectivityConfig{}, log: s.log, endp: endp} | ||
962 | 122 | |||
963 | 123 | nopTicker := make(chan []interface{}) | ||
964 | 124 | testingbus.SetWatchSource(endp, "StateChanged", nopTicker) | ||
965 | 125 | testingbus.SetWatchSource(endp, "PropertiesChanged", nopTicker) | ||
966 | 126 | defer close(nopTicker) | ||
967 | 99 | 127 | ||
968 | 100 | c.Check(cs.start(), Equals, networkmanager.Connecting) | 128 | c.Check(cs.start(), Equals, networkmanager.Connecting) |
969 | 101 | } | 129 | } |
970 | @@ -105,17 +133,25 @@ | |||
971 | 105 | // watch, we recover and try again. | 133 | // watch, we recover and try again. |
972 | 106 | func (s *ConnSuite) TestStartRetriesWatch(c *C) { | 134 | func (s *ConnSuite) TestStartRetriesWatch(c *C) { |
973 | 107 | nmcond := condition.Chain( | 135 | nmcond := condition.Chain( |
975 | 108 | 1, condition.Work(true), // 1 call to nm works | 136 | 2, condition.Work(true), // 2 call to nm works |
976 | 109 | 1, condition.Work(false), // 1 call to nm fails | 137 | 1, condition.Work(false), // 1 call to nm fails |
977 | 110 | 0, condition.Work(true)) // and everything works from there on | 138 | 0, condition.Work(true)) // and everything works from there on |
978 | 111 | endp := testingbus.NewTestingEndpoint(condition.Work(true), nmcond, | 139 | endp := testingbus.NewTestingEndpoint(condition.Work(true), nmcond, |
979 | 112 | uint32(networkmanager.Connecting), | 140 | uint32(networkmanager.Connecting), |
982 | 113 | uint32(networkmanager.ConnectedGlobal)) | 141 | uint32(networkmanager.Connecting), |
983 | 114 | cs := connectedState{config: ConnectivityConfig{}, log: s.log, endp: endp} | 142 | helloCon, |
984 | 143 | ) | ||
985 | 144 | cs := ConnectedState{config: ConnectivityConfig{}, log: s.log, endp: endp} | ||
986 | 145 | watchTicker := make(chan []interface{}, 1) | ||
987 | 146 | nopTicker := make(chan []interface{}) | ||
988 | 147 | testingbus.SetWatchSource(endp, "StateChanged", watchTicker) | ||
989 | 148 | testingbus.SetWatchSource(endp, "PropertiesChanged", nopTicker) | ||
990 | 149 | defer close(nopTicker) | ||
991 | 150 | defer close(watchTicker) | ||
992 | 115 | 151 | ||
993 | 116 | c.Check(cs.start(), Equals, networkmanager.Connecting) | 152 | c.Check(cs.start(), Equals, networkmanager.Connecting) |
994 | 117 | c.Check(cs.connAttempts, Equals, uint32(2)) | 153 | c.Check(cs.connAttempts, Equals, uint32(2)) |
996 | 118 | c.Check(<-cs.networkStateCh, Equals, networkmanager.Connecting) | 154 | watchTicker <- []interface{}{uint32(networkmanager.ConnectedGlobal)} |
997 | 119 | c.Check(<-cs.networkStateCh, Equals, networkmanager.ConnectedGlobal) | 155 | c.Check(<-cs.networkStateCh, Equals, networkmanager.ConnectedGlobal) |
998 | 120 | } | 156 | } |
999 | 121 | 157 | ||
1000 | @@ -144,7 +180,7 @@ | |||
1001 | 144 | } | 180 | } |
1002 | 145 | } | 181 | } |
1003 | 146 | 182 | ||
1005 | 147 | func (rep *racyEndpoint) WatchSignal(member string, f func(...interface{}), d func()) error { | 183 | func (rep *racyEndpoint) WatchSignal(member string, f func(...interface{}), d func()) (bus.Cancellable, error) { |
1006 | 148 | if member == "StateChanged" { | 184 | if member == "StateChanged" { |
1007 | 149 | // we count never having gotten the state as happening "after" now. | 185 | // we count never having gotten the state as happening "after" now. |
1008 | 150 | rep.lock.RLock() | 186 | rep.lock.RLock() |
1009 | @@ -157,7 +193,7 @@ | |||
1010 | 157 | d() | 193 | d() |
1011 | 158 | }() | 194 | }() |
1012 | 159 | } | 195 | } |
1014 | 160 | return nil | 196 | return nil, nil |
1015 | 161 | } | 197 | } |
1016 | 162 | 198 | ||
1017 | 163 | func (*racyEndpoint) Close() {} | 199 | func (*racyEndpoint) Close() {} |
1018 | @@ -186,7 +222,7 @@ | |||
1019 | 186 | func (s *ConnSuite) TestStartAvoidsRace(c *C) { | 222 | func (s *ConnSuite) TestStartAvoidsRace(c *C) { |
1020 | 187 | for delta := time.Second; delta > 1; delta /= 2 { | 223 | for delta := time.Second; delta > 1; delta /= 2 { |
1021 | 188 | rep := &racyEndpoint{delta: delta} | 224 | rep := &racyEndpoint{delta: delta} |
1023 | 189 | cs := connectedState{config: ConnectivityConfig{}, log: s.log, endp: rep} | 225 | cs := ConnectedState{config: ConnectivityConfig{}, log: s.log, endp: rep} |
1024 | 190 | f := Commentf("when delta=%s", delta) | 226 | f := Commentf("when delta=%s", delta) |
1025 | 191 | c.Assert(cs.start(), Equals, networkmanager.Connecting, f) | 227 | c.Assert(cs.start(), Equals, networkmanager.Connecting, f) |
1026 | 192 | c.Assert(takeNext(cs.networkStateCh), Equals, networkmanager.ConnectedGlobal, f) | 228 | c.Assert(takeNext(cs.networkStateCh), Equals, networkmanager.ConnectedGlobal, f) |
1027 | @@ -194,9 +230,18 @@ | |||
1028 | 194 | } | 230 | } |
1029 | 195 | 231 | ||
1030 | 196 | /* | 232 | /* |
1032 | 197 | tests for connectedStateStep() | 233 | tests for step() |
1033 | 198 | */ | 234 | */ |
1034 | 199 | 235 | ||
1035 | 236 | type testWebchk func(ch chan<- bool) | ||
1036 | 237 | |||
1037 | 238 | func (x testWebchk) Webcheck(ch chan<- bool) { | ||
1038 | 239 | x(ch) | ||
1039 | 240 | } | ||
1040 | 241 | |||
1041 | 242 | func (x testWebchk) Close() { | ||
1042 | 243 | } | ||
1043 | 244 | |||
1044 | 200 | func (s *ConnSuite) TestSteps(c *C) { | 245 | func (s *ConnSuite) TestSteps(c *C) { |
1045 | 201 | var webget_p condition.Interface = condition.Work(true) | 246 | var webget_p condition.Interface = condition.Work(true) |
1046 | 202 | recheck_timeout := 50 * time.Millisecond | 247 | recheck_timeout := 50 * time.Millisecond |
1047 | @@ -205,24 +250,24 @@ | |||
1048 | 205 | RecheckTimeout: config.ConfigTimeDuration{recheck_timeout}, | 250 | RecheckTimeout: config.ConfigTimeDuration{recheck_timeout}, |
1049 | 206 | } | 251 | } |
1050 | 207 | ch := make(chan networkmanager.State, 10) | 252 | ch := make(chan networkmanager.State, 10) |
1052 | 208 | cs := &connectedState{ | 253 | cs := &ConnectedState{ |
1053 | 209 | config: cfg, | 254 | config: cfg, |
1054 | 210 | networkStateCh: ch, | 255 | networkStateCh: ch, |
1055 | 211 | timer: time.NewTimer(time.Second), | 256 | timer: time.NewTimer(time.Second), |
1056 | 212 | log: s.log, | 257 | log: s.log, |
1058 | 213 | webget: func(ch chan<- bool) { ch <- webget_p.OK() }, | 258 | webchk: testWebchk(func(ch chan<- bool) { ch <- webget_p.OK() }), |
1059 | 214 | lastSent: false, | 259 | lastSent: false, |
1060 | 215 | } | 260 | } |
1061 | 216 | ch <- networkmanager.ConnectedGlobal | 261 | ch <- networkmanager.ConnectedGlobal |
1063 | 217 | f, e := cs.connectedStateStep() | 262 | f, e := cs.step() |
1064 | 218 | c.Check(e, IsNil) | 263 | c.Check(e, IsNil) |
1065 | 219 | c.Check(f, Equals, true) | 264 | c.Check(f, Equals, true) |
1066 | 220 | ch <- networkmanager.Disconnected | 265 | ch <- networkmanager.Disconnected |
1067 | 221 | ch <- networkmanager.ConnectedGlobal | 266 | ch <- networkmanager.ConnectedGlobal |
1069 | 222 | f, e = cs.connectedStateStep() | 267 | f, e = cs.step() |
1070 | 223 | c.Check(e, IsNil) | 268 | c.Check(e, IsNil) |
1071 | 224 | c.Check(f, Equals, false) | 269 | c.Check(f, Equals, false) |
1073 | 225 | f, e = cs.connectedStateStep() | 270 | f, e = cs.step() |
1074 | 226 | c.Check(e, IsNil) | 271 | c.Check(e, IsNil) |
1075 | 227 | c.Check(f, Equals, true) | 272 | c.Check(f, Equals, true) |
1076 | 228 | 273 | ||
1077 | @@ -230,7 +275,7 @@ | |||
1078 | 230 | webget_p = condition.Fail2Work(1) | 275 | webget_p = condition.Fail2Work(1) |
1079 | 231 | ch <- networkmanager.Disconnected | 276 | ch <- networkmanager.Disconnected |
1080 | 232 | ch <- networkmanager.ConnectedGlobal | 277 | ch <- networkmanager.ConnectedGlobal |
1082 | 233 | f, e = cs.connectedStateStep() | 278 | f, e = cs.step() |
1083 | 234 | c.Check(e, IsNil) | 279 | c.Check(e, IsNil) |
1084 | 235 | c.Check(f, Equals, false) // first false is from the Disconnected | 280 | c.Check(f, Equals, false) // first false is from the Disconnected |
1085 | 236 | 281 | ||
1086 | @@ -239,7 +284,7 @@ | |||
1087 | 239 | _t := time.NewTimer(recheck_timeout / 2) | 284 | _t := time.NewTimer(recheck_timeout / 2) |
1088 | 240 | 285 | ||
1089 | 241 | go func() { | 286 | go func() { |
1091 | 242 | f, e := cs.connectedStateStep() | 287 | f, e := cs.step() |
1092 | 243 | c.Check(e, IsNil) | 288 | c.Check(e, IsNil) |
1093 | 244 | _ch <- f | 289 | _ch <- f |
1094 | 245 | }() | 290 | }() |
1095 | @@ -257,15 +302,15 @@ | |||
1096 | 257 | ch <- networkmanager.Disconnected // this should not | 302 | ch <- networkmanager.Disconnected // this should not |
1097 | 258 | ch <- networkmanager.ConnectedGlobal // this should trigger a 'true' | 303 | ch <- networkmanager.ConnectedGlobal // this should trigger a 'true' |
1098 | 259 | 304 | ||
1100 | 260 | f, e = cs.connectedStateStep() | 305 | f, e = cs.step() |
1101 | 261 | c.Check(e, IsNil) | 306 | c.Check(e, IsNil) |
1102 | 262 | c.Check(f, Equals, false) | 307 | c.Check(f, Equals, false) |
1104 | 263 | f, e = cs.connectedStateStep() | 308 | f, e = cs.step() |
1105 | 264 | c.Check(e, IsNil) | 309 | c.Check(e, IsNil) |
1106 | 265 | c.Check(f, Equals, true) | 310 | c.Check(f, Equals, true) |
1107 | 266 | 311 | ||
1108 | 267 | close(ch) // this should make it error out | 312 | close(ch) // this should make it error out |
1110 | 268 | _, e = cs.connectedStateStep() | 313 | _, e = cs.step() |
1111 | 269 | c.Check(e, NotNil) | 314 | c.Check(e, NotNil) |
1112 | 270 | } | 315 | } |
1113 | 271 | 316 | ||
1114 | @@ -285,32 +330,50 @@ | |||
1115 | 285 | } | 330 | } |
1116 | 286 | 331 | ||
1117 | 287 | endp := testingbus.NewTestingEndpoint(condition.Work(true), condition.Work(true), | 332 | endp := testingbus.NewTestingEndpoint(condition.Work(true), condition.Work(true), |
1120 | 288 | uint32(networkmanager.ConnectedGlobal), | 333 | uint32(networkmanager.Disconnected), |
1121 | 289 | uint32(networkmanager.Disconnected), | 334 | helloCon, |
1122 | 335 | uint32(networkmanager.Disconnected), | ||
1123 | 336 | helloCon, | ||
1124 | 290 | ) | 337 | ) |
1125 | 291 | 338 | ||
1128 | 292 | watchTicker := make(chan bool) | 339 | watchTicker := make(chan []interface{}) |
1129 | 293 | testingbus.SetWatchTicker(endp, watchTicker) | 340 | testingbus.SetWatchSource(endp, "StateChanged", watchTicker) |
1130 | 341 | nopTicker := make(chan []interface{}) | ||
1131 | 342 | testingbus.SetWatchSource(endp, "PropertiesChanged", nopTicker) | ||
1132 | 294 | 343 | ||
1133 | 295 | out := make(chan bool) | 344 | out := make(chan bool) |
1134 | 296 | dt := time.Second / 10 | 345 | dt := time.Second / 10 |
1135 | 297 | timer := time.NewTimer(dt) | 346 | timer := time.NewTimer(dt) |
1137 | 298 | go ConnectedState(endp, cfg, s.log, out) | 347 | cs := New(endp, cfg, s.log) |
1138 | 348 | defer cs.Cancel() | ||
1139 | 349 | go cs.Track(out) | ||
1140 | 299 | var v bool | 350 | var v bool |
1141 | 300 | expecteds := []struct { | 351 | expecteds := []struct { |
1145 | 301 | p bool | 352 | p bool |
1146 | 302 | s string | 353 | s string |
1147 | 303 | n int | 354 | todo string |
1148 | 304 | }{ | 355 | }{ |
1153 | 305 | {false, "first state is always false", 0}, | 356 | {false, "first state is always false", ""}, |
1154 | 306 | {true, "then it should be true as per ConnectedGlobal above", 0}, | 357 | {true, "then it should be true as per ConnectedGlobal above", "ConnectedGlobal"}, |
1155 | 307 | {false, "then it should be false (Disconnected)", 2}, | 358 | {false, "then it should be false (Disconnected)", "Disconnected"}, |
1156 | 308 | {false, "then it should be false again because it's restarted", 2}, | 359 | {false, "then it should be false again because it's restarted", "close"}, |
1157 | 309 | } | 360 | } |
1158 | 310 | 361 | ||
1159 | 362 | defer func() { | ||
1160 | 363 | if watchTicker != nil { | ||
1161 | 364 | close(watchTicker) | ||
1162 | 365 | } | ||
1163 | 366 | }() | ||
1164 | 367 | defer close(nopTicker) | ||
1165 | 311 | for i, expected := range expecteds { | 368 | for i, expected := range expecteds { |
1168 | 312 | for j := 0; j < expected.n; j++ { | 369 | switch expected.todo { |
1169 | 313 | watchTicker <- true | 370 | case "ConnectedGlobal": |
1170 | 371 | watchTicker <- []interface{}{uint32(networkmanager.ConnectedGlobal)} | ||
1171 | 372 | case "Disconnected": | ||
1172 | 373 | watchTicker <- []interface{}{uint32(networkmanager.Disconnected)} | ||
1173 | 374 | case "close": | ||
1174 | 375 | close(watchTicker) | ||
1175 | 376 | watchTicker = nil | ||
1176 | 314 | } | 377 | } |
1177 | 315 | timer.Reset(dt) | 378 | timer.Reset(dt) |
1178 | 316 | select { | 379 | select { |
1179 | @@ -335,31 +398,41 @@ | |||
1180 | 335 | 398 | ||
1181 | 336 | endp := testingbus.NewTestingEndpoint(condition.Work(true), condition.Work(true), | 399 | endp := testingbus.NewTestingEndpoint(condition.Work(true), condition.Work(true), |
1182 | 337 | uint32(networkmanager.ConnectedGlobal), | 400 | uint32(networkmanager.ConnectedGlobal), |
1184 | 338 | map[string]dbus.Variant{"PrimaryConnection": dbus.Variant{dbus.ObjectPath("hello")}}, | 401 | helloCon, |
1185 | 339 | ) | 402 | ) |
1186 | 340 | 403 | ||
1189 | 341 | watchTicker := make(chan bool) | 404 | watchTicker := make(chan []interface{}) |
1190 | 342 | testingbus.SetWatchTicker(endp, watchTicker) | 405 | testingbus.SetWatchSource(endp, "PropertiesChanged", watchTicker) |
1191 | 406 | nopTicker := make(chan []interface{}) | ||
1192 | 407 | testingbus.SetWatchSource(endp, "StateChanged", nopTicker) | ||
1193 | 343 | 408 | ||
1194 | 344 | out := make(chan bool) | 409 | out := make(chan bool) |
1195 | 345 | dt := time.Second / 10 | 410 | dt := time.Second / 10 |
1196 | 346 | timer := time.NewTimer(dt) | 411 | timer := time.NewTimer(dt) |
1198 | 347 | go ConnectedState(endp, cfg, s.log, out) | 412 | cs := New(endp, cfg, s.log) |
1199 | 413 | defer cs.Cancel() | ||
1200 | 414 | go cs.Track(out) | ||
1201 | 348 | var v bool | 415 | var v bool |
1202 | 349 | expecteds := []struct { | 416 | expecteds := []struct { |
1206 | 350 | p bool | 417 | p bool |
1207 | 351 | s string | 418 | s string |
1208 | 352 | n int | 419 | changedConn bool |
1209 | 353 | }{ | 420 | }{ |
1214 | 354 | {false, "first state is always false", 0}, | 421 | {false, "first state is always false", false}, |
1215 | 355 | {true, "then it should be true as per ConnectedGlobal above", 0}, | 422 | {true, "then it should be true as per ConnectedGlobal above", false}, |
1216 | 356 | {false, "then, false (PrimaryConnection changed)", 2}, | 423 | {false, "then, false (PrimaryConnection changed)", true}, |
1217 | 357 | {true, "then it should be true (webcheck passed)", 0}, | 424 | {true, "then it should be true (webcheck passed)", false}, |
1218 | 358 | } | 425 | } |
1219 | 359 | 426 | ||
1220 | 427 | defer func() { | ||
1221 | 428 | if watchTicker != nil { | ||
1222 | 429 | close(watchTicker) | ||
1223 | 430 | } | ||
1224 | 431 | }() | ||
1225 | 432 | defer close(nopTicker) | ||
1226 | 360 | for i, expected := range expecteds { | 433 | for i, expected := range expecteds { |
1229 | 361 | for j := 0; j < expected.n; j++ { | 434 | if expected.changedConn { |
1230 | 362 | watchTicker <- true | 435 | watchTicker <- []interface{}{helloConProps} |
1231 | 363 | } | 436 | } |
1232 | 364 | timer.Reset(dt) | 437 | timer.Reset(dt) |
1233 | 365 | select { | 438 | select { |
1234 | 366 | 439 | ||
1235 | === modified file 'bus/connectivity/webchecker.go' | |||
1236 | --- bus/connectivity/webchecker.go 2015-01-22 17:34:18 +0000 | |||
1237 | +++ bus/connectivity/webchecker.go 2015-03-26 16:42:21 +0000 | |||
1238 | @@ -1,5 +1,5 @@ | |||
1239 | 1 | /* | 1 | /* |
1241 | 2 | Copyright 2013-2014 Canonical Ltd. | 2 | Copyright 2013-2015 Canonical Ltd. |
1242 | 3 | 3 | ||
1243 | 4 | This program is free software: you can redistribute it and/or modify it | 4 | This program is free software: you can redistribute it and/or modify it |
1244 | 5 | under the terms of the GNU General Public License version 3, as published | 5 | under the terms of the GNU General Public License version 3, as published |
1245 | @@ -40,6 +40,8 @@ | |||
1246 | 40 | // contents match the target. If so, then it sends true; if anything | 40 | // contents match the target. If so, then it sends true; if anything |
1247 | 41 | // fails, it sends false. | 41 | // fails, it sends false. |
1248 | 42 | Webcheck(chan<- bool) | 42 | Webcheck(chan<- bool) |
1249 | 43 | // Close idle connections. | ||
1250 | 44 | Close() | ||
1251 | 43 | } | 45 | } |
1252 | 44 | 46 | ||
1253 | 45 | type webchecker struct { | 47 | type webchecker struct { |
1254 | @@ -72,8 +74,11 @@ | |||
1255 | 72 | hash := md5.New() | 74 | hash := md5.New() |
1256 | 73 | _, err = io.CopyN(hash, response.Body, 1024) | 75 | _, err = io.CopyN(hash, response.Body, 1024) |
1257 | 74 | if err != io.EOF { | 76 | if err != io.EOF { |
1260 | 75 | wb.log.Errorf("reading %s, expecting EOF, got: %v", | 77 | if err == nil { |
1261 | 76 | wb.url, err) | 78 | wb.log.Errorf("reading %s, but response body is larger than 1k.", wb.url) |
1262 | 79 | } else { | ||
1263 | 80 | wb.log.Errorf("reading %s, expecting EOF, got: %v", wb.url, err) | ||
1264 | 81 | } | ||
1265 | 77 | ch <- false | 82 | ch <- false |
1266 | 78 | return | 83 | return |
1267 | 79 | } | 84 | } |
1268 | @@ -86,3 +91,7 @@ | |||
1269 | 86 | ch <- false | 91 | ch <- false |
1270 | 87 | } | 92 | } |
1271 | 88 | } | 93 | } |
1272 | 94 | |||
1273 | 95 | func (wb *webchecker) Close() { | ||
1274 | 96 | wb.cli.Transport.(*http13.Transport).CloseIdleConnections() | ||
1275 | 97 | } | ||
1276 | 89 | 98 | ||
1277 | === modified file 'bus/connectivity/webchecker_test.go' | |||
1278 | --- bus/connectivity/webchecker_test.go 2014-03-20 12:24:33 +0000 | |||
1279 | +++ bus/connectivity/webchecker_test.go 2015-03-26 16:42:21 +0000 | |||
1280 | @@ -1,5 +1,5 @@ | |||
1281 | 1 | /* | 1 | /* |
1283 | 2 | Copyright 2013-2014 Canonical Ltd. | 2 | Copyright 2013-2015 Canonical Ltd. |
1284 | 3 | 3 | ||
1285 | 4 | This program is free software: you can redistribute it and/or modify it | 4 | This program is free software: you can redistribute it and/or modify it |
1286 | 5 | under the terms of the GNU General Public License version 3, as published | 5 | under the terms of the GNU General Public License version 3, as published |
1287 | @@ -18,7 +18,6 @@ | |||
1288 | 18 | 18 | ||
1289 | 19 | import ( | 19 | import ( |
1290 | 20 | . "launchpad.net/gocheck" | 20 | . "launchpad.net/gocheck" |
1291 | 21 | "launchpad.net/ubuntu-push/logger" | ||
1292 | 22 | helpers "launchpad.net/ubuntu-push/testing" | 21 | helpers "launchpad.net/ubuntu-push/testing" |
1293 | 23 | "launchpad.net/ubuntu-push/util" | 22 | "launchpad.net/ubuntu-push/util" |
1294 | 24 | "net/http" | 23 | "net/http" |
1295 | @@ -28,7 +27,7 @@ | |||
1296 | 28 | 27 | ||
1297 | 29 | type WebcheckerSuite struct { | 28 | type WebcheckerSuite struct { |
1298 | 30 | timeouts []time.Duration | 29 | timeouts []time.Duration |
1300 | 31 | log logger.Logger | 30 | log *helpers.TestLogger |
1301 | 32 | } | 31 | } |
1302 | 33 | 32 | ||
1303 | 34 | var _ = Suite(&WebcheckerSuite{}) | 33 | var _ = Suite(&WebcheckerSuite{}) |
1304 | @@ -82,6 +81,7 @@ | |||
1305 | 82 | defer ts.Close() | 81 | defer ts.Close() |
1306 | 83 | 82 | ||
1307 | 84 | ck := NewWebchecker(ts.URL, staticHash, 5*time.Second, s.log) | 83 | ck := NewWebchecker(ts.URL, staticHash, 5*time.Second, s.log) |
1308 | 84 | defer ck.Close() | ||
1309 | 85 | ch := make(chan bool, 1) | 85 | ch := make(chan bool, 1) |
1310 | 86 | ck.Webcheck(ch) | 86 | ck.Webcheck(ch) |
1311 | 87 | c.Check(<-ch, Equals, true) | 87 | c.Check(<-ch, Equals, true) |
1312 | @@ -90,6 +90,7 @@ | |||
1313 | 90 | // Webchecker sends false if the download fails. | 90 | // Webchecker sends false if the download fails. |
1314 | 91 | func (s *WebcheckerSuite) TestActualFails(c *C) { | 91 | func (s *WebcheckerSuite) TestActualFails(c *C) { |
1315 | 92 | ck := NewWebchecker("garbage://", "", 5*time.Second, s.log) | 92 | ck := NewWebchecker("garbage://", "", 5*time.Second, s.log) |
1316 | 93 | defer ck.Close() | ||
1317 | 93 | ch := make(chan bool, 1) | 94 | ch := make(chan bool, 1) |
1318 | 94 | ck.Webcheck(ch) | 95 | ck.Webcheck(ch) |
1319 | 95 | c.Check(<-ch, Equals, false) | 96 | c.Check(<-ch, Equals, false) |
1320 | @@ -101,9 +102,11 @@ | |||
1321 | 101 | defer ts.Close() | 102 | defer ts.Close() |
1322 | 102 | 103 | ||
1323 | 103 | ck := NewWebchecker(ts.URL, staticHash, 5*time.Second, s.log) | 104 | ck := NewWebchecker(ts.URL, staticHash, 5*time.Second, s.log) |
1324 | 105 | defer ck.Close() | ||
1325 | 104 | ch := make(chan bool, 1) | 106 | ch := make(chan bool, 1) |
1326 | 105 | ck.Webcheck(ch) | 107 | ck.Webcheck(ch) |
1327 | 106 | c.Check(<-ch, Equals, false) | 108 | c.Check(<-ch, Equals, false) |
1328 | 109 | c.Check(s.log.Captured(), Matches, "(?ism).*content mismatch.*") | ||
1329 | 107 | } | 110 | } |
1330 | 108 | 111 | ||
1331 | 109 | // Webchecker sends false if the download is too big | 112 | // Webchecker sends false if the download is too big |
1332 | @@ -112,9 +115,11 @@ | |||
1333 | 112 | defer ts.Close() | 115 | defer ts.Close() |
1334 | 113 | 116 | ||
1335 | 114 | ck := NewWebchecker(ts.URL, bigHash, 5*time.Second, s.log) | 117 | ck := NewWebchecker(ts.URL, bigHash, 5*time.Second, s.log) |
1336 | 118 | defer ck.Close() | ||
1337 | 115 | ch := make(chan bool, 1) | 119 | ch := make(chan bool, 1) |
1338 | 116 | ck.Webcheck(ch) | 120 | ck.Webcheck(ch) |
1339 | 117 | c.Check(<-ch, Equals, false) | 121 | c.Check(<-ch, Equals, false) |
1340 | 122 | c.Check(s.log.Captured(), Matches, "(?ism).*larger than 1k.*") | ||
1341 | 118 | } | 123 | } |
1342 | 119 | 124 | ||
1343 | 120 | // Webchecker sends false if the request timeouts | 125 | // Webchecker sends false if the request timeouts |
1344 | @@ -130,6 +135,7 @@ | |||
1345 | 130 | }() | 135 | }() |
1346 | 131 | 136 | ||
1347 | 132 | ck := NewWebchecker(ts.URL, bigHash, time.Second, s.log) | 137 | ck := NewWebchecker(ts.URL, bigHash, time.Second, s.log) |
1348 | 138 | defer ck.Close() | ||
1349 | 133 | ch := make(chan bool, 1) | 139 | ch := make(chan bool, 1) |
1350 | 134 | ck.Webcheck(ch) | 140 | ck.Webcheck(ch) |
1351 | 135 | c.Check(<-ch, Equals, false) | 141 | c.Check(<-ch, Equals, false) |
1352 | 136 | 142 | ||
1353 | === modified file 'bus/endpoint.go' | |||
1354 | --- bus/endpoint.go 2015-01-22 17:34:18 +0000 | |||
1355 | +++ bus/endpoint.go 2015-03-26 16:42:21 +0000 | |||
1356 | @@ -35,10 +35,15 @@ | |||
1357 | 35 | type BusMethod func(string, []interface{}, []interface{}) ([]interface{}, error) | 35 | type BusMethod func(string, []interface{}, []interface{}) ([]interface{}, error) |
1358 | 36 | type DispatchMap map[string]BusMethod | 36 | type DispatchMap map[string]BusMethod |
1359 | 37 | 37 | ||
1360 | 38 | // Cancellable can be canceled. | ||
1361 | 39 | type Cancellable interface { | ||
1362 | 40 | Cancel() error | ||
1363 | 41 | } | ||
1364 | 42 | |||
1365 | 38 | // bus.Endpoint represents the DBus connection itself. | 43 | // bus.Endpoint represents the DBus connection itself. |
1366 | 39 | type Endpoint interface { | 44 | type Endpoint interface { |
1367 | 40 | GrabName(allowReplacement bool) <-chan error | 45 | GrabName(allowReplacement bool) <-chan error |
1369 | 41 | WatchSignal(member string, f func(...interface{}), d func()) error | 46 | WatchSignal(member string, f func(...interface{}), d func()) (Cancellable, error) |
1370 | 42 | WatchMethod(DispatchMap, string, ...interface{}) | 47 | WatchMethod(DispatchMap, string, ...interface{}) |
1371 | 43 | Signal(string, string, []interface{}) error | 48 | Signal(string, string, []interface{}) error |
1372 | 44 | Call(member string, args []interface{}, rvs ...interface{}) error | 49 | Call(member string, args []interface{}, rvs ...interface{}) error |
1373 | @@ -123,16 +128,16 @@ | |||
1374 | 123 | // sends the values over a channel, and d() would close the channel. | 128 | // sends the values over a channel, and d() would close the channel. |
1375 | 124 | // | 129 | // |
1376 | 125 | // XXX: untested | 130 | // XXX: untested |
1378 | 126 | func (endp *endpoint) WatchSignal(member string, f func(...interface{}), d func()) error { | 131 | func (endp *endpoint) WatchSignal(member string, f func(...interface{}), d func()) (Cancellable, error) { |
1379 | 127 | watch, err := endp.proxy.WatchSignal(endp.addr.Interface, member) | 132 | watch, err := endp.proxy.WatchSignal(endp.addr.Interface, member) |
1380 | 128 | if err != nil { | 133 | if err != nil { |
1381 | 129 | endp.log.Debugf("failed to set up the watch: %s", err) | 134 | endp.log.Debugf("failed to set up the watch: %s", err) |
1383 | 130 | return err | 135 | return nil, err |
1384 | 131 | } | 136 | } |
1385 | 132 | 137 | ||
1386 | 133 | go endp.unpackMessages(watch, f, d, member) | 138 | go endp.unpackMessages(watch, f, d, member) |
1387 | 134 | 139 | ||
1389 | 135 | return nil | 140 | return watch, nil |
1390 | 136 | } | 141 | } |
1391 | 137 | 142 | ||
1392 | 138 | // Call() invokes the provided member method (on the name, path and | 143 | // Call() invokes the provided member method (on the name, path and |
1393 | @@ -324,6 +329,6 @@ | |||
1394 | 324 | } | 329 | } |
1395 | 325 | f(endp.unpackOneMsg(msg, member)...) | 330 | f(endp.unpackOneMsg(msg, member)...) |
1396 | 326 | } | 331 | } |
1398 | 327 | endp.log.Errorf("got not-OK from %s watch", member) | 332 | endp.log.Debugf("got not-OK from %s watch", member) |
1399 | 328 | d() | 333 | d() |
1400 | 329 | } | 334 | } |
1401 | 330 | 335 | ||
1402 | === modified file 'bus/haptic/haptic.go' | |||
1403 | --- bus/haptic/haptic.go 2014-08-08 01:07:38 +0000 | |||
1404 | +++ bus/haptic/haptic.go 2015-03-26 16:42:21 +0000 | |||
1405 | @@ -20,6 +20,7 @@ | |||
1406 | 20 | 20 | ||
1407 | 21 | import ( | 21 | import ( |
1408 | 22 | "launchpad.net/ubuntu-push/bus" | 22 | "launchpad.net/ubuntu-push/bus" |
1409 | 23 | "launchpad.net/ubuntu-push/bus/accounts" | ||
1410 | 23 | "launchpad.net/ubuntu-push/click" | 24 | "launchpad.net/ubuntu-push/click" |
1411 | 24 | "launchpad.net/ubuntu-push/launch_helper" | 25 | "launchpad.net/ubuntu-push/launch_helper" |
1412 | 25 | "launchpad.net/ubuntu-push/logger" | 26 | "launchpad.net/ubuntu-push/logger" |
1413 | @@ -36,12 +37,13 @@ | |||
1414 | 36 | type Haptic struct { | 37 | type Haptic struct { |
1415 | 37 | bus bus.Endpoint | 38 | bus bus.Endpoint |
1416 | 38 | log logger.Logger | 39 | log logger.Logger |
1417 | 40 | acc accounts.Accounts | ||
1418 | 39 | fallback *launch_helper.Vibration | 41 | fallback *launch_helper.Vibration |
1419 | 40 | } | 42 | } |
1420 | 41 | 43 | ||
1421 | 42 | // New returns a new Haptic that'll use the provided bus.Endpoint | 44 | // New returns a new Haptic that'll use the provided bus.Endpoint |
1424 | 43 | func New(endp bus.Endpoint, log logger.Logger, fallback *launch_helper.Vibration) *Haptic { | 45 | func New(endp bus.Endpoint, log logger.Logger, acc accounts.Accounts, fallback *launch_helper.Vibration) *Haptic { |
1425 | 44 | return &Haptic{endp, log, fallback} | 46 | return &Haptic{endp, log, acc, fallback} |
1426 | 45 | } | 47 | } |
1427 | 46 | 48 | ||
1428 | 47 | // Present presents the notification via a vibrate pattern | 49 | // Present presents the notification via a vibrate pattern |
1429 | @@ -50,6 +52,11 @@ | |||
1430 | 50 | panic("please check notification is not nil before calling present") | 52 | panic("please check notification is not nil before calling present") |
1431 | 51 | } | 53 | } |
1432 | 52 | 54 | ||
1433 | 55 | if !haptic.acc.Vibrate() { | ||
1434 | 56 | haptic.log.Debugf("[%s] vibrate disabled by user.", nid) | ||
1435 | 57 | return false | ||
1436 | 58 | } | ||
1437 | 59 | |||
1438 | 53 | vib := notification.Vibration(haptic.fallback) | 60 | vib := notification.Vibration(haptic.fallback) |
1439 | 54 | if vib == nil { | 61 | if vib == nil { |
1440 | 55 | haptic.log.Debugf("[%s] notification has no Vibrate.", nid) | 62 | haptic.log.Debugf("[%s] notification has no Vibrate.", nid) |
1441 | 56 | 63 | ||
1442 | === modified file 'bus/haptic/haptic_test.go' | |||
1443 | --- bus/haptic/haptic_test.go 2014-08-08 01:07:38 +0000 | |||
1444 | +++ bus/haptic/haptic_test.go 2015-03-26 16:42:21 +0000 | |||
1445 | @@ -35,20 +35,36 @@ | |||
1446 | 35 | type hapticSuite struct { | 35 | type hapticSuite struct { |
1447 | 36 | log *helpers.TestLogger | 36 | log *helpers.TestLogger |
1448 | 37 | app *click.AppId | 37 | app *click.AppId |
1450 | 38 | } | 38 | acc *mockAccounts |
1451 | 39 | } | ||
1452 | 40 | |||
1453 | 41 | type mockAccounts struct { | ||
1454 | 42 | vib bool | ||
1455 | 43 | sil bool | ||
1456 | 44 | snd string | ||
1457 | 45 | err error | ||
1458 | 46 | } | ||
1459 | 47 | |||
1460 | 48 | func (m *mockAccounts) Start() error { return m.err } | ||
1461 | 49 | func (m *mockAccounts) Cancel() error { return m.err } | ||
1462 | 50 | func (m *mockAccounts) SilentMode() bool { return m.sil } | ||
1463 | 51 | func (m *mockAccounts) Vibrate() bool { return m.vib } | ||
1464 | 52 | func (m *mockAccounts) MessageSoundFile() string { return m.snd } | ||
1465 | 53 | func (m *mockAccounts) String() string { return "<mockAccounts>" } | ||
1466 | 39 | 54 | ||
1467 | 40 | var _ = Suite(&hapticSuite{}) | 55 | var _ = Suite(&hapticSuite{}) |
1468 | 41 | 56 | ||
1469 | 42 | func (hs *hapticSuite) SetUpTest(c *C) { | 57 | func (hs *hapticSuite) SetUpTest(c *C) { |
1470 | 43 | hs.log = helpers.NewTestLogger(c, "debug") | 58 | hs.log = helpers.NewTestLogger(c, "debug") |
1471 | 44 | hs.app = clickhelp.MustParseAppId("com.example.test_test-app_0") | 59 | hs.app = clickhelp.MustParseAppId("com.example.test_test-app_0") |
1472 | 60 | hs.acc = &mockAccounts{true, false, "xyzzy", nil} | ||
1473 | 45 | } | 61 | } |
1474 | 46 | 62 | ||
1475 | 47 | // checks that Present() actually calls VibratePattern | 63 | // checks that Present() actually calls VibratePattern |
1476 | 48 | func (hs *hapticSuite) TestPresentPresents(c *C) { | 64 | func (hs *hapticSuite) TestPresentPresents(c *C) { |
1477 | 49 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true)) | 65 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true)) |
1478 | 50 | 66 | ||
1480 | 51 | ec := New(endp, hs.log, nil) | 67 | ec := New(endp, hs.log, hs.acc, nil) |
1481 | 52 | notif := launch_helper.Notification{RawVibration: json.RawMessage(`{"pattern": [200, 100], "repeat": 2}`)} | 68 | notif := launch_helper.Notification{RawVibration: json.RawMessage(`{"pattern": [200, 100], "repeat": 2}`)} |
1482 | 53 | c.Check(ec.Present(hs.app, "nid", ¬if), Equals, true) | 69 | c.Check(ec.Present(hs.app, "nid", ¬if), Equals, true) |
1483 | 54 | callArgs := testibus.GetCallArgs(endp) | 70 | callArgs := testibus.GetCallArgs(endp) |
1484 | @@ -61,7 +77,7 @@ | |||
1485 | 61 | func (hs *hapticSuite) TestPresentDefaultsRepeatTo1(c *C) { | 77 | func (hs *hapticSuite) TestPresentDefaultsRepeatTo1(c *C) { |
1486 | 62 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true)) | 78 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true)) |
1487 | 63 | 79 | ||
1489 | 64 | ec := New(endp, hs.log, nil) | 80 | ec := New(endp, hs.log, hs.acc, nil) |
1490 | 65 | // note: no Repeat: | 81 | // note: no Repeat: |
1491 | 66 | notif := launch_helper.Notification{RawVibration: json.RawMessage(`{"pattern": [200, 100]}`)} | 82 | notif := launch_helper.Notification{RawVibration: json.RawMessage(`{"pattern": [200, 100]}`)} |
1492 | 67 | c.Check(ec.Present(hs.app, "nid", ¬if), Equals, true) | 83 | c.Check(ec.Present(hs.app, "nid", ¬if), Equals, true) |
1493 | @@ -76,7 +92,7 @@ | |||
1494 | 76 | func (hs *hapticSuite) TestSkipIfMissing(c *C) { | 92 | func (hs *hapticSuite) TestSkipIfMissing(c *C) { |
1495 | 77 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true)) | 93 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true)) |
1496 | 78 | 94 | ||
1498 | 79 | ec := New(endp, hs.log, nil) | 95 | ec := New(endp, hs.log, hs.acc, nil) |
1499 | 80 | // no Vibration in the notificaton | 96 | // no Vibration in the notificaton |
1500 | 81 | c.Check(ec.Present(hs.app, "", &launch_helper.Notification{}), Equals, false) | 97 | c.Check(ec.Present(hs.app, "", &launch_helper.Notification{}), Equals, false) |
1501 | 82 | // empty Vibration | 98 | // empty Vibration |
1502 | @@ -85,11 +101,24 @@ | |||
1503 | 85 | c.Check(ec.Present(hs.app, "", &launch_helper.Notification{RawVibration: json.RawMessage(`{}`)}), Equals, false) | 101 | c.Check(ec.Present(hs.app, "", &launch_helper.Notification{RawVibration: json.RawMessage(`{}`)}), Equals, false) |
1504 | 86 | } | 102 | } |
1505 | 87 | 103 | ||
1506 | 104 | // check that Present() does not present if the accounts' Vibrate() returns false | ||
1507 | 105 | func (hs *hapticSuite) TestPresentSkipsIfVibrateDisabled(c *C) { | ||
1508 | 106 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true)) | ||
1509 | 107 | fallback := &launch_helper.Vibration{Pattern: []uint32{200, 100}, Repeat: 2} | ||
1510 | 108 | |||
1511 | 109 | ec := New(endp, hs.log, hs.acc, fallback) | ||
1512 | 110 | notif := launch_helper.Notification{RawVibration: json.RawMessage(`true`)} | ||
1513 | 111 | c.Assert(ec.Present(hs.app, "nid", ¬if), Equals, true) | ||
1514 | 112 | // ok! | ||
1515 | 113 | hs.acc.vib = false | ||
1516 | 114 | c.Check(ec.Present(hs.app, "nid", ¬if), Equals, false) | ||
1517 | 115 | } | ||
1518 | 116 | |||
1519 | 88 | // check that Present() panics if the notification is nil | 117 | // check that Present() panics if the notification is nil |
1520 | 89 | func (hs *hapticSuite) TestPanicsIfNil(c *C) { | 118 | func (hs *hapticSuite) TestPanicsIfNil(c *C) { |
1521 | 90 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true)) | 119 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true)) |
1522 | 91 | 120 | ||
1524 | 92 | ec := New(endp, hs.log, nil) | 121 | ec := New(endp, hs.log, hs.acc, nil) |
1525 | 93 | // no notification at all | 122 | // no notification at all |
1526 | 94 | c.Check(func() { ec.Present(hs.app, "", nil) }, Panics, `please check notification is not nil before calling present`) | 123 | c.Check(func() { ec.Present(hs.app, "", nil) }, Panics, `please check notification is not nil before calling present`) |
1527 | 95 | } | 124 | } |
1528 | @@ -99,7 +128,7 @@ | |||
1529 | 99 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true)) | 128 | endp := testibus.NewTestingEndpoint(nil, condition.Work(true)) |
1530 | 100 | fallback := &launch_helper.Vibration{Pattern: []uint32{200, 100}, Repeat: 2} | 129 | fallback := &launch_helper.Vibration{Pattern: []uint32{200, 100}, Repeat: 2} |
1531 | 101 | 130 | ||
1533 | 102 | ec := New(endp, hs.log, fallback) | 131 | ec := New(endp, hs.log, hs.acc, fallback) |
1534 | 103 | notif := launch_helper.Notification{RawVibration: json.RawMessage(`false`)} | 132 | notif := launch_helper.Notification{RawVibration: json.RawMessage(`false`)} |
1535 | 104 | c.Check(ec.Present(hs.app, "nid", ¬if), Equals, false) | 133 | c.Check(ec.Present(hs.app, "nid", ¬if), Equals, false) |
1536 | 105 | notif = launch_helper.Notification{RawVibration: json.RawMessage(`true`)} | 134 | notif = launch_helper.Notification{RawVibration: json.RawMessage(`true`)} |
1537 | 106 | 135 | ||
1538 | === modified file 'bus/networkmanager/networkmanager.go' | |||
1539 | --- bus/networkmanager/networkmanager.go 2015-01-22 17:34:18 +0000 | |||
1540 | +++ bus/networkmanager/networkmanager.go 2015-03-26 16:42:21 +0000 | |||
1541 | @@ -42,13 +42,13 @@ | |||
1542 | 42 | GetState() State | 42 | GetState() State |
1543 | 43 | // WatchState listens for changes to NetworkManager's state, and sends | 43 | // WatchState listens for changes to NetworkManager's state, and sends |
1544 | 44 | // them out over the channel returned. | 44 | // them out over the channel returned. |
1546 | 45 | WatchState() (<-chan State, error) | 45 | WatchState() (<-chan State, bus.Cancellable, error) |
1547 | 46 | // GetPrimaryConnection fetches and returns NetworkManager's current | 46 | // GetPrimaryConnection fetches and returns NetworkManager's current |
1548 | 47 | // primary connection. | 47 | // primary connection. |
1549 | 48 | GetPrimaryConnection() string | 48 | GetPrimaryConnection() string |
1550 | 49 | // WatchPrimaryConnection listens for changes of NetworkManager's | 49 | // WatchPrimaryConnection listens for changes of NetworkManager's |
1551 | 50 | // Primary Connection, and sends it out over the channel returned. | 50 | // Primary Connection, and sends it out over the channel returned. |
1553 | 51 | WatchPrimaryConnection() (<-chan string, error) | 51 | WatchPrimaryConnection() (<-chan string, bus.Cancellable, error) |
1554 | 52 | } | 52 | } |
1555 | 53 | 53 | ||
1556 | 54 | type networkManager struct { | 54 | type networkManager struct { |
1557 | @@ -85,9 +85,9 @@ | |||
1558 | 85 | return State(v) | 85 | return State(v) |
1559 | 86 | } | 86 | } |
1560 | 87 | 87 | ||
1562 | 88 | func (nm *networkManager) WatchState() (<-chan State, error) { | 88 | func (nm *networkManager) WatchState() (<-chan State, bus.Cancellable, error) { |
1563 | 89 | ch := make(chan State) | 89 | ch := make(chan State) |
1565 | 90 | err := nm.bus.WatchSignal("StateChanged", | 90 | w, err := nm.bus.WatchSignal("StateChanged", |
1566 | 91 | func(ns ...interface{}) { | 91 | func(ns ...interface{}) { |
1567 | 92 | stint, ok := ns[0].(uint32) | 92 | stint, ok := ns[0].(uint32) |
1568 | 93 | if !ok { | 93 | if !ok { |
1569 | @@ -101,10 +101,10 @@ | |||
1570 | 101 | func() { close(ch) }) | 101 | func() { close(ch) }) |
1571 | 102 | if err != nil { | 102 | if err != nil { |
1572 | 103 | nm.log.Debugf("Failed to set up the watch: %s", err) | 103 | nm.log.Debugf("Failed to set up the watch: %s", err) |
1574 | 104 | return nil, err | 104 | return nil, nil, err |
1575 | 105 | } | 105 | } |
1576 | 106 | 106 | ||
1578 | 107 | return ch, nil | 107 | return ch, w, nil |
1579 | 108 | } | 108 | } |
1580 | 109 | 109 | ||
1581 | 110 | func (nm *networkManager) GetPrimaryConnection() string { | 110 | func (nm *networkManager) GetPrimaryConnection() string { |
1582 | @@ -124,9 +124,9 @@ | |||
1583 | 124 | return string(v) | 124 | return string(v) |
1584 | 125 | } | 125 | } |
1585 | 126 | 126 | ||
1587 | 127 | func (nm *networkManager) WatchPrimaryConnection() (<-chan string, error) { | 127 | func (nm *networkManager) WatchPrimaryConnection() (<-chan string, bus.Cancellable, error) { |
1588 | 128 | ch := make(chan string) | 128 | ch := make(chan string) |
1590 | 129 | err := nm.bus.WatchSignal("PropertiesChanged", | 129 | w, err := nm.bus.WatchSignal("PropertiesChanged", |
1591 | 130 | func(ppsi ...interface{}) { | 130 | func(ppsi ...interface{}) { |
1592 | 131 | pps, ok := ppsi[0].(map[string]dbus.Variant) | 131 | pps, ok := ppsi[0].(map[string]dbus.Variant) |
1593 | 132 | if !ok { | 132 | if !ok { |
1594 | @@ -147,8 +147,8 @@ | |||
1595 | 147 | }, func() { close(ch) }) | 147 | }, func() { close(ch) }) |
1596 | 148 | if err != nil { | 148 | if err != nil { |
1597 | 149 | nm.log.Debugf("failed to set up the watch: %s", err) | 149 | nm.log.Debugf("failed to set up the watch: %s", err) |
1599 | 150 | return nil, err | 150 | return nil, nil, err |
1600 | 151 | } | 151 | } |
1601 | 152 | 152 | ||
1603 | 153 | return ch, nil | 153 | return ch, w, nil |
1604 | 154 | } | 154 | } |
1605 | 155 | 155 | ||
1606 | === modified file 'bus/networkmanager/networkmanager_test.go' | |||
1607 | --- bus/networkmanager/networkmanager_test.go 2014-04-04 12:01:42 +0000 | |||
1608 | +++ bus/networkmanager/networkmanager_test.go 2015-03-26 16:42:21 +0000 | |||
1609 | @@ -90,8 +90,9 @@ | |||
1610 | 90 | func (s *NMSuite) TestWatchState(c *C) { | 90 | func (s *NMSuite) TestWatchState(c *C) { |
1611 | 91 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), uint32(Unknown), uint32(Asleep), uint32(ConnectedGlobal)) | 91 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), uint32(Unknown), uint32(Asleep), uint32(ConnectedGlobal)) |
1612 | 92 | nm := New(tc, s.log) | 92 | nm := New(tc, s.log) |
1615 | 93 | ch, err := nm.WatchState() | 93 | ch, w, err := nm.WatchState() |
1616 | 94 | c.Check(err, IsNil) | 94 | c.Assert(err, IsNil) |
1617 | 95 | defer w.Cancel() | ||
1618 | 95 | l := []State{<-ch, <-ch, <-ch} | 96 | l := []State{<-ch, <-ch, <-ch} |
1619 | 96 | c.Check(l, DeepEquals, []State{Unknown, Asleep, ConnectedGlobal}) | 97 | c.Check(l, DeepEquals, []State{Unknown, Asleep, ConnectedGlobal}) |
1620 | 97 | } | 98 | } |
1621 | @@ -99,7 +100,7 @@ | |||
1622 | 99 | // WatchState returns on error if the dbus call fails | 100 | // WatchState returns on error if the dbus call fails |
1623 | 100 | func (s *NMSuite) TestWatchStateFails(c *C) { | 101 | func (s *NMSuite) TestWatchStateFails(c *C) { |
1624 | 101 | nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(false)), s.log) | 102 | nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(false)), s.log) |
1626 | 102 | _, err := nm.WatchState() | 103 | _, _, err := nm.WatchState() |
1627 | 103 | c.Check(err, NotNil) | 104 | c.Check(err, NotNil) |
1628 | 104 | } | 105 | } |
1629 | 105 | 106 | ||
1630 | @@ -107,8 +108,9 @@ | |||
1631 | 107 | func (s *NMSuite) TestWatchStateClosesOnWatchBail(c *C) { | 108 | func (s *NMSuite) TestWatchStateClosesOnWatchBail(c *C) { |
1632 | 108 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true)) | 109 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true)) |
1633 | 109 | nm := New(tc, s.log) | 110 | nm := New(tc, s.log) |
1636 | 110 | ch, err := nm.WatchState() | 111 | ch, w, err := nm.WatchState() |
1637 | 111 | c.Check(err, IsNil) | 112 | c.Assert(err, IsNil) |
1638 | 113 | defer w.Cancel() | ||
1639 | 112 | _, ok := <-ch | 114 | _, ok := <-ch |
1640 | 113 | c.Check(ok, Equals, false) | 115 | c.Check(ok, Equals, false) |
1641 | 114 | } | 116 | } |
1642 | @@ -117,8 +119,9 @@ | |||
1643 | 117 | func (s *NMSuite) TestWatchStateSurvivesRubbishValues(c *C) { | 119 | func (s *NMSuite) TestWatchStateSurvivesRubbishValues(c *C) { |
1644 | 118 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), "a") | 120 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), "a") |
1645 | 119 | nm := New(tc, s.log) | 121 | nm := New(tc, s.log) |
1648 | 120 | ch, err := nm.WatchState() | 122 | ch, w, err := nm.WatchState() |
1649 | 121 | c.Check(err, IsNil) | 123 | c.Assert(err, IsNil) |
1650 | 124 | defer w.Cancel() | ||
1651 | 122 | _, ok := <-ch | 125 | _, ok := <-ch |
1652 | 123 | c.Check(ok, Equals, false) | 126 | c.Check(ok, Equals, false) |
1653 | 124 | } | 127 | } |
1654 | @@ -164,8 +167,9 @@ | |||
1655 | 164 | mkPriConMap("/b/2"), | 167 | mkPriConMap("/b/2"), |
1656 | 165 | mkPriConMap("/c/3")) | 168 | mkPriConMap("/c/3")) |
1657 | 166 | nm := New(tc, s.log) | 169 | nm := New(tc, s.log) |
1660 | 167 | ch, err := nm.WatchPrimaryConnection() | 170 | ch, w, err := nm.WatchPrimaryConnection() |
1661 | 168 | c.Check(err, IsNil) | 171 | c.Assert(err, IsNil) |
1662 | 172 | defer w.Cancel() | ||
1663 | 169 | l := []string{<-ch, <-ch, <-ch} | 173 | l := []string{<-ch, <-ch, <-ch} |
1664 | 170 | c.Check(l, DeepEquals, []string{"/a/1", "/b/2", "/c/3"}) | 174 | c.Check(l, DeepEquals, []string{"/a/1", "/b/2", "/c/3"}) |
1665 | 171 | } | 175 | } |
1666 | @@ -173,7 +177,7 @@ | |||
1667 | 173 | // WatchPrimaryConnection returns on error if the dbus call fails | 177 | // WatchPrimaryConnection returns on error if the dbus call fails |
1668 | 174 | func (s *NMSuite) TestWatchPrimaryConnectionFails(c *C) { | 178 | func (s *NMSuite) TestWatchPrimaryConnectionFails(c *C) { |
1669 | 175 | nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(false)), s.log) | 179 | nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(false)), s.log) |
1671 | 176 | _, err := nm.WatchPrimaryConnection() | 180 | _, _, err := nm.WatchPrimaryConnection() |
1672 | 177 | c.Check(err, NotNil) | 181 | c.Check(err, NotNil) |
1673 | 178 | } | 182 | } |
1674 | 179 | 183 | ||
1675 | @@ -181,8 +185,9 @@ | |||
1676 | 181 | func (s *NMSuite) TestWatchPrimaryConnectionClosesOnWatchBail(c *C) { | 185 | func (s *NMSuite) TestWatchPrimaryConnectionClosesOnWatchBail(c *C) { |
1677 | 182 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true)) | 186 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true)) |
1678 | 183 | nm := New(tc, s.log) | 187 | nm := New(tc, s.log) |
1681 | 184 | ch, err := nm.WatchPrimaryConnection() | 188 | ch, w, err := nm.WatchPrimaryConnection() |
1682 | 185 | c.Check(err, IsNil) | 189 | c.Assert(err, IsNil) |
1683 | 190 | defer w.Cancel() | ||
1684 | 186 | _, ok := <-ch | 191 | _, ok := <-ch |
1685 | 187 | c.Check(ok, Equals, false) | 192 | c.Check(ok, Equals, false) |
1686 | 188 | } | 193 | } |
1687 | @@ -191,8 +196,9 @@ | |||
1688 | 191 | func (s *NMSuite) TestWatchPrimaryConnectionSurvivesRubbishValues(c *C) { | 196 | func (s *NMSuite) TestWatchPrimaryConnectionSurvivesRubbishValues(c *C) { |
1689 | 192 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), "a") | 197 | tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), "a") |
1690 | 193 | nm := New(tc, s.log) | 198 | nm := New(tc, s.log) |
1692 | 194 | ch, err := nm.WatchPrimaryConnection() | 199 | ch, w, err := nm.WatchPrimaryConnection() |
1693 | 195 | c.Assert(err, IsNil) | 200 | c.Assert(err, IsNil) |
1694 | 201 | defer w.Cancel() | ||
1695 | 196 | _, ok := <-ch | 202 | _, ok := <-ch |
1696 | 197 | c.Check(ok, Equals, false) | 203 | c.Check(ok, Equals, false) |
1697 | 198 | } | 204 | } |
1698 | @@ -204,8 +210,9 @@ | |||
1699 | 204 | map[string]dbus.Variant{"PrimaryConnection": dbus.Variant{dbus.ObjectPath("42")}}, | 210 | map[string]dbus.Variant{"PrimaryConnection": dbus.Variant{dbus.ObjectPath("42")}}, |
1700 | 205 | ) | 211 | ) |
1701 | 206 | nm := New(tc, s.log) | 212 | nm := New(tc, s.log) |
1703 | 207 | ch, err := nm.WatchPrimaryConnection() | 213 | ch, w, err := nm.WatchPrimaryConnection() |
1704 | 208 | c.Assert(err, IsNil) | 214 | c.Assert(err, IsNil) |
1705 | 215 | defer w.Cancel() | ||
1706 | 209 | v, ok := <-ch | 216 | v, ok := <-ch |
1707 | 210 | c.Check(ok, Equals, true) | 217 | c.Check(ok, Equals, true) |
1708 | 211 | c.Check(v, Equals, "42") | 218 | c.Check(v, Equals, "42") |
1709 | @@ -218,8 +225,9 @@ | |||
1710 | 218 | map[string]dbus.Variant{"PrimaryConnection": dbus.Variant{dbus.ObjectPath("42")}}, | 225 | map[string]dbus.Variant{"PrimaryConnection": dbus.Variant{dbus.ObjectPath("42")}}, |
1711 | 219 | ) | 226 | ) |
1712 | 220 | nm := New(tc, s.log) | 227 | nm := New(tc, s.log) |
1714 | 221 | ch, err := nm.WatchPrimaryConnection() | 228 | ch, w, err := nm.WatchPrimaryConnection() |
1715 | 222 | c.Assert(err, IsNil) | 229 | c.Assert(err, IsNil) |
1716 | 230 | defer w.Cancel() | ||
1717 | 223 | v, ok := <-ch | 231 | v, ok := <-ch |
1718 | 224 | c.Check(ok, Equals, true) | 232 | c.Check(ok, Equals, true) |
1719 | 225 | c.Check(v, Equals, "42") | 233 | c.Check(v, Equals, "42") |
1720 | 226 | 234 | ||
1721 | === modified file 'bus/notifications/raw.go' | |||
1722 | --- bus/notifications/raw.go 2015-01-22 17:34:18 +0000 | |||
1723 | +++ bus/notifications/raw.go 2015-03-26 16:42:21 +0000 | |||
1724 | @@ -93,7 +93,7 @@ | |||
1725 | 93 | // and sends them over the channel provided | 93 | // and sends them over the channel provided |
1726 | 94 | func (raw *RawNotifications) WatchActions() (<-chan *RawAction, error) { | 94 | func (raw *RawNotifications) WatchActions() (<-chan *RawAction, error) { |
1727 | 95 | ch := make(chan *RawAction) | 95 | ch := make(chan *RawAction) |
1729 | 96 | err := raw.bus.WatchSignal("ActionInvoked", | 96 | _, err := raw.bus.WatchSignal("ActionInvoked", |
1730 | 97 | func(ns ...interface{}) { | 97 | func(ns ...interface{}) { |
1731 | 98 | if len(ns) != 2 { | 98 | if len(ns) != 2 { |
1732 | 99 | raw.log.Debugf("ActionInvoked delivered %d things instead of 2", len(ns)) | 99 | raw.log.Debugf("ActionInvoked delivered %d things instead of 2", len(ns)) |
1733 | 100 | 100 | ||
1734 | === modified file 'bus/notifications/raw_test.go' | |||
1735 | --- bus/notifications/raw_test.go 2014-08-15 10:33:04 +0000 | |||
1736 | +++ bus/notifications/raw_test.go 2015-03-26 16:42:21 +0000 | |||
1737 | @@ -111,14 +111,16 @@ | |||
1738 | 111 | errstr string | 111 | errstr string |
1739 | 112 | endp bus.Endpoint | 112 | endp bus.Endpoint |
1740 | 113 | works bool | 113 | works bool |
1741 | 114 | src chan []interface{} | ||
1742 | 114 | } | 115 | } |
1743 | 115 | 116 | ||
1744 | 116 | func (s *RawSuite) TestWatchActionsToleratesDBusWeirdness(c *C) { | 117 | func (s *RawSuite) TestWatchActionsToleratesDBusWeirdness(c *C) { |
1745 | 117 | X := func(errstr string, args ...interface{}) tst { | 118 | X := func(errstr string, args ...interface{}) tst { |
1750 | 118 | endp := testibus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), args) | 119 | endp := testibus.NewMultiValuedTestingEndpoint(nil, condition.Work(true)) |
1751 | 119 | // stop the endpoint from closing the channel: | 120 | src := make(chan []interface{}, 1) |
1752 | 120 | testibus.SetWatchTicker(endp, make(chan bool)) | 121 | testibus.SetWatchSource(endp, "ActionInvoked", src) |
1753 | 121 | return tst{errstr, endp, errstr == ""} | 122 | src <- args |
1754 | 123 | return tst{errstr, endp, errstr == "", src} | ||
1755 | 122 | } | 124 | } |
1756 | 123 | 125 | ||
1757 | 124 | ts := []tst{ | 126 | ts := []tst{ |
1758 | @@ -146,6 +148,7 @@ | |||
1759 | 146 | } | 148 | } |
1760 | 147 | c.Check(s.log.Captured(), Matches, `(?ms).*`+t.errstr+`.*`) | 149 | c.Check(s.log.Captured(), Matches, `(?ms).*`+t.errstr+`.*`) |
1761 | 148 | s.log.ResetCapture() | 150 | s.log.ResetCapture() |
1762 | 151 | close(t.src) | ||
1763 | 149 | } | 152 | } |
1764 | 150 | 153 | ||
1765 | 151 | } | 154 | } |
1766 | 152 | 155 | ||
1767 | === modified file 'bus/testing/testing_endpoint.go' | |||
1768 | --- bus/testing/testing_endpoint.go 2014-07-04 23:00:42 +0000 | |||
1769 | +++ bus/testing/testing_endpoint.go 2015-03-26 16:42:21 +0000 | |||
1770 | @@ -36,13 +36,15 @@ | |||
1771 | 36 | } | 36 | } |
1772 | 37 | 37 | ||
1773 | 38 | type testingEndpoint struct { | 38 | type testingEndpoint struct { |
1781 | 39 | dialCond condition.Interface | 39 | dialCond condition.Interface |
1782 | 40 | callCond condition.Interface | 40 | callCond condition.Interface |
1783 | 41 | retvals [][]interface{} | 41 | usedLck sync.Mutex |
1784 | 42 | watchTicker chan bool | 42 | used int |
1785 | 43 | watchLck sync.RWMutex | 43 | retvals [][]interface{} |
1786 | 44 | callArgs []callArgs | 44 | watchSources map[string]chan []interface{} |
1787 | 45 | callArgsLck sync.RWMutex | 45 | watchLck sync.RWMutex |
1788 | 46 | callArgs []callArgs | ||
1789 | 47 | callArgsLck sync.RWMutex | ||
1790 | 46 | } | 48 | } |
1791 | 47 | 49 | ||
1792 | 48 | // Build a bus.Endpoint that calls OK() on its condition before returning | 50 | // Build a bus.Endpoint that calls OK() on its condition before returning |
1793 | @@ -51,7 +53,7 @@ | |||
1794 | 51 | // NOTE: Call() always returns the first return value; Watch() will provide | 53 | // NOTE: Call() always returns the first return value; Watch() will provide |
1795 | 52 | // each of them in turn, irrespective of whether Call has been called. | 54 | // each of them in turn, irrespective of whether Call has been called. |
1796 | 53 | func NewMultiValuedTestingEndpoint(dialCond condition.Interface, callCond condition.Interface, retvalses ...[]interface{}) bus.Endpoint { | 55 | func NewMultiValuedTestingEndpoint(dialCond condition.Interface, callCond condition.Interface, retvalses ...[]interface{}) bus.Endpoint { |
1798 | 54 | return &testingEndpoint{dialCond: dialCond, callCond: callCond, retvals: retvalses} | 56 | return &testingEndpoint{dialCond: dialCond, callCond: callCond, retvals: retvalses, watchSources: make(map[string]chan []interface{})} |
1799 | 55 | } | 57 | } |
1800 | 56 | 58 | ||
1801 | 57 | func NewTestingEndpoint(dialCond condition.Interface, callCond condition.Interface, retvals ...interface{}) bus.Endpoint { | 59 | func NewTestingEndpoint(dialCond condition.Interface, callCond condition.Interface, retvals ...interface{}) bus.Endpoint { |
1802 | @@ -59,15 +61,15 @@ | |||
1803 | 59 | for i, x := range retvals { | 61 | for i, x := range retvals { |
1804 | 60 | retvalses[i] = []interface{}{x} | 62 | retvalses[i] = []interface{}{x} |
1805 | 61 | } | 63 | } |
1807 | 62 | return &testingEndpoint{dialCond: dialCond, callCond: callCond, retvals: retvalses} | 64 | return &testingEndpoint{dialCond: dialCond, callCond: callCond, retvals: retvalses, watchSources: make(map[string]chan []interface{})} |
1808 | 63 | } | 65 | } |
1809 | 64 | 66 | ||
1814 | 65 | // If SetWatchTicker is called with a non-nil watchTicker, it is used | 67 | // If SetWatchSource is called with a non-nil watchSource, it is used |
1815 | 66 | // instead of the default timeout to wait while sending values over | 68 | // instead of the default timeout and retvals to get values to send |
1816 | 67 | // WatchSignal. Set it to nil again to restore default behaviour. | 69 | // over WatchSignal. Set it to nil again to restore default behaviour. |
1817 | 68 | func SetWatchTicker(tc bus.Endpoint, watchTicker chan bool) { | 70 | func SetWatchSource(tc bus.Endpoint, member string, watchSource chan []interface{}) { |
1818 | 69 | tc.(*testingEndpoint).watchLck.Lock() | 71 | tc.(*testingEndpoint).watchLck.Lock() |
1820 | 70 | tc.(*testingEndpoint).watchTicker = watchTicker | 72 | tc.(*testingEndpoint).watchSources[member] = watchSource |
1821 | 71 | tc.(*testingEndpoint).watchLck.Unlock() | 73 | tc.(*testingEndpoint).watchLck.Unlock() |
1822 | 72 | } | 74 | } |
1823 | 73 | 75 | ||
1824 | @@ -78,27 +80,77 @@ | |||
1825 | 78 | return tc.(*testingEndpoint).callArgs | 80 | return tc.(*testingEndpoint).callArgs |
1826 | 79 | } | 81 | } |
1827 | 80 | 82 | ||
1828 | 83 | type watchCancel struct { | ||
1829 | 84 | done chan struct{} | ||
1830 | 85 | cancelled chan struct{} | ||
1831 | 86 | lck sync.Mutex | ||
1832 | 87 | member string | ||
1833 | 88 | } | ||
1834 | 89 | |||
1835 | 90 | // this waits for actual cancelllation for test convenience | ||
1836 | 91 | func (wc *watchCancel) Cancel() error { | ||
1837 | 92 | wc.lck.Lock() | ||
1838 | 93 | defer wc.lck.Unlock() | ||
1839 | 94 | if wc.cancelled != nil { | ||
1840 | 95 | close(wc.cancelled) | ||
1841 | 96 | wc.cancelled = nil | ||
1842 | 97 | <-wc.done | ||
1843 | 98 | } | ||
1844 | 99 | return nil | ||
1845 | 100 | } | ||
1846 | 101 | |||
1847 | 81 | // See Endpoint's WatchSignal. This WatchSignal will check its condition to | 102 | // See Endpoint's WatchSignal. This WatchSignal will check its condition to |
1848 | 82 | // decide whether to return an error, or provide each of its return values | 103 | // decide whether to return an error, or provide each of its return values |
1850 | 83 | func (tc *testingEndpoint) WatchSignal(member string, f func(...interface{}), d func()) error { | 104 | // or values from the previously set watchSource for member. |
1851 | 105 | func (tc *testingEndpoint) WatchSignal(member string, f func(...interface{}), d func()) (bus.Cancellable, error) { | ||
1852 | 84 | if tc.callCond.OK() { | 106 | if tc.callCond.OK() { |
1853 | 107 | cancelled := make(chan struct{}) | ||
1854 | 108 | done := make(chan struct{}) | ||
1855 | 85 | go func() { | 109 | go func() { |
1865 | 86 | for _, v := range tc.retvals { | 110 | tc.watchLck.RLock() |
1866 | 87 | f(v...) | 111 | source := tc.watchSources[member] |
1867 | 88 | tc.watchLck.RLock() | 112 | tc.watchLck.RUnlock() |
1868 | 89 | ticker := tc.watchTicker | 113 | if source == nil { |
1869 | 90 | tc.watchLck.RUnlock() | 114 | tc.usedLck.Lock() |
1870 | 91 | if ticker != nil { | 115 | idx := tc.used |
1871 | 92 | <-ticker | 116 | tc.used++ |
1872 | 93 | } else { | 117 | tc.usedLck.Unlock() |
1873 | 94 | time.Sleep(10 * time.Millisecond) | 118 | source = make(chan []interface{}) |
1874 | 119 | go func() { | ||
1875 | 120 | Feed: | ||
1876 | 121 | for _, v := range tc.retvals[idx:] { | ||
1877 | 122 | select { | ||
1878 | 123 | case source <- v: | ||
1879 | 124 | case <-cancelled: | ||
1880 | 125 | break Feed | ||
1881 | 126 | } | ||
1882 | 127 | select { | ||
1883 | 128 | case <-time.After(10 * time.Millisecond): | ||
1884 | 129 | case <-cancelled: | ||
1885 | 130 | break Feed | ||
1886 | 131 | } | ||
1887 | 132 | } | ||
1888 | 133 | close(source) | ||
1889 | 134 | }() | ||
1890 | 135 | } | ||
1891 | 136 | Receive: | ||
1892 | 137 | for { | ||
1893 | 138 | select { | ||
1894 | 139 | case v, ok := <-source: | ||
1895 | 140 | if !ok { | ||
1896 | 141 | break Receive | ||
1897 | 142 | } | ||
1898 | 143 | f(v...) | ||
1899 | 144 | case <-cancelled: | ||
1900 | 145 | break Receive | ||
1901 | 95 | } | 146 | } |
1902 | 96 | } | 147 | } |
1903 | 97 | d() | 148 | d() |
1904 | 149 | close(done) | ||
1905 | 98 | }() | 150 | }() |
1907 | 99 | return nil | 151 | return &watchCancel{cancelled: cancelled, done: done, member: member}, nil |
1908 | 100 | } else { | 152 | } else { |
1910 | 101 | return errors.New("no way") | 153 | return nil, errors.New("no way") |
1911 | 102 | } | 154 | } |
1912 | 103 | } | 155 | } |
1913 | 104 | 156 | ||
1914 | @@ -112,20 +164,24 @@ | |||
1915 | 112 | if tc.callCond.OK() { | 164 | if tc.callCond.OK() { |
1916 | 113 | expected := len(rvs) | 165 | expected := len(rvs) |
1917 | 114 | var provided int | 166 | var provided int |
1919 | 115 | if len(tc.retvals) == 0 { | 167 | tc.usedLck.Lock() |
1920 | 168 | idx := tc.used | ||
1921 | 169 | tc.used++ | ||
1922 | 170 | tc.usedLck.Unlock() | ||
1923 | 171 | if len(tc.retvals) <= idx { | ||
1924 | 116 | if expected != 0 { | 172 | if expected != 0 { |
1925 | 117 | panic("No return values provided!") | 173 | panic("No return values provided!") |
1926 | 118 | } | 174 | } |
1927 | 119 | provided = 0 | 175 | provided = 0 |
1928 | 120 | } else { | 176 | } else { |
1930 | 121 | provided = len(tc.retvals[0]) | 177 | provided = len(tc.retvals[idx]) |
1931 | 122 | } | 178 | } |
1932 | 123 | if provided != expected { | 179 | if provided != expected { |
1933 | 124 | return errors.New("provided/expected return vals mismatch") | 180 | return errors.New("provided/expected return vals mismatch") |
1934 | 125 | } | 181 | } |
1935 | 126 | if provided != 0 { | 182 | if provided != 0 { |
1936 | 127 | x := dbus.NewMethodCallMessage("", "", "", "") | 183 | x := dbus.NewMethodCallMessage("", "", "", "") |
1938 | 128 | err := x.AppendArgs(tc.retvals[0]...) | 184 | err := x.AppendArgs(tc.retvals[idx]...) |
1939 | 129 | if err != nil { | 185 | if err != nil { |
1940 | 130 | return err | 186 | return err |
1941 | 131 | } | 187 | } |
1942 | 132 | 188 | ||
1943 | === modified file 'bus/testing/testing_endpoint_test.go' | |||
1944 | --- bus/testing/testing_endpoint_test.go 2014-07-04 23:00:42 +0000 | |||
1945 | +++ bus/testing/testing_endpoint_test.go 2015-03-26 16:42:21 +0000 | |||
1946 | @@ -17,11 +17,13 @@ | |||
1947 | 17 | package testing | 17 | package testing |
1948 | 18 | 18 | ||
1949 | 19 | import ( | 19 | import ( |
1950 | 20 | "testing" | ||
1951 | 21 | "time" | ||
1952 | 22 | |||
1953 | 20 | . "launchpad.net/gocheck" | 23 | . "launchpad.net/gocheck" |
1954 | 24 | |||
1955 | 21 | "launchpad.net/ubuntu-push/bus" | 25 | "launchpad.net/ubuntu-push/bus" |
1956 | 22 | "launchpad.net/ubuntu-push/testing/condition" | 26 | "launchpad.net/ubuntu-push/testing/condition" |
1957 | 23 | "testing" | ||
1958 | 24 | "time" | ||
1959 | 25 | ) | 27 | ) |
1960 | 26 | 28 | ||
1961 | 27 | // hook up gocheck | 29 | // hook up gocheck |
1962 | @@ -100,8 +102,9 @@ | |||
1963 | 100 | var m, n uint32 = 42, 17 | 102 | var m, n uint32 = 42, 17 |
1964 | 101 | endp := NewTestingEndpoint(nil, condition.Work(true), m, n) | 103 | endp := NewTestingEndpoint(nil, condition.Work(true), m, n) |
1965 | 102 | ch := make(chan uint32) | 104 | ch := make(chan uint32) |
1968 | 103 | e := endp.WatchSignal("what", func(us ...interface{}) { ch <- us[0].(uint32) }, func() { close(ch) }) | 105 | w, e := endp.WatchSignal("which", func(us ...interface{}) { ch <- us[0].(uint32) }, func() { close(ch) }) |
1969 | 104 | c.Check(e, IsNil) | 106 | c.Assert(e, IsNil) |
1970 | 107 | defer w.Cancel() | ||
1971 | 105 | c.Check(<-ch, Equals, m) | 108 | c.Check(<-ch, Equals, m) |
1972 | 106 | c.Check(<-ch, Equals, n) | 109 | c.Check(<-ch, Equals, n) |
1973 | 107 | } | 110 | } |
1974 | @@ -110,8 +113,9 @@ | |||
1975 | 110 | func (s *TestingEndpointSuite) TestWatchDestructor(c *C) { | 113 | func (s *TestingEndpointSuite) TestWatchDestructor(c *C) { |
1976 | 111 | endp := NewTestingEndpoint(nil, condition.Work(true)) | 114 | endp := NewTestingEndpoint(nil, condition.Work(true)) |
1977 | 112 | ch := make(chan uint32) | 115 | ch := make(chan uint32) |
1980 | 113 | e := endp.WatchSignal("what", func(us ...interface{}) {}, func() { close(ch) }) | 116 | w, e := endp.WatchSignal("what", func(us ...interface{}) {}, func() { close(ch) }) |
1981 | 114 | c.Check(e, IsNil) | 117 | c.Assert(e, IsNil) |
1982 | 118 | defer w.Cancel() | ||
1983 | 115 | _, ok := <-ch | 119 | _, ok := <-ch |
1984 | 116 | c.Check(ok, Equals, false) | 120 | c.Check(ok, Equals, false) |
1985 | 117 | } | 121 | } |
1986 | @@ -130,25 +134,28 @@ | |||
1987 | 130 | // Test that WatchSignal() with a negative condition returns an error. | 134 | // Test that WatchSignal() with a negative condition returns an error. |
1988 | 131 | func (s *TestingEndpointSuite) TestWatchFails(c *C) { | 135 | func (s *TestingEndpointSuite) TestWatchFails(c *C) { |
1989 | 132 | endp := NewTestingEndpoint(nil, condition.Work(false)) | 136 | endp := NewTestingEndpoint(nil, condition.Work(false)) |
1991 | 133 | e := endp.WatchSignal("what", func(us ...interface{}) {}, func() {}) | 137 | w, e := endp.WatchSignal("what", func(us ...interface{}) {}, func() {}) |
1992 | 134 | c.Check(e, NotNil) | 138 | c.Check(e, NotNil) |
1993 | 139 | c.Check(w, IsNil) | ||
1994 | 135 | } | 140 | } |
1995 | 136 | 141 | ||
1997 | 137 | // Test WatchSignal can use the WatchTicker instead of a timeout (if | 142 | // Test WatchSignal can use a watchSource instead of a timeout and retvals (if |
1998 | 138 | // the former is not nil) | 143 | // the former is not nil) |
2004 | 139 | func (s *TestingEndpointSuite) TestWatchTicker(c *C) { | 144 | func (s *TestingEndpointSuite) TestWatchSources(c *C) { |
2005 | 140 | watchTicker := make(chan bool, 3) | 145 | watchTicker := make(chan []interface{}, 3) |
2006 | 141 | watchTicker <- true | 146 | watchTicker <- []interface{}{true} |
2007 | 142 | watchTicker <- true | 147 | watchTicker <- []interface{}{true} |
2008 | 143 | watchTicker <- true | 148 | watchTicker <- []interface{}{true} |
2009 | 144 | c.Assert(len(watchTicker), Equals, 3) | 149 | c.Assert(len(watchTicker), Equals, 3) |
2010 | 145 | 150 | ||
2011 | 146 | endp := NewTestingEndpoint(nil, condition.Work(true), 0, 0) | 151 | endp := NewTestingEndpoint(nil, condition.Work(true), 0, 0) |
2013 | 147 | SetWatchTicker(endp, watchTicker) | 152 | SetWatchSource(endp, "what", watchTicker) |
2014 | 148 | ch := make(chan int) | 153 | ch := make(chan int) |
2017 | 149 | e := endp.WatchSignal("what", func(us ...interface{}) {}, func() { close(ch) }) | 154 | w, e := endp.WatchSignal("what", func(us ...interface{}) {}, func() { close(ch) }) |
2018 | 150 | c.Check(e, IsNil) | 155 | c.Assert(e, IsNil) |
2019 | 156 | defer w.Cancel() | ||
2020 | 151 | 157 | ||
2021 | 158 | close(watchTicker) | ||
2022 | 152 | // wait for the destructor to be called | 159 | // wait for the destructor to be called |
2023 | 153 | select { | 160 | select { |
2024 | 154 | case <-time.Tick(10 * time.Millisecond): | 161 | case <-time.Tick(10 * time.Millisecond): |
2025 | @@ -156,8 +163,21 @@ | |||
2026 | 156 | case <-ch: | 163 | case <-ch: |
2027 | 157 | } | 164 | } |
2028 | 158 | 165 | ||
2031 | 159 | // now if all went well, the ticker will have been tuck twice. | 166 | // now if all went well, the ticker will have been exhausted. |
2032 | 160 | c.Assert(len(watchTicker), Equals, 1) | 167 | c.Assert(len(watchTicker), Equals, 0) |
2033 | 168 | } | ||
2034 | 169 | |||
2035 | 170 | // Test that WatchSignal() calls the destructor callback when canceled. | ||
2036 | 171 | func (s *TestingEndpointSuite) TestWatchCancel(c *C) { | ||
2037 | 172 | endp := NewTestingEndpoint(nil, condition.Work(true)) | ||
2038 | 173 | ch := make(chan uint32) | ||
2039 | 174 | w, e := endp.WatchSignal("what", func(us ...interface{}) {}, func() { close(ch) }) | ||
2040 | 175 | c.Assert(e, IsNil) | ||
2041 | 176 | defer w.Cancel() | ||
2042 | 177 | SetWatchSource(endp, "what", make(chan []interface{})) | ||
2043 | 178 | w.Cancel() | ||
2044 | 179 | _, ok := <-ch | ||
2045 | 180 | c.Check(ok, Equals, false) | ||
2046 | 161 | } | 181 | } |
2047 | 162 | 182 | ||
2048 | 163 | // Tests that GetProperty() works | 183 | // Tests that GetProperty() works |
2049 | 164 | 184 | ||
2050 | === modified file 'click/cappinfo/cappinfo.go' | |||
2051 | --- click/cappinfo/cappinfo.go 2014-07-08 23:23:13 +0000 | |||
2052 | +++ click/cappinfo/cappinfo.go 2015-03-26 16:42:21 +0000 | |||
2053 | @@ -37,6 +37,26 @@ | |||
2054 | 37 | g_free (desktop_id); | 37 | g_free (desktop_id); |
2055 | 38 | return filename; | 38 | return filename; |
2056 | 39 | } | 39 | } |
2057 | 40 | |||
2058 | 41 | gchar* app_symbolic_icon_from_desktop_id (gchar* desktop_id) { | ||
2059 | 42 | gchar* x_symbolic_icon; | ||
2060 | 43 | GIcon* symbolic_icon; | ||
2061 | 44 | GDesktopAppInfo* app_info = g_desktop_app_info_new (desktop_id); | ||
2062 | 45 | if (app_info != NULL) { | ||
2063 | 46 | if((x_symbolic_icon = g_desktop_app_info_get_string(app_info, "X-Ubuntu-SymbolicIcon"))) { | ||
2064 | 47 | GFile *file; | ||
2065 | 48 | file = g_file_new_for_path(x_symbolic_icon); | ||
2066 | 49 | symbolic_icon = g_file_icon_new (file); | ||
2067 | 50 | g_object_unref (file); | ||
2068 | 51 | g_free(x_symbolic_icon); | ||
2069 | 52 | g_object_unref (app_info); | ||
2070 | 53 | return g_icon_to_string(symbolic_icon); | ||
2071 | 54 | } | ||
2072 | 55 | g_object_unref (app_info); | ||
2073 | 56 | } | ||
2074 | 57 | g_free (desktop_id); | ||
2075 | 58 | return NULL; | ||
2076 | 59 | } | ||
2077 | 40 | */ | 60 | */ |
2078 | 41 | import "C" | 61 | import "C" |
2079 | 42 | 62 | ||
2080 | @@ -45,3 +65,14 @@ | |||
2081 | 45 | defer C.g_free((C.gpointer)(name)) | 65 | defer C.g_free((C.gpointer)(name)) |
2082 | 46 | return C.GoString((*C.char)(name)) | 66 | return C.GoString((*C.char)(name)) |
2083 | 47 | } | 67 | } |
2084 | 68 | |||
2085 | 69 | func appSymbolicIconFromDesktopId(desktopId string) string { | ||
2086 | 70 | name := C.app_symbolic_icon_from_desktop_id((*C.gchar)(C.CString(desktopId))) | ||
2087 | 71 | if name == nil { | ||
2088 | 72 | return "" | ||
2089 | 73 | } | ||
2090 | 74 | defer C.g_free((C.gpointer)(name)) | ||
2091 | 75 | return C.GoString((*C.char)(name)) | ||
2092 | 76 | } | ||
2093 | 77 | |||
2094 | 78 | var AppSymbolicIconFromDesktopId = appSymbolicIconFromDesktopId | ||
2095 | 48 | 79 | ||
2096 | === modified file 'click/cclick/cclick.go' | |||
2097 | --- click/cclick/cclick.go 2014-07-07 22:04:30 +0000 | |||
2098 | +++ click/cclick/cclick.go 2015-03-26 16:42:21 +0000 | |||
2099 | @@ -51,6 +51,7 @@ | |||
2100 | 51 | } | 51 | } |
2101 | 52 | ccu.cref = cref | 52 | ccu.cref = cref |
2102 | 53 | runtime.SetFinalizer(holder, func(interface{}) { | 53 | runtime.SetFinalizer(holder, func(interface{}) { |
2103 | 54 | ccu.cref = nil // 1.3 gc gets confused otherwise | ||
2104 | 54 | C.g_object_unref((C.gpointer)(cref)) | 55 | C.g_object_unref((C.gpointer)(cref)) |
2105 | 55 | }) | 56 | }) |
2106 | 56 | return nil | 57 | return nil |
2107 | 57 | 58 | ||
2108 | === modified file 'click/click.go' | |||
2109 | --- click/click.go 2014-08-15 10:32:51 +0000 | |||
2110 | +++ click/click.go 2015-03-26 16:42:21 +0000 | |||
2111 | @@ -146,6 +146,10 @@ | |||
2112 | 146 | var symbolic = _symbolic | 146 | var symbolic = _symbolic |
2113 | 147 | 147 | ||
2114 | 148 | func (app *AppId) SymbolicIcon() string { | 148 | func (app *AppId) SymbolicIcon() string { |
2115 | 149 | symbolicIcon := cappinfo.AppSymbolicIconFromDesktopId(app.DesktopId()) | ||
2116 | 150 | if symbolicIcon != "" { | ||
2117 | 151 | return symbolicIcon | ||
2118 | 152 | } | ||
2119 | 149 | return symbolic(app.Icon()) | 153 | return symbolic(app.Icon()) |
2120 | 150 | } | 154 | } |
2121 | 151 | 155 | ||
2122 | 152 | 156 | ||
2123 | === modified file 'click/click_test.go' | |||
2124 | --- click/click_test.go 2014-08-15 10:32:51 +0000 | |||
2125 | +++ click/click_test.go 2015-03-26 16:42:21 +0000 | |||
2126 | @@ -22,6 +22,8 @@ | |||
2127 | 22 | "testing" | 22 | "testing" |
2128 | 23 | 23 | ||
2129 | 24 | . "launchpad.net/gocheck" | 24 | . "launchpad.net/gocheck" |
2130 | 25 | |||
2131 | 26 | "launchpad.net/ubuntu-push/click/cappinfo" | ||
2132 | 25 | ) | 27 | ) |
2133 | 26 | 28 | ||
2134 | 27 | func TestClick(t *testing.T) { TestingT(t) } | 29 | func TestClick(t *testing.T) { TestingT(t) } |
2135 | @@ -200,3 +202,15 @@ | |||
2136 | 200 | c.Assert(err, IsNil) | 202 | c.Assert(err, IsNil) |
2137 | 201 | c.Check(app.SymbolicIcon(), Equals, "xyzzy") | 203 | c.Check(app.SymbolicIcon(), Equals, "xyzzy") |
2138 | 202 | } | 204 | } |
2139 | 205 | |||
2140 | 206 | func (s *clickSuite) TestSymbolicFromDesktopFile(c *C) { | ||
2141 | 207 | orig := cappinfo.AppSymbolicIconFromDesktopId | ||
2142 | 208 | cappinfo.AppSymbolicIconFromDesktopId = func(desktopId string) string { | ||
2143 | 209 | return "/foo/symbolic" | ||
2144 | 210 | } | ||
2145 | 211 | defer func() { | ||
2146 | 212 | cappinfo.AppSymbolicIconFromDesktopId = orig | ||
2147 | 213 | }() | ||
2148 | 214 | app, _ := ParseAppId("com.ubuntu.clock_clock_1.2") | ||
2149 | 215 | c.Check(app.SymbolicIcon(), Equals, "/foo/symbolic") | ||
2150 | 216 | } | ||
2151 | 203 | 217 | ||
2152 | === modified file 'client/client.go' | |||
2153 | --- client/client.go 2015-01-22 17:34:18 +0000 | |||
2154 | +++ client/client.go 2015-03-26 16:42:21 +0000 | |||
2155 | @@ -115,8 +115,7 @@ | |||
2156 | 115 | systemImageEndp bus.Endpoint | 115 | systemImageEndp bus.Endpoint |
2157 | 116 | systemImageInfo *systemimage.InfoResult | 116 | systemImageInfo *systemimage.InfoResult |
2158 | 117 | connCh chan bool | 117 | connCh chan bool |
2161 | 118 | hasConnectivity bool | 118 | session session.ClientSession |
2160 | 119 | session *session.ClientSession | ||
2162 | 120 | sessionConnectedCh chan uint32 | 119 | sessionConnectedCh chan uint32 |
2163 | 121 | pushService PushService | 120 | pushService PushService |
2164 | 122 | postalService PostalService | 121 | postalService PostalService |
2165 | @@ -125,16 +124,20 @@ | |||
2166 | 125 | installedChecker click.InstalledChecker | 124 | installedChecker click.InstalledChecker |
2167 | 126 | poller poller.Poller | 125 | poller poller.Poller |
2168 | 127 | accountsCh <-chan accounts.Changed | 126 | accountsCh <-chan accounts.Changed |
2169 | 127 | // session-side channels | ||
2170 | 128 | broadcastCh chan *session.BroadcastNotification | ||
2171 | 129 | notificationsCh chan session.AddressedNotification | ||
2172 | 128 | } | 130 | } |
2173 | 129 | 131 | ||
2174 | 130 | // Creates a new Ubuntu Push Notifications client-side daemon that will use | 132 | // Creates a new Ubuntu Push Notifications client-side daemon that will use |
2175 | 131 | // the given configuration file. | 133 | // the given configuration file. |
2176 | 132 | func NewPushClient(configPath string, leveldbPath string) *PushClient { | 134 | func NewPushClient(configPath string, leveldbPath string) *PushClient { |
2182 | 133 | client := new(PushClient) | 135 | return &PushClient{ |
2183 | 134 | client.configPath = configPath | 136 | configPath: configPath, |
2184 | 135 | client.leveldbPath = leveldbPath | 137 | leveldbPath: leveldbPath, |
2185 | 136 | 138 | broadcastCh: make(chan *session.BroadcastNotification), | |
2186 | 137 | return client | 139 | notificationsCh: make(chan session.AddressedNotification), |
2187 | 140 | } | ||
2188 | 138 | } | 141 | } |
2189 | 139 | 142 | ||
2190 | 140 | var newIdentifier = identifier.New | 143 | var newIdentifier = identifier.New |
2191 | @@ -206,6 +209,8 @@ | |||
2192 | 206 | AuthGetter: client.getAuthorization, | 209 | AuthGetter: client.getAuthorization, |
2193 | 207 | AuthURL: client.config.SessionURL, | 210 | AuthURL: client.config.SessionURL, |
2194 | 208 | AddresseeChecker: client, | 211 | AddresseeChecker: client, |
2195 | 212 | BroadcastCh: client.broadcastCh, | ||
2196 | 213 | NotificationsCh: client.notificationsCh, | ||
2197 | 209 | } | 214 | } |
2198 | 210 | } | 215 | } |
2199 | 211 | 216 | ||
2200 | @@ -280,8 +285,10 @@ | |||
2201 | 280 | 285 | ||
2202 | 281 | // takeTheBus starts the connection(s) to D-Bus and sets up associated event channels | 286 | // takeTheBus starts the connection(s) to D-Bus and sets up associated event channels |
2203 | 282 | func (client *PushClient) takeTheBus() error { | 287 | func (client *PushClient) takeTheBus() error { |
2206 | 283 | go connectivity.ConnectedState(client.connectivityEndp, | 288 | fmt.Println("FOO") |
2207 | 284 | client.config.ConnectivityConfig, client.log, client.connCh) | 289 | cs := connectivity.New(client.connectivityEndp, |
2208 | 290 | client.config.ConnectivityConfig, client.log) | ||
2209 | 291 | go cs.Track(client.connCh) | ||
2210 | 285 | util.NewAutoRedialer(client.systemImageEndp).Redial() | 292 | util.NewAutoRedialer(client.systemImageEndp).Redial() |
2211 | 286 | sysimg := systemimage.New(client.systemImageEndp, client.log) | 293 | sysimg := systemimage.New(client.systemImageEndp, client.log) |
2212 | 287 | info, err := sysimg.Info() | 294 | info, err := sysimg.Info() |
2213 | @@ -306,6 +313,7 @@ | |||
2214 | 306 | return err | 313 | return err |
2215 | 307 | } | 314 | } |
2216 | 308 | client.session = sess | 315 | client.session = sess |
2217 | 316 | sess.KeepConnection() | ||
2218 | 309 | client.poller = poller.New(client.derivePollerSetup()) | 317 | client.poller = poller.New(client.derivePollerSetup()) |
2219 | 310 | return nil | 318 | return nil |
2220 | 311 | } | 319 | } |
2221 | @@ -374,29 +382,6 @@ | |||
2222 | 374 | } | 382 | } |
2223 | 375 | } | 383 | } |
2224 | 376 | 384 | ||
2225 | 377 | // handleConnState deals with connectivity events | ||
2226 | 378 | func (client *PushClient) handleConnState(hasConnectivity bool) { | ||
2227 | 379 | client.log.Debugf("handleConnState: %v", hasConnectivity) | ||
2228 | 380 | if client.hasConnectivity == hasConnectivity { | ||
2229 | 381 | // nothing to do! | ||
2230 | 382 | return | ||
2231 | 383 | } | ||
2232 | 384 | client.hasConnectivity = hasConnectivity | ||
2233 | 385 | client.session.Close() | ||
2234 | 386 | if hasConnectivity { | ||
2235 | 387 | client.session.AutoRedial(client.sessionConnectedCh) | ||
2236 | 388 | } | ||
2237 | 389 | } | ||
2238 | 390 | |||
2239 | 391 | // handleErr deals with the session erroring out of its loop | ||
2240 | 392 | func (client *PushClient) handleErr(err error) { | ||
2241 | 393 | // if we're not connected, we don't really care | ||
2242 | 394 | client.log.Errorf("session exited: %s", err) | ||
2243 | 395 | if client.hasConnectivity { | ||
2244 | 396 | client.session.AutoRedial(client.sessionConnectedCh) | ||
2245 | 397 | } | ||
2246 | 398 | } | ||
2247 | 399 | |||
2248 | 400 | // filterBroadcastNotification finds out if the notification is about an actual | 385 | // filterBroadcastNotification finds out if the notification is about an actual |
2249 | 401 | // upgrade for the device. It expects msg.Decoded entries to look | 386 | // upgrade for the device. It expects msg.Decoded entries to look |
2250 | 402 | // like: | 387 | // like: |
2251 | @@ -467,28 +452,18 @@ | |||
2252 | 467 | return nil | 452 | return nil |
2253 | 468 | } | 453 | } |
2254 | 469 | 454 | ||
2255 | 470 | // handleAccountsChange deals with the user adding or removing (or | ||
2256 | 471 | // changing) the u1 account used to auth | ||
2257 | 472 | func (client *PushClient) handleAccountsChange() { | ||
2258 | 473 | client.log.Infof("U1 account changed; restarting session") | ||
2259 | 474 | client.session.ClearCookie() | ||
2260 | 475 | client.session.Close() | ||
2261 | 476 | } | ||
2262 | 477 | |||
2263 | 478 | // doLoop connects events with their handlers | 455 | // doLoop connects events with their handlers |
2265 | 479 | func (client *PushClient) doLoop(connhandler func(bool), bcasthandler func(*session.BroadcastNotification) error, ucasthandler func(session.AddressedNotification) error, errhandler func(error), unregisterhandler func(*click.AppId), accountshandler func()) { | 456 | func (client *PushClient) doLoop(connhandler func(bool), bcasthandler func(*session.BroadcastNotification) error, ucasthandler func(session.AddressedNotification) error, unregisterhandler func(*click.AppId), accountshandler func()) { |
2266 | 480 | for { | 457 | for { |
2267 | 481 | select { | 458 | select { |
2268 | 482 | case <-client.accountsCh: | 459 | case <-client.accountsCh: |
2269 | 483 | accountshandler() | 460 | accountshandler() |
2270 | 484 | case state := <-client.connCh: | 461 | case state := <-client.connCh: |
2271 | 485 | connhandler(state) | 462 | connhandler(state) |
2273 | 486 | case bcast := <-client.session.BroadcastCh: | 463 | case bcast := <-client.broadcastCh: |
2274 | 487 | bcasthandler(bcast) | 464 | bcasthandler(bcast) |
2276 | 488 | case aucast := <-client.session.NotificationsCh: | 465 | case aucast := <-client.notificationsCh: |
2277 | 489 | ucasthandler(aucast) | 466 | ucasthandler(aucast) |
2278 | 490 | case err := <-client.session.ErrCh: | ||
2279 | 491 | errhandler(err) | ||
2280 | 492 | case count := <-client.sessionConnectedCh: | 467 | case count := <-client.sessionConnectedCh: |
2281 | 493 | client.log.Debugf("session connected after %d attempts", count) | 468 | client.log.Debugf("session connected after %d attempts", count) |
2282 | 494 | case app := <-client.unregisterCh: | 469 | case app := <-client.unregisterCh: |
2283 | @@ -510,12 +485,11 @@ | |||
2284 | 510 | 485 | ||
2285 | 511 | // Loop calls doLoop with the "real" handlers | 486 | // Loop calls doLoop with the "real" handlers |
2286 | 512 | func (client *PushClient) Loop() { | 487 | func (client *PushClient) Loop() { |
2288 | 513 | client.doLoop(client.handleConnState, | 488 | client.doLoop(client.session.HasConnectivity, |
2289 | 514 | client.handleBroadcastNotification, | 489 | client.handleBroadcastNotification, |
2290 | 515 | client.handleUnicastNotification, | 490 | client.handleUnicastNotification, |
2291 | 516 | client.handleErr, | ||
2292 | 517 | client.handleUnregister, | 491 | client.handleUnregister, |
2294 | 518 | client.handleAccountsChange, | 492 | client.session.ResetCookie, |
2295 | 519 | ) | 493 | ) |
2296 | 520 | } | 494 | } |
2297 | 521 | 495 | ||
2298 | 522 | 496 | ||
2299 | === modified file 'client/client_test.go' | |||
2300 | --- client/client_test.go 2015-01-22 17:34:18 +0000 | |||
2301 | +++ client/client_test.go 2015-03-26 16:42:21 +0000 | |||
2302 | @@ -27,9 +27,11 @@ | |||
2303 | 27 | "os" | 27 | "os" |
2304 | 28 | "path/filepath" | 28 | "path/filepath" |
2305 | 29 | "reflect" | 29 | "reflect" |
2306 | 30 | //"runtime" | ||
2307 | 30 | "testing" | 31 | "testing" |
2308 | 31 | "time" | 32 | "time" |
2309 | 32 | 33 | ||
2310 | 34 | "launchpad.net/go-dbus/v1" | ||
2311 | 33 | . "launchpad.net/gocheck" | 35 | . "launchpad.net/gocheck" |
2312 | 34 | 36 | ||
2313 | 35 | "launchpad.net/ubuntu-push/accounts" | 37 | "launchpad.net/ubuntu-push/accounts" |
2314 | @@ -41,7 +43,6 @@ | |||
2315 | 41 | clickhelp "launchpad.net/ubuntu-push/click/testing" | 43 | clickhelp "launchpad.net/ubuntu-push/click/testing" |
2316 | 42 | "launchpad.net/ubuntu-push/client/service" | 44 | "launchpad.net/ubuntu-push/client/service" |
2317 | 43 | "launchpad.net/ubuntu-push/client/session" | 45 | "launchpad.net/ubuntu-push/client/session" |
2318 | 44 | "launchpad.net/ubuntu-push/client/session/seenstate" | ||
2319 | 45 | "launchpad.net/ubuntu-push/config" | 46 | "launchpad.net/ubuntu-push/config" |
2320 | 46 | "launchpad.net/ubuntu-push/identifier" | 47 | "launchpad.net/ubuntu-push/identifier" |
2321 | 47 | idtesting "launchpad.net/ubuntu-push/identifier/testing" | 48 | idtesting "launchpad.net/ubuntu-push/identifier/testing" |
2322 | @@ -203,6 +204,10 @@ | |||
2323 | 203 | cs.writeTestConfig(nil) | 204 | cs.writeTestConfig(nil) |
2324 | 204 | } | 205 | } |
2325 | 205 | 206 | ||
2326 | 207 | func (cs *clientSuite) TearDownTest(c *C) { | ||
2327 | 208 | //helpers.DumpGoroutines() | ||
2328 | 209 | } | ||
2329 | 210 | |||
2330 | 206 | type sqlientSuite struct{ clientSuite } | 211 | type sqlientSuite struct{ clientSuite } |
2331 | 207 | 212 | ||
2332 | 208 | func (s *sqlientSuite) SetUpSuite(c *C) { | 213 | func (s *sqlientSuite) SetUpSuite(c *C) { |
2333 | @@ -421,6 +426,8 @@ | |||
2334 | 421 | AuthGetter: func(string) string { return "" }, | 426 | AuthGetter: func(string) string { return "" }, |
2335 | 422 | AuthURL: "xyzzy://", | 427 | AuthURL: "xyzzy://", |
2336 | 423 | AddresseeChecker: cli, | 428 | AddresseeChecker: cli, |
2337 | 429 | BroadcastCh: make(chan *session.BroadcastNotification), | ||
2338 | 430 | NotificationsCh: make(chan session.AddressedNotification), | ||
2339 | 424 | } | 431 | } |
2340 | 425 | // sanity check that we are looking at all fields | 432 | // sanity check that we are looking at all fields |
2341 | 426 | vExpected := reflect.ValueOf(expected) | 433 | vExpected := reflect.ValueOf(expected) |
2342 | @@ -434,6 +441,11 @@ | |||
2343 | 434 | conf := cli.deriveSessionConfig(info) | 441 | conf := cli.deriveSessionConfig(info) |
2344 | 435 | // compare authGetter by string | 442 | // compare authGetter by string |
2345 | 436 | c.Check(fmt.Sprintf("%#v", conf.AuthGetter), Equals, fmt.Sprintf("%#v", cli.getAuthorization)) | 443 | c.Check(fmt.Sprintf("%#v", conf.AuthGetter), Equals, fmt.Sprintf("%#v", cli.getAuthorization)) |
2346 | 444 | // channels are ok as long as non-nil | ||
2347 | 445 | conf.BroadcastCh = nil | ||
2348 | 446 | conf.NotificationsCh = nil | ||
2349 | 447 | expected.BroadcastCh = nil | ||
2350 | 448 | expected.NotificationsCh = nil | ||
2351 | 437 | // and set it to nil | 449 | // and set it to nil |
2352 | 438 | conf.AuthGetter = nil | 450 | conf.AuthGetter = nil |
2353 | 439 | expected.AuthGetter = nil | 451 | expected.AuthGetter = nil |
2354 | @@ -515,10 +527,18 @@ | |||
2355 | 515 | /***************************************************************** | 527 | /***************************************************************** |
2356 | 516 | derivePollerSetup tests | 528 | derivePollerSetup tests |
2357 | 517 | ******************************************************************/ | 529 | ******************************************************************/ |
2358 | 530 | type derivePollerSession struct{} | ||
2359 | 531 | |||
2360 | 532 | func (s *derivePollerSession) ResetCookie() {} | ||
2361 | 533 | func (s *derivePollerSession) State() session.ClientSessionState { return session.Unknown } | ||
2362 | 534 | func (s *derivePollerSession) HasConnectivity(bool) {} | ||
2363 | 535 | func (s *derivePollerSession) KeepConnection() error { return nil } | ||
2364 | 536 | func (s *derivePollerSession) StopKeepConnection() {} | ||
2365 | 537 | |||
2366 | 518 | func (cs *clientSuite) TestDerivePollerSetup(c *C) { | 538 | func (cs *clientSuite) TestDerivePollerSetup(c *C) { |
2367 | 519 | cs.writeTestConfig(map[string]interface{}{}) | 539 | cs.writeTestConfig(map[string]interface{}{}) |
2368 | 520 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 540 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
2370 | 521 | cli.session = new(session.ClientSession) | 541 | cli.session = new(derivePollerSession) |
2371 | 522 | err := cli.configure() | 542 | err := cli.configure() |
2372 | 523 | c.Assert(err, IsNil) | 543 | c.Assert(err, IsNil) |
2373 | 524 | expected := &poller.PollerSetup{ | 544 | expected := &poller.PollerSetup{ |
2374 | @@ -647,11 +667,19 @@ | |||
2375 | 647 | // testing endpoints | 667 | // testing endpoints |
2376 | 648 | cCond := condition.Fail2Work(7) | 668 | cCond := condition.Fail2Work(7) |
2377 | 649 | cEndp := testibus.NewTestingEndpoint(cCond, condition.Work(true), | 669 | cEndp := testibus.NewTestingEndpoint(cCond, condition.Work(true), |
2379 | 650 | uint32(networkmanager.ConnectedGlobal), | 670 | uint32(networkmanager.Connecting), |
2380 | 671 | dbus.ObjectPath("hello"), | ||
2381 | 672 | uint32(networkmanager.Connecting), | ||
2382 | 673 | dbus.ObjectPath("hello"), | ||
2383 | 651 | ) | 674 | ) |
2384 | 652 | siCond := condition.Fail2Work(2) | 675 | siCond := condition.Fail2Work(2) |
2385 | 653 | siEndp := testibus.NewMultiValuedTestingEndpoint(siCond, condition.Work(true), []interface{}{int32(101), "mako", "daily", "Unknown", map[string]string{}}) | 676 | siEndp := testibus.NewMultiValuedTestingEndpoint(siCond, condition.Work(true), []interface{}{int32(101), "mako", "daily", "Unknown", map[string]string{}}) |
2387 | 654 | testibus.SetWatchTicker(cEndp, make(chan bool)) | 677 | tickerCh := make(chan []interface{}) |
2388 | 678 | nopTickerCh := make(chan []interface{}) | ||
2389 | 679 | testibus.SetWatchSource(cEndp, "StateChanged", tickerCh) | ||
2390 | 680 | testibus.SetWatchSource(cEndp, "PropertiesChanged", nopTickerCh) | ||
2391 | 681 | defer close(tickerCh) | ||
2392 | 682 | defer close(nopTickerCh) | ||
2393 | 655 | // ok, create the thing | 683 | // ok, create the thing |
2394 | 656 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 684 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
2395 | 657 | cli.log = cs.log | 685 | cli.log = cs.log |
2396 | @@ -667,6 +695,7 @@ | |||
2397 | 667 | c.Assert(cli.takeTheBus(), IsNil) | 695 | c.Assert(cli.takeTheBus(), IsNil) |
2398 | 668 | 696 | ||
2399 | 669 | c.Check(takeNextBool(cli.connCh), Equals, false) | 697 | c.Check(takeNextBool(cli.connCh), Equals, false) |
2400 | 698 | tickerCh <- []interface{}{uint32(networkmanager.ConnectedGlobal)} | ||
2401 | 670 | c.Check(takeNextBool(cli.connCh), Equals, true) | 699 | c.Check(takeNextBool(cli.connCh), Equals, true) |
2402 | 671 | // the connectivity endpoint retried until connected | 700 | // the connectivity endpoint retried until connected |
2403 | 672 | c.Check(cCond.OK(), Equals, true) | 701 | c.Check(cCond.OK(), Equals, true) |
2404 | @@ -690,21 +719,6 @@ | |||
2405 | 690 | } | 719 | } |
2406 | 691 | 720 | ||
2407 | 692 | /***************************************************************** | 721 | /***************************************************************** |
2408 | 693 | handleErr tests | ||
2409 | 694 | ******************************************************************/ | ||
2410 | 695 | |||
2411 | 696 | func (cs *clientSuite) TestHandleErr(c *C) { | ||
2412 | 697 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | ||
2413 | 698 | cli.log = cs.log | ||
2414 | 699 | cli.systemImageInfo = siInfoRes | ||
2415 | 700 | c.Assert(cli.initSessionAndPoller(), IsNil) | ||
2416 | 701 | cs.log.ResetCapture() | ||
2417 | 702 | cli.hasConnectivity = true | ||
2418 | 703 | cli.handleErr(errors.New("bananas")) | ||
2419 | 704 | c.Check(cs.log.Captured(), Matches, ".*session exited.*bananas\n") | ||
2420 | 705 | } | ||
2421 | 706 | |||
2422 | 707 | /***************************************************************** | ||
2423 | 708 | seenStateFactory tests | 722 | seenStateFactory tests |
2424 | 709 | ******************************************************************/ | 723 | ******************************************************************/ |
2425 | 710 | 724 | ||
2426 | @@ -712,6 +726,7 @@ | |||
2427 | 712 | cli := NewPushClient(cs.configPath, "") | 726 | cli := NewPushClient(cs.configPath, "") |
2428 | 713 | ln, err := cli.seenStateFactory() | 727 | ln, err := cli.seenStateFactory() |
2429 | 714 | c.Assert(err, IsNil) | 728 | c.Assert(err, IsNil) |
2430 | 729 | defer ln.Close() | ||
2431 | 715 | c.Check(fmt.Sprintf("%T", ln), Equals, "*seenstate.memSeenState") | 730 | c.Check(fmt.Sprintf("%T", ln), Equals, "*seenstate.memSeenState") |
2432 | 716 | } | 731 | } |
2433 | 717 | 732 | ||
2434 | @@ -719,63 +734,11 @@ | |||
2435 | 719 | cli := NewPushClient(cs.configPath, ":memory:") | 734 | cli := NewPushClient(cs.configPath, ":memory:") |
2436 | 720 | ln, err := cli.seenStateFactory() | 735 | ln, err := cli.seenStateFactory() |
2437 | 721 | c.Assert(err, IsNil) | 736 | c.Assert(err, IsNil) |
2438 | 737 | defer ln.Close() | ||
2439 | 722 | c.Check(fmt.Sprintf("%T", ln), Equals, "*seenstate.sqliteSeenState") | 738 | c.Check(fmt.Sprintf("%T", ln), Equals, "*seenstate.sqliteSeenState") |
2440 | 723 | } | 739 | } |
2441 | 724 | 740 | ||
2442 | 725 | /***************************************************************** | 741 | /***************************************************************** |
2443 | 726 | handleConnState tests | ||
2444 | 727 | ******************************************************************/ | ||
2445 | 728 | |||
2446 | 729 | func (cs *clientSuite) TestHandleConnStateD2C(c *C) { | ||
2447 | 730 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | ||
2448 | 731 | cli.log = cs.log | ||
2449 | 732 | cli.systemImageInfo = siInfoRes | ||
2450 | 733 | c.Assert(cli.initSessionAndPoller(), IsNil) | ||
2451 | 734 | |||
2452 | 735 | c.Assert(cli.hasConnectivity, Equals, false) | ||
2453 | 736 | cli.handleConnState(true) | ||
2454 | 737 | c.Check(cli.hasConnectivity, Equals, true) | ||
2455 | 738 | c.Assert(cli.session, NotNil) | ||
2456 | 739 | } | ||
2457 | 740 | |||
2458 | 741 | func (cs *clientSuite) TestHandleConnStateSame(c *C) { | ||
2459 | 742 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | ||
2460 | 743 | cli.log = cs.log | ||
2461 | 744 | // here we want to check that we don't do anything | ||
2462 | 745 | c.Assert(cli.session, IsNil) | ||
2463 | 746 | c.Assert(cli.hasConnectivity, Equals, false) | ||
2464 | 747 | cli.handleConnState(false) | ||
2465 | 748 | c.Check(cli.session, IsNil) | ||
2466 | 749 | |||
2467 | 750 | cli.hasConnectivity = true | ||
2468 | 751 | cli.handleConnState(true) | ||
2469 | 752 | c.Check(cli.session, IsNil) | ||
2470 | 753 | } | ||
2471 | 754 | |||
2472 | 755 | func (cs *clientSuite) TestHandleConnStateC2D(c *C) { | ||
2473 | 756 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | ||
2474 | 757 | cli.log = cs.log | ||
2475 | 758 | cli.session, _ = session.NewSession(cli.config.Addr, cli.deriveSessionConfig(nil), cli.deviceId, seenstate.NewSeenState, cs.log) | ||
2476 | 759 | cli.session.Dial() | ||
2477 | 760 | cli.hasConnectivity = true | ||
2478 | 761 | |||
2479 | 762 | // cli.session.State() will be "Error" here, for now at least | ||
2480 | 763 | c.Check(cli.session.State(), Not(Equals), session.Disconnected) | ||
2481 | 764 | cli.handleConnState(false) | ||
2482 | 765 | c.Check(cli.session.State(), Equals, session.Disconnected) | ||
2483 | 766 | } | ||
2484 | 767 | |||
2485 | 768 | func (cs *clientSuite) TestHandleConnStateC2DPending(c *C) { | ||
2486 | 769 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | ||
2487 | 770 | cli.log = cs.log | ||
2488 | 771 | cli.session, _ = session.NewSession(cli.config.Addr, cli.deriveSessionConfig(nil), cli.deviceId, seenstate.NewSeenState, cs.log) | ||
2489 | 772 | cli.hasConnectivity = true | ||
2490 | 773 | |||
2491 | 774 | cli.handleConnState(false) | ||
2492 | 775 | c.Check(cli.session.State(), Equals, session.Disconnected) | ||
2493 | 776 | } | ||
2494 | 777 | |||
2495 | 778 | /***************************************************************** | ||
2496 | 779 | filterBroadcastNotification tests | 742 | filterBroadcastNotification tests |
2497 | 780 | ******************************************************************/ | 743 | ******************************************************************/ |
2498 | 781 | 744 | ||
2499 | @@ -993,7 +956,6 @@ | |||
2500 | 993 | var nopConn = func(bool) {} | 956 | var nopConn = func(bool) {} |
2501 | 994 | var nopBcast = func(*session.BroadcastNotification) error { return nil } | 957 | var nopBcast = func(*session.BroadcastNotification) error { return nil } |
2502 | 995 | var nopUcast = func(session.AddressedNotification) error { return nil } | 958 | var nopUcast = func(session.AddressedNotification) error { return nil } |
2503 | 996 | var nopError = func(error) {} | ||
2504 | 997 | var nopUnregister = func(*click.AppId) {} | 959 | var nopUnregister = func(*click.AppId) {} |
2505 | 998 | var nopAcct = func() {} | 960 | var nopAcct = func() {} |
2506 | 999 | 961 | ||
2507 | @@ -1006,7 +968,7 @@ | |||
2508 | 1006 | c.Assert(cli.initSessionAndPoller(), IsNil) | 968 | c.Assert(cli.initSessionAndPoller(), IsNil) |
2509 | 1007 | 969 | ||
2510 | 1008 | ch := make(chan bool, 1) | 970 | ch := make(chan bool, 1) |
2512 | 1009 | go cli.doLoop(func(bool) { ch <- true }, nopBcast, nopUcast, nopError, nopUnregister, nopAcct) | 971 | go cli.doLoop(func(bool) { ch <- true }, nopBcast, nopUcast, nopUnregister, nopAcct) |
2513 | 1010 | c.Check(takeNextBool(ch), Equals, true) | 972 | c.Check(takeNextBool(ch), Equals, true) |
2514 | 1011 | } | 973 | } |
2515 | 1012 | 974 | ||
2516 | @@ -1015,11 +977,11 @@ | |||
2517 | 1015 | cli.log = cs.log | 977 | cli.log = cs.log |
2518 | 1016 | cli.systemImageInfo = siInfoRes | 978 | cli.systemImageInfo = siInfoRes |
2519 | 1017 | c.Assert(cli.initSessionAndPoller(), IsNil) | 979 | c.Assert(cli.initSessionAndPoller(), IsNil) |
2522 | 1018 | cli.session.BroadcastCh = make(chan *session.BroadcastNotification, 1) | 980 | cli.broadcastCh = make(chan *session.BroadcastNotification, 1) |
2523 | 1019 | cli.session.BroadcastCh <- &session.BroadcastNotification{} | 981 | cli.broadcastCh <- &session.BroadcastNotification{} |
2524 | 1020 | 982 | ||
2525 | 1021 | ch := make(chan bool, 1) | 983 | ch := make(chan bool, 1) |
2527 | 1022 | go cli.doLoop(nopConn, func(_ *session.BroadcastNotification) error { ch <- true; return nil }, nopUcast, nopError, nopUnregister, nopAcct) | 984 | go cli.doLoop(nopConn, func(_ *session.BroadcastNotification) error { ch <- true; return nil }, nopUcast, nopUnregister, nopAcct) |
2528 | 1023 | c.Check(takeNextBool(ch), Equals, true) | 985 | c.Check(takeNextBool(ch), Equals, true) |
2529 | 1024 | } | 986 | } |
2530 | 1025 | 987 | ||
2531 | @@ -1028,24 +990,11 @@ | |||
2532 | 1028 | cli.log = cs.log | 990 | cli.log = cs.log |
2533 | 1029 | cli.systemImageInfo = siInfoRes | 991 | cli.systemImageInfo = siInfoRes |
2534 | 1030 | c.Assert(cli.initSessionAndPoller(), IsNil) | 992 | c.Assert(cli.initSessionAndPoller(), IsNil) |
2553 | 1031 | cli.session.NotificationsCh = make(chan session.AddressedNotification, 1) | 993 | cli.notificationsCh = make(chan session.AddressedNotification, 1) |
2554 | 1032 | cli.session.NotificationsCh <- session.AddressedNotification{} | 994 | cli.notificationsCh <- session.AddressedNotification{} |
2555 | 1033 | 995 | ||
2556 | 1034 | ch := make(chan bool, 1) | 996 | ch := make(chan bool, 1) |
2557 | 1035 | go cli.doLoop(nopConn, nopBcast, func(session.AddressedNotification) error { ch <- true; return nil }, nopError, nopUnregister, nopAcct) | 997 | go cli.doLoop(nopConn, nopBcast, func(session.AddressedNotification) error { ch <- true; return nil }, nopUnregister, nopAcct) |
2540 | 1036 | c.Check(takeNextBool(ch), Equals, true) | ||
2541 | 1037 | } | ||
2542 | 1038 | |||
2543 | 1039 | func (cs *clientSuite) TestDoLoopErr(c *C) { | ||
2544 | 1040 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | ||
2545 | 1041 | cli.log = cs.log | ||
2546 | 1042 | cli.systemImageInfo = siInfoRes | ||
2547 | 1043 | c.Assert(cli.initSessionAndPoller(), IsNil) | ||
2548 | 1044 | cli.session.ErrCh = make(chan error, 1) | ||
2549 | 1045 | cli.session.ErrCh <- nil | ||
2550 | 1046 | |||
2551 | 1047 | ch := make(chan bool, 1) | ||
2552 | 1048 | go cli.doLoop(nopConn, nopBcast, nopUcast, func(error) { ch <- true }, nopUnregister, nopAcct) | ||
2558 | 1049 | c.Check(takeNextBool(ch), Equals, true) | 998 | c.Check(takeNextBool(ch), Equals, true) |
2559 | 1050 | } | 999 | } |
2560 | 1051 | 1000 | ||
2561 | @@ -1058,7 +1007,7 @@ | |||
2562 | 1058 | cli.unregisterCh <- app1 | 1007 | cli.unregisterCh <- app1 |
2563 | 1059 | 1008 | ||
2564 | 1060 | ch := make(chan bool, 1) | 1009 | ch := make(chan bool, 1) |
2566 | 1061 | go cli.doLoop(nopConn, nopBcast, nopUcast, nopError, func(app *click.AppId) { c.Check(app.Original(), Equals, appId1); ch <- true }, nopAcct) | 1010 | go cli.doLoop(nopConn, nopBcast, nopUcast, func(app *click.AppId) { c.Check(app.Original(), Equals, appId1); ch <- true }, nopAcct) |
2567 | 1062 | c.Check(takeNextBool(ch), Equals, true) | 1011 | c.Check(takeNextBool(ch), Equals, true) |
2568 | 1063 | } | 1012 | } |
2569 | 1064 | 1013 | ||
2570 | @@ -1072,7 +1021,7 @@ | |||
2571 | 1072 | cli.accountsCh = acctCh | 1021 | cli.accountsCh = acctCh |
2572 | 1073 | 1022 | ||
2573 | 1074 | ch := make(chan bool, 1) | 1023 | ch := make(chan bool, 1) |
2575 | 1075 | go cli.doLoop(nopConn, nopBcast, nopUcast, nopError, nopUnregister, func() { ch <- true }) | 1024 | go cli.doLoop(nopConn, nopBcast, nopUcast, nopUnregister, func() { ch <- true }) |
2576 | 1076 | c.Check(takeNextBool(ch), Equals, true) | 1025 | c.Check(takeNextBool(ch), Equals, true) |
2577 | 1077 | } | 1026 | } |
2578 | 1078 | 1027 | ||
2579 | @@ -1107,6 +1056,20 @@ | |||
2580 | 1107 | Loop() tests | 1056 | Loop() tests |
2581 | 1108 | ******************************************************************/ | 1057 | ******************************************************************/ |
2582 | 1109 | 1058 | ||
2583 | 1059 | type loopSession struct{ hasConn bool } | ||
2584 | 1060 | |||
2585 | 1061 | func (s *loopSession) ResetCookie() {} | ||
2586 | 1062 | func (s *loopSession) State() session.ClientSessionState { | ||
2587 | 1063 | if s.hasConn { | ||
2588 | 1064 | return session.Connected | ||
2589 | 1065 | } else { | ||
2590 | 1066 | return session.Disconnected | ||
2591 | 1067 | } | ||
2592 | 1068 | } | ||
2593 | 1069 | func (s *loopSession) HasConnectivity(hasConn bool) { s.hasConn = hasConn } | ||
2594 | 1070 | func (s *loopSession) KeepConnection() error { return nil } | ||
2595 | 1071 | func (s *loopSession) StopKeepConnection() {} | ||
2596 | 1072 | |||
2597 | 1110 | func (cs *clientSuite) TestLoop(c *C) { | 1073 | func (cs *clientSuite) TestLoop(c *C) { |
2598 | 1111 | cli := NewPushClient(cs.configPath, cs.leveldbPath) | 1074 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
2599 | 1112 | cli.connCh = make(chan bool) | 1075 | cli.connCh = make(chan bool) |
2600 | @@ -1121,8 +1084,7 @@ | |||
2601 | 1121 | 1084 | ||
2602 | 1122 | c.Assert(cli.initSessionAndPoller(), IsNil) | 1085 | c.Assert(cli.initSessionAndPoller(), IsNil) |
2603 | 1123 | 1086 | ||
2606 | 1124 | cli.session.BroadcastCh = make(chan *session.BroadcastNotification) | 1087 | cli.broadcastCh = make(chan *session.BroadcastNotification) |
2605 | 1125 | cli.session.ErrCh = make(chan error) | ||
2607 | 1126 | 1088 | ||
2608 | 1127 | // we use tick() to make sure things have been through the | 1089 | // we use tick() to make sure things have been through the |
2609 | 1128 | // event loop at least once before looking at things; | 1090 | // event loop at least once before looking at things; |
2610 | @@ -1130,6 +1092,10 @@ | |||
2611 | 1130 | // at and the loop itself. | 1092 | // at and the loop itself. |
2612 | 1131 | tick := func() { cli.sessionConnectedCh <- 42 } | 1093 | tick := func() { cli.sessionConnectedCh <- 42 } |
2613 | 1132 | 1094 | ||
2614 | 1095 | c.Assert(cli.session, NotNil) | ||
2615 | 1096 | cli.session.StopKeepConnection() | ||
2616 | 1097 | cli.session = &loopSession{} | ||
2617 | 1098 | |||
2618 | 1133 | go cli.Loop() | 1099 | go cli.Loop() |
2619 | 1134 | 1100 | ||
2620 | 1135 | // sessionConnectedCh to nothing in particular, but it'll help sync this test | 1101 | // sessionConnectedCh to nothing in particular, but it'll help sync this test |
2621 | @@ -1139,24 +1105,19 @@ | |||
2622 | 1139 | 1105 | ||
2623 | 1140 | // loop() should have connected: | 1106 | // loop() should have connected: |
2624 | 1141 | // * connCh to the connectivity checker | 1107 | // * connCh to the connectivity checker |
2626 | 1142 | c.Check(cli.hasConnectivity, Equals, false) | 1108 | c.Check(cli.session.State(), Equals, session.Disconnected) |
2627 | 1143 | cli.connCh <- true | 1109 | cli.connCh <- true |
2628 | 1144 | tick() | 1110 | tick() |
2630 | 1145 | c.Check(cli.hasConnectivity, Equals, true) | 1111 | c.Check(cli.session.State(), Equals, session.Connected) |
2631 | 1146 | cli.connCh <- false | 1112 | cli.connCh <- false |
2632 | 1147 | tick() | 1113 | tick() |
2634 | 1148 | c.Check(cli.hasConnectivity, Equals, false) | 1114 | c.Check(cli.session.State(), Equals, session.Disconnected) |
2635 | 1149 | 1115 | ||
2636 | 1150 | // * session.BroadcastCh to the notifications handler | 1116 | // * session.BroadcastCh to the notifications handler |
2637 | 1151 | c.Check(d.bcastCount, Equals, 0) | 1117 | c.Check(d.bcastCount, Equals, 0) |
2639 | 1152 | cli.session.BroadcastCh <- positiveBroadcastNotification | 1118 | cli.broadcastCh <- positiveBroadcastNotification |
2640 | 1153 | tick() | 1119 | tick() |
2641 | 1154 | c.Check(d.bcastCount, Equals, 1) | 1120 | c.Check(d.bcastCount, Equals, 1) |
2642 | 1155 | |||
2643 | 1156 | // * session.ErrCh to the error handler | ||
2644 | 1157 | cli.session.ErrCh <- nil | ||
2645 | 1158 | tick() | ||
2646 | 1159 | c.Check(cs.log.Captured(), Matches, "(?ms).*session exited.*") | ||
2647 | 1160 | } | 1121 | } |
2648 | 1161 | 1122 | ||
2649 | 1162 | /***************************************************************** | 1123 | /***************************************************************** |
2650 | 1163 | 1124 | ||
2651 | === modified file 'client/service/postal.go' | |||
2652 | --- client/service/postal.go 2015-01-22 17:34:18 +0000 | |||
2653 | +++ client/service/postal.go 2015-03-26 16:42:21 +0000 | |||
2654 | @@ -24,6 +24,7 @@ | |||
2655 | 24 | "code.google.com/p/go-uuid/uuid" | 24 | "code.google.com/p/go-uuid/uuid" |
2656 | 25 | 25 | ||
2657 | 26 | "launchpad.net/ubuntu-push/bus" | 26 | "launchpad.net/ubuntu-push/bus" |
2658 | 27 | "launchpad.net/ubuntu-push/bus/accounts" | ||
2659 | 27 | "launchpad.net/ubuntu-push/bus/emblemcounter" | 28 | "launchpad.net/ubuntu-push/bus/emblemcounter" |
2660 | 28 | "launchpad.net/ubuntu-push/bus/haptic" | 29 | "launchpad.net/ubuntu-push/bus/haptic" |
2661 | 29 | "launchpad.net/ubuntu-push/bus/notifications" | 30 | "launchpad.net/ubuntu-push/bus/notifications" |
2662 | @@ -75,6 +76,7 @@ | |||
2663 | 75 | // the endpoints are only exposed for testing from client | 76 | // the endpoints are only exposed for testing from client |
2664 | 76 | // XXX: uncouple some more so this isn't necessary | 77 | // XXX: uncouple some more so this isn't necessary |
2665 | 77 | EmblemCounterEndp bus.Endpoint | 78 | EmblemCounterEndp bus.Endpoint |
2666 | 79 | AccountsEndp bus.Endpoint | ||
2667 | 78 | HapticEndp bus.Endpoint | 80 | HapticEndp bus.Endpoint |
2668 | 79 | NotificationsEndp bus.Endpoint | 81 | NotificationsEndp bus.Endpoint |
2669 | 80 | UnityGreeterEndp bus.Endpoint | 82 | UnityGreeterEndp bus.Endpoint |
2670 | @@ -82,6 +84,7 @@ | |||
2671 | 82 | // presenters: | 84 | // presenters: |
2672 | 83 | Presenters []Presenter | 85 | Presenters []Presenter |
2673 | 84 | emblemCounter *emblemcounter.EmblemCounter | 86 | emblemCounter *emblemcounter.EmblemCounter |
2674 | 87 | accounts accounts.Accounts | ||
2675 | 85 | haptic *haptic.Haptic | 88 | haptic *haptic.Haptic |
2676 | 86 | notifications *notifications.RawNotifications | 89 | notifications *notifications.RawNotifications |
2677 | 87 | sound *sounds.Sound | 90 | sound *sounds.Sound |
2678 | @@ -117,6 +120,7 @@ | |||
2679 | 117 | svc.fallbackSound = setup.FallbackSound | 120 | svc.fallbackSound = setup.FallbackSound |
2680 | 118 | svc.NotificationsEndp = bus.SessionBus.Endpoint(notifications.BusAddress, log) | 121 | svc.NotificationsEndp = bus.SessionBus.Endpoint(notifications.BusAddress, log) |
2681 | 119 | svc.EmblemCounterEndp = bus.SessionBus.Endpoint(emblemcounter.BusAddress, log) | 122 | svc.EmblemCounterEndp = bus.SessionBus.Endpoint(emblemcounter.BusAddress, log) |
2682 | 123 | svc.AccountsEndp = bus.SystemBus.Endpoint(accounts.BusAddress, log) | ||
2683 | 120 | svc.HapticEndp = bus.SessionBus.Endpoint(haptic.BusAddress, log) | 124 | svc.HapticEndp = bus.SessionBus.Endpoint(haptic.BusAddress, log) |
2684 | 121 | svc.UnityGreeterEndp = bus.SessionBus.Endpoint(unitygreeter.BusAddress, log) | 125 | svc.UnityGreeterEndp = bus.SessionBus.Endpoint(unitygreeter.BusAddress, log) |
2685 | 122 | svc.WindowStackEndp = bus.SessionBus.Endpoint(windowstack.BusAddress, log) | 126 | svc.WindowStackEndp = bus.SessionBus.Endpoint(windowstack.BusAddress, log) |
2686 | @@ -158,8 +162,13 @@ | |||
2687 | 158 | svc.urlDispatcher = urldispatcher.New(svc.Log) | 162 | svc.urlDispatcher = urldispatcher.New(svc.Log) |
2688 | 159 | svc.notifications = notifications.Raw(svc.NotificationsEndp, svc.Log) | 163 | svc.notifications = notifications.Raw(svc.NotificationsEndp, svc.Log) |
2689 | 160 | svc.emblemCounter = emblemcounter.New(svc.EmblemCounterEndp, svc.Log) | 164 | svc.emblemCounter = emblemcounter.New(svc.EmblemCounterEndp, svc.Log) |
2692 | 161 | svc.haptic = haptic.New(svc.HapticEndp, svc.Log, svc.fallbackVibration) | 165 | svc.accounts = accounts.New(svc.AccountsEndp, svc.Log) |
2693 | 162 | svc.sound = sounds.New(svc.Log, svc.fallbackSound) | 166 | err = svc.accounts.Start() |
2694 | 167 | if err != nil { | ||
2695 | 168 | return err | ||
2696 | 169 | } | ||
2697 | 170 | svc.haptic = haptic.New(svc.HapticEndp, svc.Log, svc.accounts, svc.fallbackVibration) | ||
2698 | 171 | svc.sound = sounds.New(svc.Log, svc.accounts, svc.fallbackSound) | ||
2699 | 163 | svc.messagingMenu = messaging.New(svc.Log) | 172 | svc.messagingMenu = messaging.New(svc.Log) |
2700 | 164 | svc.Presenters = []Presenter{ | 173 | svc.Presenters = []Presenter{ |
2701 | 165 | svc.notifications, | 174 | svc.notifications, |
2702 | @@ -228,6 +237,7 @@ | |||
2703 | 228 | }{ | 237 | }{ |
2704 | 229 | {"notifications", svc.NotificationsEndp}, | 238 | {"notifications", svc.NotificationsEndp}, |
2705 | 230 | {"emblemcounter", svc.EmblemCounterEndp}, | 239 | {"emblemcounter", svc.EmblemCounterEndp}, |
2706 | 240 | {"accounts", svc.AccountsEndp}, | ||
2707 | 231 | {"haptic", svc.HapticEndp}, | 241 | {"haptic", svc.HapticEndp}, |
2708 | 232 | {"unitygreeter", svc.UnityGreeterEndp}, | 242 | {"unitygreeter", svc.UnityGreeterEndp}, |
2709 | 233 | {"windowstack", svc.WindowStackEndp}, | 243 | {"windowstack", svc.WindowStackEndp}, |
2710 | 234 | 244 | ||
2711 | === modified file 'client/service/postal_test.go' | |||
2712 | --- client/service/postal_test.go 2014-09-09 22:54:04 +0000 | |||
2713 | +++ client/service/postal_test.go 2015-03-26 16:42:21 +0000 | |||
2714 | @@ -169,6 +169,8 @@ | |||
2715 | 169 | hapticBus bus.Endpoint | 169 | hapticBus bus.Endpoint |
2716 | 170 | unityGreeterBus bus.Endpoint | 170 | unityGreeterBus bus.Endpoint |
2717 | 171 | winStackBus bus.Endpoint | 171 | winStackBus bus.Endpoint |
2718 | 172 | accountsBus bus.Endpoint | ||
2719 | 173 | accountsCh chan []interface{} | ||
2720 | 172 | fakeLauncher *fakeHelperLauncher | 174 | fakeLauncher *fakeHelperLauncher |
2721 | 173 | getTempDir func(string) (string, error) | 175 | getTempDir func(string) (string, error) |
2722 | 174 | oldIsBlisted func(*click.AppId) bool | 176 | oldIsBlisted func(*click.AppId) bool |
2723 | @@ -194,6 +196,7 @@ | |||
2724 | 194 | ps.bus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true)) | 196 | ps.bus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true)) |
2725 | 195 | ps.notifBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true)) | 197 | ps.notifBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true)) |
2726 | 196 | ps.counterBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true)) | 198 | ps.counterBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true)) |
2727 | 199 | ps.accountsBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), map[string]dbus.Variant{"IncomingMessageVibrate": dbus.Variant{true}}) | ||
2728 | 197 | ps.hapticBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true)) | 200 | ps.hapticBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true)) |
2729 | 198 | ps.unityGreeterBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), false) | 201 | ps.unityGreeterBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), false) |
2730 | 199 | ps.winStackBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), []windowstack.WindowsInfo{}) | 202 | ps.winStackBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), []windowstack.WindowsInfo{}) |
2731 | @@ -206,11 +209,15 @@ | |||
2732 | 206 | tmpDir := filepath.Join(d, pkgName) | 209 | tmpDir := filepath.Join(d, pkgName) |
2733 | 207 | return tmpDir, os.MkdirAll(tmpDir, 0700) | 210 | return tmpDir, os.MkdirAll(tmpDir, 0700) |
2734 | 208 | } | 211 | } |
2735 | 212 | |||
2736 | 213 | ps.accountsCh = make(chan []interface{}) | ||
2737 | 214 | testibus.SetWatchSource(ps.accountsBus, "PropertiesChanged", ps.accountsCh) | ||
2738 | 209 | } | 215 | } |
2739 | 210 | 216 | ||
2740 | 211 | func (ps *postalSuite) TearDownTest(c *C) { | 217 | func (ps *postalSuite) TearDownTest(c *C) { |
2741 | 212 | isBlacklisted = ps.oldIsBlisted | 218 | isBlacklisted = ps.oldIsBlisted |
2742 | 213 | launch_helper.GetTempDir = ps.getTempDir | 219 | launch_helper.GetTempDir = ps.getTempDir |
2743 | 220 | close(ps.accountsCh) | ||
2744 | 214 | } | 221 | } |
2745 | 215 | 222 | ||
2746 | 216 | func (ts *trivialPostalSuite) SetUpTest(c *C) { | 223 | func (ts *trivialPostalSuite) SetUpTest(c *C) { |
2747 | @@ -227,6 +234,7 @@ | |||
2748 | 227 | pst.Bus = ps.bus | 234 | pst.Bus = ps.bus |
2749 | 228 | pst.NotificationsEndp = ps.notifBus | 235 | pst.NotificationsEndp = ps.notifBus |
2750 | 229 | pst.EmblemCounterEndp = ps.counterBus | 236 | pst.EmblemCounterEndp = ps.counterBus |
2751 | 237 | pst.AccountsEndp = ps.accountsBus | ||
2752 | 230 | pst.HapticEndp = ps.hapticBus | 238 | pst.HapticEndp = ps.hapticBus |
2753 | 231 | pst.UnityGreeterEndp = ps.unityGreeterBus | 239 | pst.UnityGreeterEndp = ps.unityGreeterBus |
2754 | 232 | pst.WindowStackEndp = ps.winStackBus | 240 | pst.WindowStackEndp = ps.winStackBus |
2755 | @@ -544,6 +552,7 @@ | |||
2756 | 544 | svc := NewPostalService(ps.cfg, ps.log) | 552 | svc := NewPostalService(ps.cfg, ps.log) |
2757 | 545 | svc.Bus = endp | 553 | svc.Bus = endp |
2758 | 546 | svc.EmblemCounterEndp = endp | 554 | svc.EmblemCounterEndp = endp |
2759 | 555 | svc.AccountsEndp = ps.accountsBus | ||
2760 | 547 | svc.HapticEndp = endp | 556 | svc.HapticEndp = endp |
2761 | 548 | svc.NotificationsEndp = endp | 557 | svc.NotificationsEndp = endp |
2762 | 549 | svc.UnityGreeterEndp = ps.unityGreeterBus | 558 | svc.UnityGreeterEndp = ps.unityGreeterBus |
2763 | @@ -552,6 +561,10 @@ | |||
2764 | 552 | svc.fallbackVibration = &launch_helper.Vibration{Pattern: []uint32{1}} | 561 | svc.fallbackVibration = &launch_helper.Vibration{Pattern: []uint32{1}} |
2765 | 553 | c.Assert(svc.Start(), IsNil) | 562 | c.Assert(svc.Start(), IsNil) |
2766 | 554 | 563 | ||
2767 | 564 | nopTicker := make(chan []interface{}) | ||
2768 | 565 | testibus.SetWatchSource(endp, "ActionInvoked", nopTicker) | ||
2769 | 566 | defer close(nopTicker) | ||
2770 | 567 | |||
2771 | 555 | // Persist is false so we just check the log | 568 | // Persist is false so we just check the log |
2772 | 556 | card := &launch_helper.Card{Icon: "icon-value", Summary: "summary-value", Body: "body-value", Popup: true, Persist: false} | 569 | card := &launch_helper.Card{Icon: "icon-value", Summary: "summary-value", Body: "body-value", Popup: true, Persist: false} |
2773 | 557 | vib := json.RawMessage(`true`) | 570 | vib := json.RawMessage(`true`) |
2774 | @@ -837,6 +850,10 @@ | |||
2775 | 837 | } | 850 | } |
2776 | 838 | 851 | ||
2777 | 839 | func (ps *postalSuite) TestBlacklisted(c *C) { | 852 | func (ps *postalSuite) TestBlacklisted(c *C) { |
2778 | 853 | ps.winStackBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), []windowstack.WindowsInfo{}, | ||
2779 | 854 | []windowstack.WindowsInfo{}, | ||
2780 | 855 | []windowstack.WindowsInfo{}, | ||
2781 | 856 | []windowstack.WindowsInfo{}) | ||
2782 | 840 | svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log)) | 857 | svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log)) |
2783 | 841 | svc.Start() | 858 | svc.Start() |
2784 | 842 | ps.blacklisted = false | 859 | ps.blacklisted = false |
2785 | 843 | 860 | ||
2786 | === modified file 'client/service/service.go' | |||
2787 | --- client/service/service.go 2015-01-22 17:34:18 +0000 | |||
2788 | +++ client/service/service.go 2015-03-26 16:42:21 +0000 | |||
2789 | @@ -140,6 +140,8 @@ | |||
2790 | 140 | case resp.StatusCode >= http.StatusInternalServerError: | 140 | case resp.StatusCode >= http.StatusInternalServerError: |
2791 | 141 | // XXX retry on 503 | 141 | // XXX retry on 503 |
2792 | 142 | return nil, ErrBadServer | 142 | return nil, ErrBadServer |
2793 | 143 | case resp.StatusCode == http.StatusUnauthorized: | ||
2794 | 144 | return nil, ErrBadAuth | ||
2795 | 143 | default: | 145 | default: |
2796 | 144 | return nil, ErrBadRequest | 146 | return nil, ErrBadRequest |
2797 | 145 | } | 147 | } |
2798 | 146 | 148 | ||
2799 | === modified file 'client/service/service_test.go' | |||
2800 | --- client/service/service_test.go 2014-08-06 09:01:59 +0000 | |||
2801 | +++ client/service/service_test.go 2015-03-26 16:42:21 +0000 | |||
2802 | @@ -19,6 +19,7 @@ | |||
2803 | 19 | import ( | 19 | import ( |
2804 | 20 | "encoding/json" | 20 | "encoding/json" |
2805 | 21 | "fmt" | 21 | "fmt" |
2806 | 22 | "io" | ||
2807 | 22 | "net/http" | 23 | "net/http" |
2808 | 23 | "net/http/httptest" | 24 | "net/http/httptest" |
2809 | 24 | "os" | 25 | "os" |
2810 | @@ -179,7 +180,8 @@ | |||
2811 | 179 | func (ss *serviceSuite) TestRegistrationWorks(c *C) { | 180 | func (ss *serviceSuite) TestRegistrationWorks(c *C) { |
2812 | 180 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 181 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
2813 | 181 | buf := make([]byte, 256) | 182 | buf := make([]byte, 256) |
2815 | 182 | n, e := r.Body.Read(buf) | 183 | n := r.ContentLength |
2816 | 184 | _, e := io.ReadFull(r.Body, buf[:n]) | ||
2817 | 183 | c.Assert(e, IsNil) | 185 | c.Assert(e, IsNil) |
2818 | 184 | req := registrationRequest{} | 186 | req := registrationRequest{} |
2819 | 185 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) | 187 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
2820 | @@ -240,6 +242,23 @@ | |||
2821 | 240 | c.Check(err, ErrorMatches, "unable to request registration: .*") | 242 | c.Check(err, ErrorMatches, "unable to request registration: .*") |
2822 | 241 | } | 243 | } |
2823 | 242 | 244 | ||
2824 | 245 | func (ss *serviceSuite) TestManageRegFailsOn401(c *C) { | ||
2825 | 246 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
2826 | 247 | http.Error(w, "Unauthorized", 401) | ||
2827 | 248 | })) | ||
2828 | 249 | defer ts.Close() | ||
2829 | 250 | setup := &PushServiceSetup{ | ||
2830 | 251 | DeviceId: "fake-device-id", | ||
2831 | 252 | RegURL: helpers.ParseURL(ts.URL), | ||
2832 | 253 | AuthGetter: func(string) string { return "tok" }, | ||
2833 | 254 | } | ||
2834 | 255 | svc := NewPushService(setup, ss.log) | ||
2835 | 256 | svc.Bus = ss.bus | ||
2836 | 257 | reg, err := svc.register(aPackageOnBus, []interface{}{anAppId}, nil) | ||
2837 | 258 | c.Check(err, Equals, ErrBadAuth) | ||
2838 | 259 | c.Check(reg, IsNil) | ||
2839 | 260 | } | ||
2840 | 261 | |||
2841 | 243 | func (ss *serviceSuite) TestManageRegFailsOn40x(c *C) { | 262 | func (ss *serviceSuite) TestManageRegFailsOn40x(c *C) { |
2842 | 244 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 263 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
2843 | 245 | http.Error(w, "I'm a teapot", 418) | 264 | http.Error(w, "I'm a teapot", 418) |
2844 | @@ -277,7 +296,8 @@ | |||
2845 | 277 | func (ss *serviceSuite) TestManageRegFailsOnBadJSON(c *C) { | 296 | func (ss *serviceSuite) TestManageRegFailsOnBadJSON(c *C) { |
2846 | 278 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 297 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
2847 | 279 | buf := make([]byte, 256) | 298 | buf := make([]byte, 256) |
2849 | 280 | n, e := r.Body.Read(buf) | 299 | n := r.ContentLength |
2850 | 300 | _, e := io.ReadFull(r.Body, buf[:n]) | ||
2851 | 281 | c.Assert(e, IsNil) | 301 | c.Assert(e, IsNil) |
2852 | 282 | req := registrationRequest{} | 302 | req := registrationRequest{} |
2853 | 283 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) | 303 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
2854 | @@ -303,7 +323,8 @@ | |||
2855 | 303 | func (ss *serviceSuite) TestManageRegFailsOnBadJSONDocument(c *C) { | 323 | func (ss *serviceSuite) TestManageRegFailsOnBadJSONDocument(c *C) { |
2856 | 304 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 324 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
2857 | 305 | buf := make([]byte, 256) | 325 | buf := make([]byte, 256) |
2859 | 306 | n, e := r.Body.Read(buf) | 326 | n := r.ContentLength |
2860 | 327 | _, e := io.ReadFull(r.Body, buf[:n]) | ||
2861 | 307 | c.Assert(e, IsNil) | 328 | c.Assert(e, IsNil) |
2862 | 308 | req := registrationRequest{} | 329 | req := registrationRequest{} |
2863 | 309 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) | 330 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
2864 | @@ -329,7 +350,8 @@ | |||
2865 | 329 | func (ss *serviceSuite) TestDBusUnregisterWorks(c *C) { | 350 | func (ss *serviceSuite) TestDBusUnregisterWorks(c *C) { |
2866 | 330 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 351 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
2867 | 331 | buf := make([]byte, 256) | 352 | buf := make([]byte, 256) |
2869 | 332 | n, e := r.Body.Read(buf) | 353 | n := r.ContentLength |
2870 | 354 | _, e := io.ReadFull(r.Body, buf[:n]) | ||
2871 | 333 | c.Assert(e, IsNil) | 355 | c.Assert(e, IsNil) |
2872 | 334 | req := registrationRequest{} | 356 | req := registrationRequest{} |
2873 | 335 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) | 357 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
2874 | @@ -356,7 +378,8 @@ | |||
2875 | 356 | invoked := make(chan bool, 1) | 378 | invoked := make(chan bool, 1) |
2876 | 357 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 379 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
2877 | 358 | buf := make([]byte, 256) | 380 | buf := make([]byte, 256) |
2879 | 359 | n, e := r.Body.Read(buf) | 381 | n := r.ContentLength |
2880 | 382 | _, e := io.ReadFull(r.Body, buf[:n]) | ||
2881 | 360 | c.Assert(e, IsNil) | 383 | c.Assert(e, IsNil) |
2882 | 361 | req := registrationRequest{} | 384 | req := registrationRequest{} |
2883 | 362 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) | 385 | c.Assert(json.Unmarshal(buf[:n], &req), IsNil) |
2884 | 363 | 386 | ||
2885 | === modified file 'client/session/seenstate/seenstate.go' | |||
2886 | --- client/session/seenstate/seenstate.go 2014-05-14 17:42:24 +0000 | |||
2887 | +++ client/session/seenstate/seenstate.go 2015-03-26 16:42:21 +0000 | |||
2888 | @@ -28,8 +28,10 @@ | |||
2889 | 28 | // GetAll() returns a "simple" map of the current levels. | 28 | // GetAll() returns a "simple" map of the current levels. |
2890 | 29 | GetAllLevels() (map[string]int64, error) | 29 | GetAllLevels() (map[string]int64, error) |
2891 | 30 | // FilterBySeen filters notifications already seen, keep track | 30 | // FilterBySeen filters notifications already seen, keep track |
2893 | 31 | // of them as well | 31 | // of them as well. |
2894 | 32 | FilterBySeen([]protocol.Notification) ([]protocol.Notification, error) | 32 | FilterBySeen([]protocol.Notification) ([]protocol.Notification, error) |
2895 | 33 | // Close closes state. | ||
2896 | 34 | Close() | ||
2897 | 33 | } | 35 | } |
2898 | 34 | 36 | ||
2899 | 35 | type memSeenState struct { | 37 | type memSeenState struct { |
2900 | @@ -58,6 +60,9 @@ | |||
2901 | 58 | return acc, nil | 60 | return acc, nil |
2902 | 59 | } | 61 | } |
2903 | 60 | 62 | ||
2904 | 63 | func (m *memSeenState) Close() { | ||
2905 | 64 | } | ||
2906 | 65 | |||
2907 | 61 | var _ SeenState = (*memSeenState)(nil) | 66 | var _ SeenState = (*memSeenState)(nil) |
2908 | 62 | 67 | ||
2909 | 63 | // NewSeenState returns an implementation of SeenState that is memory-based and | 68 | // NewSeenState returns an implementation of SeenState that is memory-based and |
2910 | 64 | 69 | ||
2911 | === modified file 'client/session/seenstate/sqlseenstate.go' | |||
2912 | --- client/session/seenstate/sqlseenstate.go 2014-05-14 17:42:24 +0000 | |||
2913 | +++ client/session/seenstate/sqlseenstate.go 2015-03-26 16:42:21 +0000 | |||
2914 | @@ -48,6 +48,11 @@ | |||
2915 | 48 | return &sqliteSeenState{db}, nil | 48 | return &sqliteSeenState{db}, nil |
2916 | 49 | } | 49 | } |
2917 | 50 | 50 | ||
2918 | 51 | // Closes closes the underlying db. | ||
2919 | 52 | func (ps *sqliteSeenState) Close() { | ||
2920 | 53 | ps.db.Close() | ||
2921 | 54 | } | ||
2922 | 55 | |||
2923 | 51 | func (ps *sqliteSeenState) SetLevel(level string, top int64) error { | 56 | func (ps *sqliteSeenState) SetLevel(level string, top int64) error { |
2924 | 52 | _, err := ps.db.Exec("REPLACE INTO level_map (level, top) VALUES (?, ?)", level, top) | 57 | _, err := ps.db.Exec("REPLACE INTO level_map (level, top) VALUES (?, ?)", level, top) |
2925 | 53 | if err != nil { | 58 | if err != nil { |
2926 | 54 | 59 | ||
2927 | === modified file 'client/session/seenstate/sqlseenstate_test.go' | |||
2928 | --- client/session/seenstate/sqlseenstate_test.go 2014-05-14 17:42:24 +0000 | |||
2929 | +++ client/session/seenstate/sqlseenstate_test.go 2015-03-26 16:42:21 +0000 | |||
2930 | @@ -112,6 +112,15 @@ | |||
2931 | 112 | c.Check(err, ErrorMatches, "cannot insert .*") | 112 | c.Check(err, ErrorMatches, "cannot insert .*") |
2932 | 113 | } | 113 | } |
2933 | 114 | 114 | ||
2934 | 115 | func (s *sqlsSuite) TestClose(c *C) { | ||
2935 | 116 | dir := c.MkDir() | ||
2936 | 117 | filename := dir + "test.db" | ||
2937 | 118 | sqls, err := NewSqliteSeenState(filename) | ||
2938 | 119 | c.Check(err, IsNil) | ||
2939 | 120 | c.Assert(sqls, NotNil) | ||
2940 | 121 | sqls.Close() | ||
2941 | 122 | } | ||
2942 | 123 | |||
2943 | 115 | func (s *sqlsSuite) TestDropPrevThan(c *C) { | 124 | func (s *sqlsSuite) TestDropPrevThan(c *C) { |
2944 | 116 | dir := c.MkDir() | 125 | dir := c.MkDir() |
2945 | 117 | filename := dir + "test.db" | 126 | filename := dir + "test.db" |
2946 | 118 | 127 | ||
2947 | === modified file 'client/session/session.go' | |||
2948 | --- client/session/session.go 2015-01-22 17:34:18 +0000 | |||
2949 | +++ client/session/session.go 2015-03-26 16:42:21 +0000 | |||
2950 | @@ -39,6 +39,14 @@ | |||
2951 | 39 | "launchpad.net/ubuntu-push/util" | 39 | "launchpad.net/ubuntu-push/util" |
2952 | 40 | ) | 40 | ) |
2953 | 41 | 41 | ||
2954 | 42 | type sessCmd uint8 | ||
2955 | 43 | |||
2956 | 44 | const ( | ||
2957 | 45 | cmdDisconnect sessCmd = iota | ||
2958 | 46 | cmdConnect | ||
2959 | 47 | cmdResetCookie | ||
2960 | 48 | ) | ||
2961 | 49 | |||
2962 | 42 | var ( | 50 | var ( |
2963 | 43 | wireVersionBytes = []byte{protocol.ProtocolWireVersion} | 51 | wireVersionBytes = []byte{protocol.ProtocolWireVersion} |
2964 | 44 | ) | 52 | ) |
2965 | @@ -70,10 +78,12 @@ | |||
2966 | 70 | 78 | ||
2967 | 71 | const ( | 79 | const ( |
2968 | 72 | Error ClientSessionState = iota | 80 | Error ClientSessionState = iota |
2969 | 81 | Pristine | ||
2970 | 73 | Disconnected | 82 | Disconnected |
2971 | 74 | Connected | 83 | Connected |
2972 | 75 | Started | 84 | Started |
2973 | 76 | Running | 85 | Running |
2974 | 86 | Shutdown | ||
2975 | 77 | Unknown | 87 | Unknown |
2976 | 78 | ) | 88 | ) |
2977 | 79 | 89 | ||
2978 | @@ -83,10 +93,12 @@ | |||
2979 | 83 | } | 93 | } |
2980 | 84 | return [Unknown]string{ | 94 | return [Unknown]string{ |
2981 | 85 | "Error", | 95 | "Error", |
2982 | 96 | "Pristine", | ||
2983 | 86 | "Disconnected", | 97 | "Disconnected", |
2984 | 87 | "Connected", | 98 | "Connected", |
2985 | 88 | "Started", | 99 | "Started", |
2986 | 89 | "Running", | 100 | "Running", |
2987 | 101 | "Shutdown", | ||
2988 | 90 | }[s] | 102 | }[s] |
2989 | 91 | } | 103 | } |
2990 | 92 | 104 | ||
2991 | @@ -118,10 +130,20 @@ | |||
2992 | 118 | AuthGetter func(string) string | 130 | AuthGetter func(string) string |
2993 | 119 | AuthURL string | 131 | AuthURL string |
2994 | 120 | AddresseeChecker AddresseeChecking | 132 | AddresseeChecker AddresseeChecking |
2995 | 133 | BroadcastCh chan *BroadcastNotification | ||
2996 | 134 | NotificationsCh chan AddressedNotification | ||
2997 | 121 | } | 135 | } |
2998 | 122 | 136 | ||
2999 | 123 | // ClientSession holds a client<->server session and its configuration. | 137 | // ClientSession holds a client<->server session and its configuration. |
3001 | 124 | type ClientSession struct { | 138 | type ClientSession interface { |
3002 | 139 | ResetCookie() | ||
3003 | 140 | State() ClientSessionState | ||
3004 | 141 | HasConnectivity(bool) | ||
3005 | 142 | KeepConnection() error | ||
3006 | 143 | StopKeepConnection() | ||
3007 | 144 | } | ||
3008 | 145 | |||
3009 | 146 | type clientSession struct { | ||
3010 | 125 | // configuration | 147 | // configuration |
3011 | 126 | DeviceId string | 148 | DeviceId string |
3012 | 127 | ClientSessionConfig | 149 | ClientSessionConfig |
3013 | @@ -145,25 +167,36 @@ | |||
3014 | 145 | proto protocol.Protocol | 167 | proto protocol.Protocol |
3015 | 146 | pingInterval time.Duration | 168 | pingInterval time.Duration |
3016 | 147 | retrier util.AutoRedialer | 169 | retrier util.AutoRedialer |
3017 | 148 | retrierLock sync.Mutex | ||
3018 | 149 | cookie string | 170 | cookie string |
3019 | 150 | // status | 171 | // status |
3024 | 151 | stateP *uint32 | 172 | stateLock sync.RWMutex |
3025 | 152 | ErrCh chan error | 173 | state ClientSessionState |
3022 | 153 | BroadcastCh chan *BroadcastNotification | ||
3023 | 154 | NotificationsCh chan AddressedNotification | ||
3026 | 155 | // authorization | 174 | // authorization |
3027 | 156 | auth string | 175 | auth string |
3028 | 157 | // autoredial knobs | 176 | // autoredial knobs |
3029 | 158 | shouldDelayP *uint32 | 177 | shouldDelayP *uint32 |
3030 | 159 | lastAutoRedial time.Time | 178 | lastAutoRedial time.Time |
3032 | 160 | redialDelay func(*ClientSession) time.Duration | 179 | redialDelay func(*clientSession) time.Duration |
3033 | 161 | redialJitter func(time.Duration) time.Duration | 180 | redialJitter func(time.Duration) time.Duration |
3034 | 162 | redialDelays []time.Duration | 181 | redialDelays []time.Duration |
3035 | 163 | redialDelaysIdx int | 182 | redialDelaysIdx int |
3036 | 183 | // connection events, and cookie reset requests, come in over here | ||
3037 | 184 | cmdCh chan sessCmd | ||
3038 | 185 | // last seen connection event is here | ||
3039 | 186 | lastConn bool | ||
3040 | 187 | // connection events are handled by this | ||
3041 | 188 | connHandler func(bool) | ||
3042 | 189 | // autoredial goes over here (xxx spurious goroutine involved) | ||
3043 | 190 | doneCh chan uint32 | ||
3044 | 191 | // main loop errors out through here (possibly another spurious goroutine) | ||
3045 | 192 | errCh chan error | ||
3046 | 193 | // main loop errors are handled by this | ||
3047 | 194 | errHandler func(error) | ||
3048 | 195 | // look, a stopper! | ||
3049 | 196 | stopCh chan struct{} | ||
3050 | 164 | } | 197 | } |
3051 | 165 | 198 | ||
3053 | 166 | func redialDelay(sess *ClientSession) time.Duration { | 199 | func redialDelay(sess *clientSession) time.Duration { |
3054 | 167 | if sess.ShouldDelay() { | 200 | if sess.ShouldDelay() { |
3055 | 168 | t := sess.redialDelays[sess.redialDelaysIdx] | 201 | t := sess.redialDelays[sess.redialDelaysIdx] |
3056 | 169 | if len(sess.redialDelays) > sess.redialDelaysIdx+1 { | 202 | if len(sess.redialDelays) > sess.redialDelaysIdx+1 { |
3057 | @@ -178,8 +211,7 @@ | |||
3058 | 178 | 211 | ||
3059 | 179 | func NewSession(serverAddrSpec string, conf ClientSessionConfig, | 212 | func NewSession(serverAddrSpec string, conf ClientSessionConfig, |
3060 | 180 | deviceId string, seenStateFactory func() (seenstate.SeenState, error), | 213 | deviceId string, seenStateFactory func() (seenstate.SeenState, error), |
3063 | 181 | log logger.Logger) (*ClientSession, error) { | 214 | log logger.Logger) (*clientSession, error) { |
3062 | 182 | state := uint32(Disconnected) | ||
3064 | 183 | seenState, err := seenStateFactory() | 215 | seenState, err := seenStateFactory() |
3065 | 184 | if err != nil { | 216 | if err != nil { |
3066 | 185 | return nil, err | 217 | return nil, err |
3067 | @@ -191,7 +223,7 @@ | |||
3068 | 191 | getHost = gethosts.New(deviceId, hostsEndpoint, conf.ExchangeTimeout) | 223 | getHost = gethosts.New(deviceId, hostsEndpoint, conf.ExchangeTimeout) |
3069 | 192 | } | 224 | } |
3070 | 193 | var shouldDelay uint32 = 0 | 225 | var shouldDelay uint32 = 0 |
3072 | 194 | sess := &ClientSession{ | 226 | sess := &clientSession{ |
3073 | 195 | ClientSessionConfig: conf, | 227 | ClientSessionConfig: conf, |
3074 | 196 | getHost: getHost, | 228 | getHost: getHost, |
3075 | 197 | fallbackHosts: fallbackHosts, | 229 | fallbackHosts: fallbackHosts, |
3076 | @@ -200,10 +232,10 @@ | |||
3077 | 200 | Protocolator: protocol.NewProtocol0, | 232 | Protocolator: protocol.NewProtocol0, |
3078 | 201 | SeenState: seenState, | 233 | SeenState: seenState, |
3079 | 202 | TLS: &tls.Config{}, | 234 | TLS: &tls.Config{}, |
3081 | 203 | stateP: &state, | 235 | state: Pristine, |
3082 | 204 | timeSince: time.Since, | 236 | timeSince: time.Since, |
3083 | 205 | shouldDelayP: &shouldDelay, | 237 | shouldDelayP: &shouldDelay, |
3085 | 206 | redialDelay: redialDelay, | 238 | redialDelay: redialDelay, // NOTE there are tests that use calling sess.redialDelay as an indication of calling autoRedial! |
3086 | 207 | redialDelays: util.Timeouts(), | 239 | redialDelays: util.Timeouts(), |
3087 | 208 | } | 240 | } |
3088 | 209 | sess.redialJitter = sess.Jitter | 241 | sess.redialJitter = sess.Jitter |
3089 | @@ -215,62 +247,78 @@ | |||
3090 | 215 | } | 247 | } |
3091 | 216 | sess.TLS.RootCAs = cp | 248 | sess.TLS.RootCAs = cp |
3092 | 217 | } | 249 | } |
3093 | 250 | sess.doneCh = make(chan uint32, 1) | ||
3094 | 251 | sess.stopCh = make(chan struct{}) | ||
3095 | 252 | sess.cmdCh = make(chan sessCmd) | ||
3096 | 253 | sess.errCh = make(chan error, 1) | ||
3097 | 254 | |||
3098 | 255 | // to be overridden by tests | ||
3099 | 256 | sess.connHandler = sess.handleConn | ||
3100 | 257 | sess.errHandler = sess.handleErr | ||
3101 | 258 | |||
3102 | 218 | return sess, nil | 259 | return sess, nil |
3103 | 219 | } | 260 | } |
3104 | 220 | 261 | ||
3106 | 221 | func (sess *ClientSession) ShouldDelay() bool { | 262 | func (sess *clientSession) ShouldDelay() bool { |
3107 | 222 | return atomic.LoadUint32(sess.shouldDelayP) != 0 | 263 | return atomic.LoadUint32(sess.shouldDelayP) != 0 |
3108 | 223 | } | 264 | } |
3109 | 224 | 265 | ||
3111 | 225 | func (sess *ClientSession) setShouldDelay() { | 266 | func (sess *clientSession) setShouldDelay() { |
3112 | 226 | atomic.StoreUint32(sess.shouldDelayP, uint32(1)) | 267 | atomic.StoreUint32(sess.shouldDelayP, uint32(1)) |
3113 | 227 | } | 268 | } |
3114 | 228 | 269 | ||
3116 | 229 | func (sess *ClientSession) clearShouldDelay() { | 270 | func (sess *clientSession) clearShouldDelay() { |
3117 | 230 | atomic.StoreUint32(sess.shouldDelayP, uint32(0)) | 271 | atomic.StoreUint32(sess.shouldDelayP, uint32(0)) |
3118 | 231 | } | 272 | } |
3119 | 232 | 273 | ||
3130 | 233 | func (sess *ClientSession) State() ClientSessionState { | 274 | func (sess *clientSession) State() ClientSessionState { |
3131 | 234 | return ClientSessionState(atomic.LoadUint32(sess.stateP)) | 275 | sess.stateLock.RLock() |
3132 | 235 | } | 276 | defer sess.stateLock.RUnlock() |
3133 | 236 | 277 | return sess.state | |
3134 | 237 | func (sess *ClientSession) setState(state ClientSessionState) { | 278 | } |
3135 | 238 | sess.Log.Debugf("session.setState: %s -> %s", ClientSessionState(atomic.LoadUint32(sess.stateP)), state) | 279 | |
3136 | 239 | atomic.StoreUint32(sess.stateP, uint32(state)) | 280 | func (sess *clientSession) setState(state ClientSessionState) { |
3137 | 240 | } | 281 | sess.stateLock.Lock() |
3138 | 241 | 282 | defer sess.stateLock.Unlock() | |
3139 | 242 | func (sess *ClientSession) setConnection(conn net.Conn) { | 283 | sess.Log.Debugf("session.setState: %s -> %s", sess.state, state) |
3140 | 284 | sess.state = state | ||
3141 | 285 | } | ||
3142 | 286 | |||
3143 | 287 | func (sess *clientSession) setConnection(conn net.Conn) { | ||
3144 | 243 | sess.connLock.Lock() | 288 | sess.connLock.Lock() |
3145 | 244 | defer sess.connLock.Unlock() | 289 | defer sess.connLock.Unlock() |
3146 | 245 | sess.Connection = conn | 290 | sess.Connection = conn |
3147 | 246 | } | 291 | } |
3148 | 247 | 292 | ||
3150 | 248 | func (sess *ClientSession) getConnection() net.Conn { | 293 | func (sess *clientSession) getConnection() net.Conn { |
3151 | 249 | sess.connLock.RLock() | 294 | sess.connLock.RLock() |
3152 | 250 | defer sess.connLock.RUnlock() | 295 | defer sess.connLock.RUnlock() |
3153 | 251 | return sess.Connection | 296 | return sess.Connection |
3154 | 252 | } | 297 | } |
3155 | 253 | 298 | ||
3157 | 254 | func (sess *ClientSession) setCookie(cookie string) { | 299 | func (sess *clientSession) setCookie(cookie string) { |
3158 | 255 | sess.connLock.Lock() | 300 | sess.connLock.Lock() |
3159 | 256 | defer sess.connLock.Unlock() | 301 | defer sess.connLock.Unlock() |
3160 | 257 | sess.cookie = cookie | 302 | sess.cookie = cookie |
3161 | 258 | } | 303 | } |
3162 | 259 | 304 | ||
3164 | 260 | func (sess *ClientSession) getCookie() string { | 305 | func (sess *clientSession) getCookie() string { |
3165 | 261 | sess.connLock.RLock() | 306 | sess.connLock.RLock() |
3166 | 262 | defer sess.connLock.RUnlock() | 307 | defer sess.connLock.RUnlock() |
3167 | 263 | return sess.cookie | 308 | return sess.cookie |
3168 | 264 | } | 309 | } |
3169 | 265 | 310 | ||
3174 | 266 | func (sess *ClientSession) ClearCookie() { | 311 | func (sess *clientSession) ResetCookie() { |
3175 | 267 | sess.connLock.Lock() | 312 | sess.cmdCh <- cmdResetCookie |
3176 | 268 | defer sess.connLock.Unlock() | 313 | } |
3177 | 269 | sess.cookie = "" | 314 | |
3178 | 315 | func (sess *clientSession) resetCookie() { | ||
3179 | 316 | sess.stopRedial() | ||
3180 | 317 | sess.doClose(true) | ||
3181 | 270 | } | 318 | } |
3182 | 271 | 319 | ||
3183 | 272 | // getHosts sets deliveryHosts possibly querying a remote endpoint | 320 | // getHosts sets deliveryHosts possibly querying a remote endpoint |
3185 | 273 | func (sess *ClientSession) getHosts() error { | 321 | func (sess *clientSession) getHosts() error { |
3186 | 274 | if sess.getHost != nil { | 322 | if sess.getHost != nil { |
3187 | 275 | if sess.deliveryHosts != nil && sess.timeSince(sess.deliveryHostsTimestamp) < sess.HostsCachingExpiryTime { | 323 | if sess.deliveryHosts != nil && sess.timeSince(sess.deliveryHostsTimestamp) < sess.HostsCachingExpiryTime { |
3188 | 276 | return nil | 324 | return nil |
3189 | @@ -294,7 +342,7 @@ | |||
3190 | 294 | 342 | ||
3191 | 295 | // addAuthorization gets the authorization blob to send to the server | 343 | // addAuthorization gets the authorization blob to send to the server |
3192 | 296 | // and adds it to the session. | 344 | // and adds it to the session. |
3194 | 297 | func (sess *ClientSession) addAuthorization() error { | 345 | func (sess *clientSession) addAuthorization() error { |
3195 | 298 | if sess.AuthGetter != nil { | 346 | if sess.AuthGetter != nil { |
3196 | 299 | sess.Log.Debugf("adding authorization") | 347 | sess.Log.Debugf("adding authorization") |
3197 | 300 | sess.auth = sess.AuthGetter(sess.AuthURL) | 348 | sess.auth = sess.AuthGetter(sess.AuthURL) |
3198 | @@ -302,13 +350,13 @@ | |||
3199 | 302 | return nil | 350 | return nil |
3200 | 303 | } | 351 | } |
3201 | 304 | 352 | ||
3203 | 305 | func (sess *ClientSession) resetHosts() { | 353 | func (sess *clientSession) resetHosts() { |
3204 | 306 | sess.deliveryHosts = nil | 354 | sess.deliveryHosts = nil |
3205 | 307 | } | 355 | } |
3206 | 308 | 356 | ||
3207 | 309 | // startConnectionAttempt/nextHostToTry help connect iterating over candidate hosts | 357 | // startConnectionAttempt/nextHostToTry help connect iterating over candidate hosts |
3208 | 310 | 358 | ||
3210 | 311 | func (sess *ClientSession) startConnectionAttempt() { | 359 | func (sess *clientSession) startConnectionAttempt() { |
3211 | 312 | if sess.timeSince(sess.lastAttemptTimestamp) > sess.ExpectAllRepairedTime { | 360 | if sess.timeSince(sess.lastAttemptTimestamp) > sess.ExpectAllRepairedTime { |
3212 | 313 | sess.tryHost = 0 | 361 | sess.tryHost = 0 |
3213 | 314 | } | 362 | } |
3214 | @@ -319,7 +367,7 @@ | |||
3215 | 319 | sess.lastAttemptTimestamp = time.Now() | 367 | sess.lastAttemptTimestamp = time.Now() |
3216 | 320 | } | 368 | } |
3217 | 321 | 369 | ||
3219 | 322 | func (sess *ClientSession) nextHostToTry() string { | 370 | func (sess *clientSession) nextHostToTry() string { |
3220 | 323 | if sess.leftToTry == 0 { | 371 | if sess.leftToTry == 0 { |
3221 | 324 | return "" | 372 | return "" |
3222 | 325 | } | 373 | } |
3223 | @@ -331,7 +379,7 @@ | |||
3224 | 331 | 379 | ||
3225 | 332 | // we reached the Started state, we can retry with the same host if we | 380 | // we reached the Started state, we can retry with the same host if we |
3226 | 333 | // have to retry again | 381 | // have to retry again |
3228 | 334 | func (sess *ClientSession) started() { | 382 | func (sess *clientSession) started() { |
3229 | 335 | sess.tryHost-- | 383 | sess.tryHost-- |
3230 | 336 | if sess.tryHost == -1 { | 384 | if sess.tryHost == -1 { |
3231 | 337 | sess.tryHost = len(sess.deliveryHosts) - 1 | 385 | sess.tryHost = len(sess.deliveryHosts) - 1 |
3232 | @@ -341,7 +389,7 @@ | |||
3233 | 341 | 389 | ||
3234 | 342 | // connect to a server using the configuration in the ClientSession | 390 | // connect to a server using the configuration in the ClientSession |
3235 | 343 | // and set up the connection. | 391 | // and set up the connection. |
3237 | 344 | func (sess *ClientSession) connect() error { | 392 | func (sess *clientSession) connect() error { |
3238 | 345 | sess.setShouldDelay() | 393 | sess.setShouldDelay() |
3239 | 346 | sess.startConnectionAttempt() | 394 | sess.startConnectionAttempt() |
3240 | 347 | var err error | 395 | var err error |
3241 | @@ -363,49 +411,47 @@ | |||
3242 | 363 | return nil | 411 | return nil |
3243 | 364 | } | 412 | } |
3244 | 365 | 413 | ||
3248 | 366 | func (sess *ClientSession) stopRedial() { | 414 | func (sess *clientSession) stopRedial() { |
3246 | 367 | sess.retrierLock.Lock() | ||
3247 | 368 | defer sess.retrierLock.Unlock() | ||
3249 | 369 | if sess.retrier != nil { | 415 | if sess.retrier != nil { |
3250 | 370 | sess.retrier.Stop() | 416 | sess.retrier.Stop() |
3251 | 371 | sess.retrier = nil | 417 | sess.retrier = nil |
3252 | 372 | } | 418 | } |
3253 | 373 | } | 419 | } |
3254 | 374 | 420 | ||
3256 | 375 | func (sess *ClientSession) AutoRedial(doneCh chan uint32) { | 421 | func (sess *clientSession) autoRedial() { |
3257 | 376 | sess.stopRedial() | 422 | sess.stopRedial() |
3258 | 377 | if time.Since(sess.lastAutoRedial) < 2*time.Second { | 423 | if time.Since(sess.lastAutoRedial) < 2*time.Second { |
3259 | 378 | sess.setShouldDelay() | 424 | sess.setShouldDelay() |
3260 | 379 | } | 425 | } |
3264 | 380 | time.Sleep(sess.redialDelay(sess)) | 426 | // xxx should we really wait on the caller goroutine? |
3265 | 381 | sess.retrierLock.Lock() | 427 | delay := sess.redialDelay(sess) |
3266 | 382 | defer sess.retrierLock.Unlock() | 428 | sess.Log.Debugf("session redial delay: %v, wait", delay) |
3267 | 429 | time.Sleep(delay) | ||
3268 | 430 | sess.Log.Debugf("session redial delay: %v, cont", delay) | ||
3269 | 383 | if sess.retrier != nil { | 431 | if sess.retrier != nil { |
3270 | 384 | panic("session AutoRedial: unexpected non-nil retrier.") | 432 | panic("session AutoRedial: unexpected non-nil retrier.") |
3271 | 385 | } | 433 | } |
3272 | 386 | sess.retrier = util.NewAutoRedialer(sess) | 434 | sess.retrier = util.NewAutoRedialer(sess) |
3273 | 387 | sess.lastAutoRedial = time.Now() | 435 | sess.lastAutoRedial = time.Now() |
3282 | 388 | go func() { | 436 | go func(retrier util.AutoRedialer) { |
3275 | 389 | sess.retrierLock.Lock() | ||
3276 | 390 | retrier := sess.retrier | ||
3277 | 391 | sess.retrierLock.Unlock() | ||
3278 | 392 | if retrier == nil { | ||
3279 | 393 | sess.Log.Debugf("session autoredialer skipping retry: retrier has been set to nil.") | ||
3280 | 394 | return | ||
3281 | 395 | } | ||
3283 | 396 | sess.Log.Debugf("session autoredialier launching Redial goroutine") | 437 | sess.Log.Debugf("session autoredialier launching Redial goroutine") |
3294 | 397 | doneCh <- retrier.Redial() | 438 | // if the redialer has been stopped before calling Redial(), it'll return 0. |
3295 | 398 | }() | 439 | sess.doneCh <- retrier.Redial() |
3296 | 399 | } | 440 | }(sess.retrier) |
3297 | 400 | 441 | } | |
3298 | 401 | func (sess *ClientSession) Close() { | 442 | |
3299 | 402 | sess.stopRedial() | 443 | func (sess *clientSession) doClose(resetCookie bool) { |
3290 | 403 | sess.doClose() | ||
3291 | 404 | } | ||
3292 | 405 | |||
3293 | 406 | func (sess *ClientSession) doClose() { | ||
3300 | 407 | sess.connLock.Lock() | 444 | sess.connLock.Lock() |
3301 | 408 | defer sess.connLock.Unlock() | 445 | defer sess.connLock.Unlock() |
3302 | 446 | if resetCookie { | ||
3303 | 447 | sess.cookie = "" | ||
3304 | 448 | } | ||
3305 | 449 | sess.closeConnection() | ||
3306 | 450 | sess.setState(Disconnected) | ||
3307 | 451 | } | ||
3308 | 452 | |||
3309 | 453 | func (sess *clientSession) closeConnection() { | ||
3310 | 454 | // *must be called with connLock held* | ||
3311 | 409 | if sess.Connection != nil { | 455 | if sess.Connection != nil { |
3312 | 410 | sess.Connection.Close() | 456 | sess.Connection.Close() |
3313 | 411 | // we ignore Close errors, on purpose (the thinking being that | 457 | // we ignore Close errors, on purpose (the thinking being that |
3314 | @@ -413,11 +459,10 @@ | |||
3315 | 413 | // you could do to recover at this stage). | 459 | // you could do to recover at this stage). |
3316 | 414 | sess.Connection = nil | 460 | sess.Connection = nil |
3317 | 415 | } | 461 | } |
3318 | 416 | sess.setState(Disconnected) | ||
3319 | 417 | } | 462 | } |
3320 | 418 | 463 | ||
3321 | 419 | // handle "ping" messages | 464 | // handle "ping" messages |
3323 | 420 | func (sess *ClientSession) handlePing() error { | 465 | func (sess *clientSession) handlePing() error { |
3324 | 421 | err := sess.proto.WriteMessage(protocol.PingPongMsg{Type: "pong"}) | 466 | err := sess.proto.WriteMessage(protocol.PingPongMsg{Type: "pong"}) |
3325 | 422 | if err == nil { | 467 | if err == nil { |
3326 | 423 | sess.Log.Debugf("ping.") | 468 | sess.Log.Debugf("ping.") |
3327 | @@ -429,7 +474,7 @@ | |||
3328 | 429 | return err | 474 | return err |
3329 | 430 | } | 475 | } |
3330 | 431 | 476 | ||
3332 | 432 | func (sess *ClientSession) decodeBroadcast(bcast *serverMsg) *BroadcastNotification { | 477 | func (sess *clientSession) decodeBroadcast(bcast *serverMsg) *BroadcastNotification { |
3333 | 433 | decoded := make([]map[string]interface{}, 0) | 478 | decoded := make([]map[string]interface{}, 0) |
3334 | 434 | for _, p := range bcast.Payloads { | 479 | for _, p := range bcast.Payloads { |
3335 | 435 | var v map[string]interface{} | 480 | var v map[string]interface{} |
3336 | @@ -447,7 +492,7 @@ | |||
3337 | 447 | } | 492 | } |
3338 | 448 | 493 | ||
3339 | 449 | // handle "broadcast" messages | 494 | // handle "broadcast" messages |
3341 | 450 | func (sess *ClientSession) handleBroadcast(bcast *serverMsg) error { | 495 | func (sess *clientSession) handleBroadcast(bcast *serverMsg) error { |
3342 | 451 | err := sess.SeenState.SetLevel(bcast.ChanId, bcast.TopLevel) | 496 | err := sess.SeenState.SetLevel(bcast.ChanId, bcast.TopLevel) |
3343 | 452 | if err != nil { | 497 | if err != nil { |
3344 | 453 | sess.setState(Error) | 498 | sess.setState(Error) |
3345 | @@ -478,7 +523,7 @@ | |||
3346 | 478 | } | 523 | } |
3347 | 479 | 524 | ||
3348 | 480 | // handle "notifications" messages | 525 | // handle "notifications" messages |
3350 | 481 | func (sess *ClientSession) handleNotifications(ucast *serverMsg) error { | 526 | func (sess *clientSession) handleNotifications(ucast *serverMsg) error { |
3351 | 482 | notifs, err := sess.SeenState.FilterBySeen(ucast.Notifications) | 527 | notifs, err := sess.SeenState.FilterBySeen(ucast.Notifications) |
3352 | 483 | if err != nil { | 528 | if err != nil { |
3353 | 484 | sess.setState(Error) | 529 | sess.setState(Error) |
3354 | @@ -512,7 +557,7 @@ | |||
3355 | 512 | } | 557 | } |
3356 | 513 | 558 | ||
3357 | 514 | // handle "connbroken" messages | 559 | // handle "connbroken" messages |
3359 | 515 | func (sess *ClientSession) handleConnBroken(connBroken *serverMsg) error { | 560 | func (sess *clientSession) handleConnBroken(connBroken *serverMsg) error { |
3360 | 516 | sess.setState(Error) | 561 | sess.setState(Error) |
3361 | 517 | reason := connBroken.Reason | 562 | reason := connBroken.Reason |
3362 | 518 | err := fmt.Errorf("server broke connection: %s", reason) | 563 | err := fmt.Errorf("server broke connection: %s", reason) |
3363 | @@ -525,7 +570,7 @@ | |||
3364 | 525 | } | 570 | } |
3365 | 526 | 571 | ||
3366 | 527 | // handle "setparams" messages | 572 | // handle "setparams" messages |
3368 | 528 | func (sess *ClientSession) handleSetParams(setParams *serverMsg) error { | 573 | func (sess *clientSession) handleSetParams(setParams *serverMsg) error { |
3369 | 529 | if setParams.SetCookie != "" { | 574 | if setParams.SetCookie != "" { |
3370 | 530 | sess.setCookie(setParams.SetCookie) | 575 | sess.setCookie(setParams.SetCookie) |
3371 | 531 | } | 576 | } |
3372 | @@ -533,7 +578,7 @@ | |||
3373 | 533 | } | 578 | } |
3374 | 534 | 579 | ||
3375 | 535 | // loop runs the session with the server, emits a stream of events. | 580 | // loop runs the session with the server, emits a stream of events. |
3377 | 536 | func (sess *ClientSession) loop() error { | 581 | func (sess *clientSession) loop() error { |
3378 | 537 | var err error | 582 | var err error |
3379 | 538 | var recv serverMsg | 583 | var recv serverMsg |
3380 | 539 | sess.setState(Running) | 584 | sess.setState(Running) |
3381 | @@ -571,7 +616,7 @@ | |||
3382 | 571 | } | 616 | } |
3383 | 572 | 617 | ||
3384 | 573 | // Call this when you've connected and want to start looping. | 618 | // Call this when you've connected and want to start looping. |
3386 | 574 | func (sess *ClientSession) start() error { | 619 | func (sess *clientSession) start() error { |
3387 | 575 | conn := sess.getConnection() | 620 | conn := sess.getConnection() |
3388 | 576 | err := conn.SetDeadline(time.Now().Add(sess.ExchangeTimeout)) | 621 | err := conn.SetDeadline(time.Now().Add(sess.ExchangeTimeout)) |
3389 | 577 | if err != nil { | 622 | if err != nil { |
3390 | @@ -634,8 +679,8 @@ | |||
3391 | 634 | 679 | ||
3392 | 635 | // run calls connect, and if it works it calls start, and if it works | 680 | // run calls connect, and if it works it calls start, and if it works |
3393 | 636 | // it runs loop in a goroutine, and ships its return value over ErrCh. | 681 | // it runs loop in a goroutine, and ships its return value over ErrCh. |
3396 | 637 | func (sess *ClientSession) run(closer func(), authChecker, hostGetter, connecter, starter, looper func() error) error { | 682 | func (sess *clientSession) run(closer func(bool), authChecker, hostGetter, connecter, starter, looper func() error) error { |
3397 | 638 | closer() | 683 | closer(false) |
3398 | 639 | if err := authChecker(); err != nil { | 684 | if err := authChecker(); err != nil { |
3399 | 640 | return err | 685 | return err |
3400 | 641 | } | 686 | } |
3401 | @@ -646,17 +691,14 @@ | |||
3402 | 646 | if err == nil { | 691 | if err == nil { |
3403 | 647 | err = starter() | 692 | err = starter() |
3404 | 648 | if err == nil { | 693 | if err == nil { |
3409 | 649 | sess.ErrCh = make(chan error, 1) | 694 | go func() { sess.errCh <- looper() }() |
3406 | 650 | sess.BroadcastCh = make(chan *BroadcastNotification) | ||
3407 | 651 | sess.NotificationsCh = make(chan AddressedNotification) | ||
3408 | 652 | go func() { sess.ErrCh <- looper() }() | ||
3410 | 653 | } | 695 | } |
3411 | 654 | } | 696 | } |
3412 | 655 | return err | 697 | return err |
3413 | 656 | } | 698 | } |
3414 | 657 | 699 | ||
3415 | 658 | // This Jitter returns a random time.Duration somewhere in [-spread, spread]. | 700 | // This Jitter returns a random time.Duration somewhere in [-spread, spread]. |
3417 | 659 | func (sess *ClientSession) Jitter(spread time.Duration) time.Duration { | 701 | func (sess *clientSession) Jitter(spread time.Duration) time.Duration { |
3418 | 660 | if spread < 0 { | 702 | if spread < 0 { |
3419 | 661 | panic("spread must be non-negative") | 703 | panic("spread must be non-negative") |
3420 | 662 | } | 704 | } |
3421 | @@ -666,7 +708,7 @@ | |||
3422 | 666 | 708 | ||
3423 | 667 | // Dial takes the session from newly created (or newly disconnected) | 709 | // Dial takes the session from newly created (or newly disconnected) |
3424 | 668 | // to running the main loop. | 710 | // to running the main loop. |
3426 | 669 | func (sess *ClientSession) Dial() error { | 711 | func (sess *clientSession) Dial() error { |
3427 | 670 | if sess.Protocolator == nil { | 712 | if sess.Protocolator == nil { |
3428 | 671 | // a missing protocolator means you've willfully overridden | 713 | // a missing protocolator means you've willfully overridden |
3429 | 672 | // it; returning an error here would prompt AutoRedial to just | 714 | // it; returning an error here would prompt AutoRedial to just |
3430 | @@ -676,6 +718,90 @@ | |||
3431 | 676 | return sess.run(sess.doClose, sess.addAuthorization, sess.getHosts, sess.connect, sess.start, sess.loop) | 718 | return sess.run(sess.doClose, sess.addAuthorization, sess.getHosts, sess.connect, sess.start, sess.loop) |
3432 | 677 | } | 719 | } |
3433 | 678 | 720 | ||
3434 | 721 | func (sess *clientSession) shutdown() { | ||
3435 | 722 | sess.Log.Infof("session shutting down.") | ||
3436 | 723 | sess.connLock.Lock() | ||
3437 | 724 | defer sess.connLock.Unlock() | ||
3438 | 725 | sess.stopRedial() | ||
3439 | 726 | sess.closeConnection() | ||
3440 | 727 | } | ||
3441 | 728 | |||
3442 | 729 | func (sess *clientSession) doKeepConnection() { | ||
3443 | 730 | for { | ||
3444 | 731 | select { | ||
3445 | 732 | case cmd := <-sess.cmdCh: | ||
3446 | 733 | switch cmd { | ||
3447 | 734 | case cmdConnect: | ||
3448 | 735 | sess.connHandler(true) | ||
3449 | 736 | case cmdDisconnect: | ||
3450 | 737 | sess.connHandler(false) | ||
3451 | 738 | case cmdResetCookie: | ||
3452 | 739 | sess.resetCookie() | ||
3453 | 740 | } | ||
3454 | 741 | case <-sess.stopCh: | ||
3455 | 742 | sess.shutdown() | ||
3456 | 743 | return | ||
3457 | 744 | case n := <-sess.doneCh: | ||
3458 | 745 | // if n == 0, the redialer aborted. If you do | ||
3459 | 746 | // anything other than log it, keep that in mind. | ||
3460 | 747 | sess.Log.Debugf("connected after %d attempts.", n) | ||
3461 | 748 | case err := <-sess.errCh: | ||
3462 | 749 | sess.errHandler(err) | ||
3463 | 750 | } | ||
3464 | 751 | } | ||
3465 | 752 | } | ||
3466 | 753 | |||
3467 | 754 | func (sess *clientSession) handleConn(hasConn bool) { | ||
3468 | 755 | sess.lastConn = hasConn | ||
3469 | 756 | |||
3470 | 757 | // Note this does not depend on the current state! That's because Dial | ||
3471 | 758 | // starts with doClose, which gets you to Disconnected even if you're | ||
3472 | 759 | // connected, and you can call Close when Disconnected without it | ||
3473 | 760 | // losing its stuff. | ||
3474 | 761 | if hasConn { | ||
3475 | 762 | sess.autoRedial() | ||
3476 | 763 | } else { | ||
3477 | 764 | sess.stopRedial() | ||
3478 | 765 | sess.doClose(false) | ||
3479 | 766 | } | ||
3480 | 767 | } | ||
3481 | 768 | |||
3482 | 769 | func (sess *clientSession) handleErr(err error) { | ||
3483 | 770 | sess.Log.Errorf("session error'ed out with %v", err) | ||
3484 | 771 | // State() == Error mostly defends interrupting an ongoing | ||
3485 | 772 | // autoRedial if we went quickly already through hasConn = | ||
3486 | 773 | // false => hasConn = true | ||
3487 | 774 | if sess.State() == Error && sess.lastConn { | ||
3488 | 775 | sess.autoRedial() | ||
3489 | 776 | } | ||
3490 | 777 | } | ||
3491 | 778 | |||
3492 | 779 | func (sess *clientSession) KeepConnection() error { | ||
3493 | 780 | sess.stateLock.Lock() | ||
3494 | 781 | defer sess.stateLock.Unlock() | ||
3495 | 782 | if sess.state != Pristine { | ||
3496 | 783 | return errors.New("don't call KeepConnection() on a non-pristine session.") | ||
3497 | 784 | } | ||
3498 | 785 | sess.state = Disconnected | ||
3499 | 786 | |||
3500 | 787 | go sess.doKeepConnection() | ||
3501 | 788 | |||
3502 | 789 | return nil | ||
3503 | 790 | } | ||
3504 | 791 | |||
3505 | 792 | func (sess *clientSession) StopKeepConnection() { | ||
3506 | 793 | sess.setState(Shutdown) | ||
3507 | 794 | close(sess.stopCh) | ||
3508 | 795 | } | ||
3509 | 796 | |||
3510 | 797 | func (sess *clientSession) HasConnectivity(hasConn bool) { | ||
3511 | 798 | if hasConn { | ||
3512 | 799 | sess.cmdCh <- cmdConnect | ||
3513 | 800 | } else { | ||
3514 | 801 | sess.cmdCh <- cmdDisconnect | ||
3515 | 802 | } | ||
3516 | 803 | } | ||
3517 | 804 | |||
3518 | 679 | func init() { | 805 | func init() { |
3519 | 680 | rand.Seed(time.Now().Unix()) // good enough for us (we're not using it for crypto) | 806 | rand.Seed(time.Now().Unix()) // good enough for us (we're not using it for crypto) |
3520 | 681 | } | 807 | } |
3521 | 682 | 808 | ||
3522 | === modified file 'client/session/session_test.go' | |||
3523 | --- client/session/session_test.go 2014-10-23 19:30:24 +0000 | |||
3524 | +++ client/session/session_test.go 2015-03-26 16:42:21 +0000 | |||
3525 | @@ -162,6 +162,7 @@ | |||
3526 | 162 | 162 | ||
3527 | 163 | func (*brokenSeenState) SetLevel(string, int64) error { return errors.New("broken.") } | 163 | func (*brokenSeenState) SetLevel(string, int64) error { return errors.New("broken.") } |
3528 | 164 | func (*brokenSeenState) GetAllLevels() (map[string]int64, error) { return nil, errors.New("broken.") } | 164 | func (*brokenSeenState) GetAllLevels() (map[string]int64, error) { return nil, errors.New("broken.") } |
3529 | 165 | func (*brokenSeenState) Close() {} | ||
3530 | 165 | func (*brokenSeenState) FilterBySeen([]protocol.Notification) ([]protocol.Notification, error) { | 166 | func (*brokenSeenState) FilterBySeen([]protocol.Notification) ([]protocol.Notification, error) { |
3531 | 166 | return nil, errors.New("broken.") | 167 | return nil, errors.New("broken.") |
3532 | 167 | } | 168 | } |
3533 | @@ -189,6 +190,24 @@ | |||
3534 | 189 | cs.lvls = func() (seenstate.SeenState, error) { return seenstate.NewSqliteSeenState(":memory:") } | 190 | cs.lvls = func() (seenstate.SeenState, error) { return seenstate.NewSqliteSeenState(":memory:") } |
3535 | 190 | } | 191 | } |
3536 | 191 | 192 | ||
3537 | 193 | func (cs *clientSessionSuite) TestStateString(c *C) { | ||
3538 | 194 | for _, i := range []struct { | ||
3539 | 195 | v ClientSessionState | ||
3540 | 196 | s string | ||
3541 | 197 | }{ | ||
3542 | 198 | {Error, "Error"}, | ||
3543 | 199 | {Pristine, "Pristine"}, | ||
3544 | 200 | {Disconnected, "Disconnected"}, | ||
3545 | 201 | {Connected, "Connected"}, | ||
3546 | 202 | {Started, "Started"}, | ||
3547 | 203 | {Running, "Running"}, | ||
3548 | 204 | {Shutdown, "Shutdown"}, | ||
3549 | 205 | {Unknown, fmt.Sprintf("??? (%d)", Unknown)}, | ||
3550 | 206 | } { | ||
3551 | 207 | c.Check(i.v.String(), Equals, i.s) | ||
3552 | 208 | } | ||
3553 | 209 | } | ||
3554 | 210 | |||
3555 | 192 | /**************************************************************** | 211 | /**************************************************************** |
3556 | 193 | parseServerAddrSpec() tests | 212 | parseServerAddrSpec() tests |
3557 | 194 | ****************************************************************/ | 213 | ****************************************************************/ |
3558 | @@ -211,10 +230,15 @@ | |||
3559 | 211 | NewSession() tests | 230 | NewSession() tests |
3560 | 212 | ****************************************************************/ | 231 | ****************************************************************/ |
3561 | 213 | 232 | ||
3563 | 214 | var dummyConf = ClientSessionConfig{} | 233 | func dummyConf() ClientSessionConfig { |
3564 | 234 | return ClientSessionConfig{ | ||
3565 | 235 | BroadcastCh: make(chan *BroadcastNotification, 5), | ||
3566 | 236 | NotificationsCh: make(chan AddressedNotification, 5), | ||
3567 | 237 | } | ||
3568 | 238 | } | ||
3569 | 215 | 239 | ||
3570 | 216 | func (cs *clientSessionSuite) TestNewSessionPlainWorks(c *C) { | 240 | func (cs *clientSessionSuite) TestNewSessionPlainWorks(c *C) { |
3572 | 217 | sess, err := NewSession("foo:443", dummyConf, "", cs.lvls, cs.log) | 241 | sess, err := NewSession("foo:443", dummyConf(), "", cs.lvls, cs.log) |
3573 | 218 | c.Check(sess, NotNil) | 242 | c.Check(sess, NotNil) |
3574 | 219 | c.Check(err, IsNil) | 243 | c.Check(err, IsNil) |
3575 | 220 | c.Check(sess.fallbackHosts, DeepEquals, []string{"foo:443"}) | 244 | c.Check(sess.fallbackHosts, DeepEquals, []string{"foo:443"}) |
3576 | @@ -224,11 +248,13 @@ | |||
3577 | 224 | c.Check(sess.redialDelays, DeepEquals, util.Timeouts()) | 248 | c.Check(sess.redialDelays, DeepEquals, util.Timeouts()) |
3578 | 225 | // but no root CAs set | 249 | // but no root CAs set |
3579 | 226 | c.Check(sess.TLS.RootCAs, IsNil) | 250 | c.Check(sess.TLS.RootCAs, IsNil) |
3581 | 227 | c.Check(sess.State(), Equals, Disconnected) | 251 | c.Check(sess.State(), Equals, Pristine) |
3582 | 252 | c.Check(sess.stopCh, NotNil) | ||
3583 | 253 | c.Check(sess.cmdCh, NotNil) | ||
3584 | 228 | } | 254 | } |
3585 | 229 | 255 | ||
3586 | 230 | func (cs *clientSessionSuite) TestNewSessionHostEndpointWorks(c *C) { | 256 | func (cs *clientSessionSuite) TestNewSessionHostEndpointWorks(c *C) { |
3588 | 231 | sess, err := NewSession("http://foo/hosts", dummyConf, "wah", cs.lvls, cs.log) | 257 | sess, err := NewSession("http://foo/hosts", dummyConf(), "wah", cs.lvls, cs.log) |
3589 | 232 | c.Assert(err, IsNil) | 258 | c.Assert(err, IsNil) |
3590 | 233 | c.Check(sess.getHost, NotNil) | 259 | c.Check(sess.getHost, NotNil) |
3591 | 234 | } | 260 | } |
3592 | @@ -254,7 +280,7 @@ | |||
3593 | 254 | 280 | ||
3594 | 255 | func (cs *clientSessionSuite) TestNewSessionBadSeenStateFails(c *C) { | 281 | func (cs *clientSessionSuite) TestNewSessionBadSeenStateFails(c *C) { |
3595 | 256 | ferr := func() (seenstate.SeenState, error) { return nil, errors.New("Busted.") } | 282 | ferr := func() (seenstate.SeenState, error) { return nil, errors.New("Busted.") } |
3597 | 257 | sess, err := NewSession("", dummyConf, "wah", ferr, cs.log) | 283 | sess, err := NewSession("", dummyConf(), "wah", ferr, cs.log) |
3598 | 258 | c.Check(sess, IsNil) | 284 | c.Check(sess, IsNil) |
3599 | 259 | c.Assert(err, NotNil) | 285 | c.Assert(err, NotNil) |
3600 | 260 | } | 286 | } |
3601 | @@ -265,7 +291,7 @@ | |||
3602 | 265 | 291 | ||
3603 | 266 | func (cs *clientSessionSuite) TestGetHostsFallback(c *C) { | 292 | func (cs *clientSessionSuite) TestGetHostsFallback(c *C) { |
3604 | 267 | fallback := []string{"foo:443", "bar:443"} | 293 | fallback := []string{"foo:443", "bar:443"} |
3606 | 268 | sess := &ClientSession{fallbackHosts: fallback} | 294 | sess := &clientSession{fallbackHosts: fallback} |
3607 | 269 | err := sess.getHosts() | 295 | err := sess.getHosts() |
3608 | 270 | c.Assert(err, IsNil) | 296 | c.Assert(err, IsNil) |
3609 | 271 | c.Check(sess.deliveryHosts, DeepEquals, fallback) | 297 | c.Check(sess.deliveryHosts, DeepEquals, fallback) |
3610 | @@ -283,14 +309,14 @@ | |||
3611 | 283 | 309 | ||
3612 | 284 | func (cs *clientSessionSuite) TestGetHostsRemote(c *C) { | 310 | func (cs *clientSessionSuite) TestGetHostsRemote(c *C) { |
3613 | 285 | hostGetter := &testHostGetter{"example.com", []string{"foo:443", "bar:443"}, nil} | 311 | hostGetter := &testHostGetter{"example.com", []string{"foo:443", "bar:443"}, nil} |
3615 | 286 | sess := &ClientSession{getHost: hostGetter, timeSince: time.Since} | 312 | sess := &clientSession{getHost: hostGetter, timeSince: time.Since} |
3616 | 287 | err := sess.getHosts() | 313 | err := sess.getHosts() |
3617 | 288 | c.Assert(err, IsNil) | 314 | c.Assert(err, IsNil) |
3618 | 289 | c.Check(sess.deliveryHosts, DeepEquals, []string{"foo:443", "bar:443"}) | 315 | c.Check(sess.deliveryHosts, DeepEquals, []string{"foo:443", "bar:443"}) |
3619 | 290 | } | 316 | } |
3620 | 291 | 317 | ||
3621 | 292 | func (cs *clientSessionSuite) TestGetHostsRemoteError(c *C) { | 318 | func (cs *clientSessionSuite) TestGetHostsRemoteError(c *C) { |
3623 | 293 | sess, err := NewSession("", dummyConf, "", cs.lvls, cs.log) | 319 | sess, err := NewSession("", dummyConf(), "", cs.lvls, cs.log) |
3624 | 294 | c.Assert(err, IsNil) | 320 | c.Assert(err, IsNil) |
3625 | 295 | hostsErr := errors.New("failed") | 321 | hostsErr := errors.New("failed") |
3626 | 296 | hostGetter := &testHostGetter{"", nil, hostsErr} | 322 | hostGetter := &testHostGetter{"", nil, hostsErr} |
3627 | @@ -303,7 +329,7 @@ | |||
3628 | 303 | 329 | ||
3629 | 304 | func (cs *clientSessionSuite) TestGetHostsRemoteCaching(c *C) { | 330 | func (cs *clientSessionSuite) TestGetHostsRemoteCaching(c *C) { |
3630 | 305 | hostGetter := &testHostGetter{"example.com", []string{"foo:443", "bar:443"}, nil} | 331 | hostGetter := &testHostGetter{"example.com", []string{"foo:443", "bar:443"}, nil} |
3632 | 306 | sess := &ClientSession{ | 332 | sess := &clientSession{ |
3633 | 307 | getHost: hostGetter, | 333 | getHost: hostGetter, |
3634 | 308 | ClientSessionConfig: ClientSessionConfig{ | 334 | ClientSessionConfig: ClientSessionConfig{ |
3635 | 309 | HostsCachingExpiryTime: 2 * time.Hour, | 335 | HostsCachingExpiryTime: 2 * time.Hour, |
3636 | @@ -328,7 +354,7 @@ | |||
3637 | 328 | 354 | ||
3638 | 329 | func (cs *clientSessionSuite) TestGetHostsRemoteCachingReset(c *C) { | 355 | func (cs *clientSessionSuite) TestGetHostsRemoteCachingReset(c *C) { |
3639 | 330 | hostGetter := &testHostGetter{"example.com", []string{"foo:443", "bar:443"}, nil} | 356 | hostGetter := &testHostGetter{"example.com", []string{"foo:443", "bar:443"}, nil} |
3641 | 331 | sess := &ClientSession{ | 357 | sess := &clientSession{ |
3642 | 332 | getHost: hostGetter, | 358 | getHost: hostGetter, |
3643 | 333 | ClientSessionConfig: ClientSessionConfig{ | 359 | ClientSessionConfig: ClientSessionConfig{ |
3644 | 334 | HostsCachingExpiryTime: 2 * time.Hour, | 360 | HostsCachingExpiryTime: 2 * time.Hour, |
3645 | @@ -355,7 +381,7 @@ | |||
3646 | 355 | 381 | ||
3647 | 356 | func (cs *clientSessionSuite) TestAddAuthorizationAddsAuthorization(c *C) { | 382 | func (cs *clientSessionSuite) TestAddAuthorizationAddsAuthorization(c *C) { |
3648 | 357 | url := "xyzzy://" | 383 | url := "xyzzy://" |
3650 | 358 | sess := &ClientSession{Log: cs.log} | 384 | sess := &clientSession{Log: cs.log} |
3651 | 359 | sess.AuthGetter = func(url string) string { return url + " auth'ed" } | 385 | sess.AuthGetter = func(url string) string { return url + " auth'ed" } |
3652 | 360 | sess.AuthURL = url | 386 | sess.AuthURL = url |
3653 | 361 | c.Assert(sess.auth, Equals, "") | 387 | c.Assert(sess.auth, Equals, "") |
3654 | @@ -365,7 +391,7 @@ | |||
3655 | 365 | } | 391 | } |
3656 | 366 | 392 | ||
3657 | 367 | func (cs *clientSessionSuite) TestAddAuthorizationSkipsIfUnset(c *C) { | 393 | func (cs *clientSessionSuite) TestAddAuthorizationSkipsIfUnset(c *C) { |
3659 | 368 | sess := &ClientSession{Log: cs.log} | 394 | sess := &clientSession{Log: cs.log} |
3660 | 369 | sess.AuthGetter = nil | 395 | sess.AuthGetter = nil |
3661 | 370 | c.Assert(sess.auth, Equals, "") | 396 | c.Assert(sess.auth, Equals, "") |
3662 | 371 | err := sess.addAuthorization() | 397 | err := sess.addAuthorization() |
3663 | @@ -379,7 +405,7 @@ | |||
3664 | 379 | 405 | ||
3665 | 380 | func (cs *clientSessionSuite) TestStartConnectionAttempt(c *C) { | 406 | func (cs *clientSessionSuite) TestStartConnectionAttempt(c *C) { |
3666 | 381 | since := time.Since(time.Time{}) | 407 | since := time.Since(time.Time{}) |
3668 | 382 | sess := &ClientSession{ | 408 | sess := &clientSession{ |
3669 | 383 | ClientSessionConfig: ClientSessionConfig{ | 409 | ClientSessionConfig: ClientSessionConfig{ |
3670 | 384 | ExpectAllRepairedTime: 10 * time.Second, | 410 | ExpectAllRepairedTime: 10 * time.Second, |
3671 | 385 | }, | 411 | }, |
3672 | @@ -403,7 +429,7 @@ | |||
3673 | 403 | 429 | ||
3674 | 404 | func (cs *clientSessionSuite) TestStartConnectionAttemptNoHostsPanic(c *C) { | 430 | func (cs *clientSessionSuite) TestStartConnectionAttemptNoHostsPanic(c *C) { |
3675 | 405 | since := time.Since(time.Time{}) | 431 | since := time.Since(time.Time{}) |
3677 | 406 | sess := &ClientSession{ | 432 | sess := &clientSession{ |
3678 | 407 | ClientSessionConfig: ClientSessionConfig{ | 433 | ClientSessionConfig: ClientSessionConfig{ |
3679 | 408 | ExpectAllRepairedTime: 10 * time.Second, | 434 | ExpectAllRepairedTime: 10 * time.Second, |
3680 | 409 | }, | 435 | }, |
3681 | @@ -415,7 +441,7 @@ | |||
3682 | 415 | } | 441 | } |
3683 | 416 | 442 | ||
3684 | 417 | func (cs *clientSessionSuite) TestNextHostToTry(c *C) { | 443 | func (cs *clientSessionSuite) TestNextHostToTry(c *C) { |
3686 | 418 | sess := &ClientSession{ | 444 | sess := &clientSession{ |
3687 | 419 | deliveryHosts: []string{"foo:443", "bar:443", "baz:443"}, | 445 | deliveryHosts: []string{"foo:443", "bar:443", "baz:443"}, |
3688 | 420 | tryHost: 0, | 446 | tryHost: 0, |
3689 | 421 | leftToTry: 3, | 447 | leftToTry: 3, |
3690 | @@ -438,7 +464,7 @@ | |||
3691 | 438 | } | 464 | } |
3692 | 439 | 465 | ||
3693 | 440 | func (cs *clientSessionSuite) TestStarted(c *C) { | 466 | func (cs *clientSessionSuite) TestStarted(c *C) { |
3695 | 441 | sess, err := NewSession("", dummyConf, "", cs.lvls, cs.log) | 467 | sess, err := NewSession("", dummyConf(), "", cs.lvls, cs.log) |
3696 | 442 | c.Assert(err, IsNil) | 468 | c.Assert(err, IsNil) |
3697 | 443 | 469 | ||
3698 | 444 | sess.deliveryHosts = []string{"foo:443", "bar:443", "baz:443"} | 470 | sess.deliveryHosts = []string{"foo:443", "bar:443", "baz:443"} |
3699 | @@ -457,7 +483,7 @@ | |||
3700 | 457 | ****************************************************************/ | 483 | ****************************************************************/ |
3701 | 458 | 484 | ||
3702 | 459 | func (cs *clientSessionSuite) TestConnectFailsWithNoAddress(c *C) { | 485 | func (cs *clientSessionSuite) TestConnectFailsWithNoAddress(c *C) { |
3704 | 460 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 486 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
3705 | 461 | c.Assert(err, IsNil) | 487 | c.Assert(err, IsNil) |
3706 | 462 | sess.deliveryHosts = []string{"nowhere"} | 488 | sess.deliveryHosts = []string{"nowhere"} |
3707 | 463 | sess.clearShouldDelay() | 489 | sess.clearShouldDelay() |
3708 | @@ -471,7 +497,7 @@ | |||
3709 | 471 | srv, err := net.Listen("tcp", "localhost:0") | 497 | srv, err := net.Listen("tcp", "localhost:0") |
3710 | 472 | c.Assert(err, IsNil) | 498 | c.Assert(err, IsNil) |
3711 | 473 | defer srv.Close() | 499 | defer srv.Close() |
3713 | 474 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 500 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
3714 | 475 | c.Assert(err, IsNil) | 501 | c.Assert(err, IsNil) |
3715 | 476 | sess.deliveryHosts = []string{srv.Addr().String()} | 502 | sess.deliveryHosts = []string{srv.Addr().String()} |
3716 | 477 | sess.clearShouldDelay() | 503 | sess.clearShouldDelay() |
3717 | @@ -486,7 +512,7 @@ | |||
3718 | 486 | srv, err := net.Listen("tcp", "localhost:0") | 512 | srv, err := net.Listen("tcp", "localhost:0") |
3719 | 487 | c.Assert(err, IsNil) | 513 | c.Assert(err, IsNil) |
3720 | 488 | defer srv.Close() | 514 | defer srv.Close() |
3722 | 489 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 515 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
3723 | 490 | c.Assert(err, IsNil) | 516 | c.Assert(err, IsNil) |
3724 | 491 | sess.deliveryHosts = []string{"nowhere", srv.Addr().String()} | 517 | sess.deliveryHosts = []string{"nowhere", srv.Addr().String()} |
3725 | 492 | sess.clearShouldDelay() | 518 | sess.clearShouldDelay() |
3726 | @@ -501,7 +527,7 @@ | |||
3727 | 501 | func (cs *clientSessionSuite) TestConnectConnectFail(c *C) { | 527 | func (cs *clientSessionSuite) TestConnectConnectFail(c *C) { |
3728 | 502 | srv, err := net.Listen("tcp", "localhost:0") | 528 | srv, err := net.Listen("tcp", "localhost:0") |
3729 | 503 | c.Assert(err, IsNil) | 529 | c.Assert(err, IsNil) |
3731 | 504 | sess, err := NewSession(srv.Addr().String(), dummyConf, "wah", cs.lvls, cs.log) | 530 | sess, err := NewSession(srv.Addr().String(), dummyConf(), "wah", cs.lvls, cs.log) |
3732 | 505 | srv.Close() | 531 | srv.Close() |
3733 | 506 | c.Assert(err, IsNil) | 532 | c.Assert(err, IsNil) |
3734 | 507 | sess.deliveryHosts = []string{srv.Addr().String()} | 533 | sess.deliveryHosts = []string{srv.Addr().String()} |
3735 | @@ -512,101 +538,58 @@ | |||
3736 | 512 | c.Check(sess.State(), Equals, Error) | 538 | c.Check(sess.State(), Equals, Error) |
3737 | 513 | } | 539 | } |
3738 | 514 | 540 | ||
3792 | 515 | /**************************************************************** | 541 | type dumbRetrier struct{ stopped bool } |
3793 | 516 | Close() tests | 542 | |
3794 | 517 | ****************************************************************/ | 543 | func (*dumbRetrier) Redial() uint32 { return 0 } |
3795 | 518 | 544 | func (d *dumbRetrier) Stop() { d.stopped = true } | |
3796 | 519 | func (cs *clientSessionSuite) TestClose(c *C) { | 545 | |
3797 | 520 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 546 | // /**************************************************************** |
3798 | 521 | c.Assert(err, IsNil) | 547 | // AutoRedial() tests |
3799 | 522 | sess.Connection = &testConn{Name: "TestClose"} | 548 | // ****************************************************************/ |
3747 | 523 | sess.Close() | ||
3748 | 524 | c.Check(sess.Connection, IsNil) | ||
3749 | 525 | c.Check(sess.State(), Equals, Disconnected) | ||
3750 | 526 | } | ||
3751 | 527 | |||
3752 | 528 | func (cs *clientSessionSuite) TestCloseTwice(c *C) { | ||
3753 | 529 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | ||
3754 | 530 | c.Assert(err, IsNil) | ||
3755 | 531 | sess.Connection = &testConn{Name: "TestCloseTwice"} | ||
3756 | 532 | sess.Close() | ||
3757 | 533 | c.Check(sess.Connection, IsNil) | ||
3758 | 534 | sess.Close() | ||
3759 | 535 | c.Check(sess.Connection, IsNil) | ||
3760 | 536 | c.Check(sess.State(), Equals, Disconnected) | ||
3761 | 537 | } | ||
3762 | 538 | |||
3763 | 539 | func (cs *clientSessionSuite) TestCloseFails(c *C) { | ||
3764 | 540 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | ||
3765 | 541 | c.Assert(err, IsNil) | ||
3766 | 542 | sess.Connection = &testConn{Name: "TestCloseFails", CloseCondition: condition.Work(false)} | ||
3767 | 543 | sess.Close() | ||
3768 | 544 | c.Check(sess.Connection, IsNil) // nothing you can do to clean up anyway | ||
3769 | 545 | c.Check(sess.State(), Equals, Disconnected) | ||
3770 | 546 | } | ||
3771 | 547 | |||
3772 | 548 | type derp struct{ stopped bool } | ||
3773 | 549 | |||
3774 | 550 | func (*derp) Redial() uint32 { return 0 } | ||
3775 | 551 | func (d *derp) Stop() { d.stopped = true } | ||
3776 | 552 | |||
3777 | 553 | func (cs *clientSessionSuite) TestCloseStopsRetrier(c *C) { | ||
3778 | 554 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | ||
3779 | 555 | c.Assert(err, IsNil) | ||
3780 | 556 | ar := new(derp) | ||
3781 | 557 | sess.retrier = ar | ||
3782 | 558 | c.Check(ar.stopped, Equals, false) | ||
3783 | 559 | sess.Close() | ||
3784 | 560 | c.Check(ar.stopped, Equals, true) | ||
3785 | 561 | sess.Close() // double close check | ||
3786 | 562 | c.Check(ar.stopped, Equals, true) | ||
3787 | 563 | } | ||
3788 | 564 | |||
3789 | 565 | /**************************************************************** | ||
3790 | 566 | AutoRedial() tests | ||
3791 | 567 | ****************************************************************/ | ||
3800 | 568 | 549 | ||
3801 | 569 | func (cs *clientSessionSuite) TestAutoRedialWorks(c *C) { | 550 | func (cs *clientSessionSuite) TestAutoRedialWorks(c *C) { |
3802 | 570 | // checks that AutoRedial sets up a retrier and tries redialing it | 551 | // checks that AutoRedial sets up a retrier and tries redialing it |
3804 | 571 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 552 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
3805 | 572 | c.Assert(err, IsNil) | 553 | c.Assert(err, IsNil) |
3807 | 573 | ar := new(derp) | 554 | ar := new(dumbRetrier) |
3808 | 574 | sess.retrier = ar | 555 | sess.retrier = ar |
3809 | 575 | c.Check(ar.stopped, Equals, false) | 556 | c.Check(ar.stopped, Equals, false) |
3811 | 576 | sess.AutoRedial(nil) | 557 | sess.autoRedial() |
3812 | 558 | defer sess.stopRedial() | ||
3813 | 577 | c.Check(ar.stopped, Equals, true) | 559 | c.Check(ar.stopped, Equals, true) |
3814 | 578 | } | 560 | } |
3815 | 579 | 561 | ||
3816 | 580 | func (cs *clientSessionSuite) TestAutoRedialStopsRetrier(c *C) { | 562 | func (cs *clientSessionSuite) TestAutoRedialStopsRetrier(c *C) { |
3817 | 581 | // checks that AutoRedial stops the previous retrier | 563 | // checks that AutoRedial stops the previous retrier |
3819 | 582 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 564 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
3820 | 583 | c.Assert(err, IsNil) | 565 | c.Assert(err, IsNil) |
3822 | 584 | ch := make(chan uint32) | 566 | sess.doneCh = make(chan uint32) |
3823 | 585 | c.Check(sess.retrier, IsNil) | 567 | c.Check(sess.retrier, IsNil) |
3825 | 586 | sess.AutoRedial(ch) | 568 | sess.autoRedial() |
3826 | 587 | c.Assert(sess.retrier, NotNil) | 569 | c.Assert(sess.retrier, NotNil) |
3827 | 588 | sess.retrier.Stop() | 570 | sess.retrier.Stop() |
3829 | 589 | c.Check(<-ch, Not(Equals), 0) | 571 | c.Check(<-sess.doneCh, Not(Equals), 0) |
3830 | 590 | } | 572 | } |
3831 | 591 | 573 | ||
3832 | 592 | func (cs *clientSessionSuite) TestAutoRedialCallsRedialDelay(c *C) { | 574 | func (cs *clientSessionSuite) TestAutoRedialCallsRedialDelay(c *C) { |
3834 | 593 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 575 | // NOTE there are tests that use calling redialDelay as an indication of calling autoRedial! |
3835 | 576 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) | ||
3836 | 594 | c.Assert(err, IsNil) | 577 | c.Assert(err, IsNil) |
3837 | 595 | flag := false | 578 | flag := false |
3840 | 596 | sess.redialDelay = func(sess *ClientSession) time.Duration { flag = true; return 0 } | 579 | sess.redialDelay = func(sess *clientSession) time.Duration { flag = true; return 0 } |
3841 | 597 | sess.AutoRedial(nil) | 580 | sess.autoRedial() |
3842 | 598 | c.Check(flag, Equals, true) | 581 | c.Check(flag, Equals, true) |
3843 | 599 | } | 582 | } |
3844 | 600 | 583 | ||
3845 | 601 | func (cs *clientSessionSuite) TestAutoRedialSetsRedialDelayIfTooQuick(c *C) { | 584 | func (cs *clientSessionSuite) TestAutoRedialSetsRedialDelayIfTooQuick(c *C) { |
3847 | 602 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 585 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
3848 | 603 | c.Assert(err, IsNil) | 586 | c.Assert(err, IsNil) |
3851 | 604 | sess.redialDelay = func(sess *ClientSession) time.Duration { return 0 } | 587 | sess.redialDelay = func(sess *clientSession) time.Duration { return 0 } |
3852 | 605 | sess.AutoRedial(nil) | 588 | sess.autoRedial() |
3853 | 606 | c.Check(sess.ShouldDelay(), Equals, false) | 589 | c.Check(sess.ShouldDelay(), Equals, false) |
3854 | 607 | sess.stopRedial() | 590 | sess.stopRedial() |
3855 | 608 | sess.clearShouldDelay() | 591 | sess.clearShouldDelay() |
3857 | 609 | sess.AutoRedial(nil) | 592 | sess.autoRedial() |
3858 | 610 | c.Check(sess.ShouldDelay(), Equals, true) | 593 | c.Check(sess.ShouldDelay(), Equals, true) |
3859 | 611 | } | 594 | } |
3860 | 612 | 595 | ||
3861 | @@ -615,29 +598,23 @@ | |||
3862 | 615 | ****************************************************************/ | 598 | ****************************************************************/ |
3863 | 616 | 599 | ||
3864 | 617 | type msgSuite struct { | 600 | type msgSuite struct { |
3866 | 618 | sess *ClientSession | 601 | sess *clientSession |
3867 | 619 | upCh chan interface{} | 602 | upCh chan interface{} |
3868 | 620 | downCh chan interface{} | 603 | downCh chan interface{} |
3869 | 621 | errCh chan error | ||
3870 | 622 | } | 604 | } |
3871 | 623 | 605 | ||
3872 | 624 | var _ = Suite(&msgSuite{}) | 606 | var _ = Suite(&msgSuite{}) |
3873 | 625 | 607 | ||
3874 | 626 | func (s *msgSuite) SetUpTest(c *C) { | 608 | func (s *msgSuite) SetUpTest(c *C) { |
3875 | 627 | var err error | 609 | var err error |
3879 | 628 | conf := ClientSessionConfig{ | 610 | conf := dummyConf() |
3880 | 629 | ExchangeTimeout: time.Millisecond, | 611 | conf.ExchangeTimeout = time.Millisecond |
3878 | 630 | } | ||
3881 | 631 | s.sess, err = NewSession("", conf, "wah", seenstate.NewSeenState, helpers.NewTestLogger(c, "debug")) | 612 | s.sess, err = NewSession("", conf, "wah", seenstate.NewSeenState, helpers.NewTestLogger(c, "debug")) |
3882 | 632 | c.Assert(err, IsNil) | 613 | c.Assert(err, IsNil) |
3883 | 633 | s.sess.Connection = &testConn{Name: "TestHandle*"} | 614 | s.sess.Connection = &testConn{Name: "TestHandle*"} |
3884 | 634 | s.errCh = make(chan error, 1) | ||
3885 | 635 | s.upCh = make(chan interface{}, 5) | 615 | s.upCh = make(chan interface{}, 5) |
3886 | 636 | s.downCh = make(chan interface{}, 5) | 616 | s.downCh = make(chan interface{}, 5) |
3887 | 637 | s.sess.proto = &testProtocol{up: s.upCh, down: s.downCh} | 617 | s.sess.proto = &testProtocol{up: s.upCh, down: s.downCh} |
3888 | 638 | // make the message channel buffered | ||
3889 | 639 | s.sess.BroadcastCh = make(chan *BroadcastNotification, 5) | ||
3890 | 640 | s.sess.NotificationsCh = make(chan AddressedNotification, 5) | ||
3891 | 641 | } | 618 | } |
3892 | 642 | 619 | ||
3893 | 643 | func (s *msgSuite) TestHandlePingWorks(c *C) { | 620 | func (s *msgSuite) TestHandlePingWorks(c *C) { |
3894 | @@ -693,10 +670,10 @@ | |||
3895 | 693 | json.RawMessage(`{"img1/m1":[102,"tubular"]}`), | 670 | json.RawMessage(`{"img1/m1":[102,"tubular"]}`), |
3896 | 694 | }, | 671 | }, |
3897 | 695 | } | 672 | } |
3899 | 696 | go func() { s.errCh <- s.sess.handleBroadcast(msg) }() | 673 | go func() { s.sess.errCh <- s.sess.handleBroadcast(msg) }() |
3900 | 697 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) | 674 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) |
3901 | 698 | s.upCh <- nil // ack ok | 675 | s.upCh <- nil // ack ok |
3903 | 699 | c.Check(<-s.errCh, Equals, nil) | 676 | c.Check(<-s.sess.errCh, Equals, nil) |
3904 | 700 | c.Assert(len(s.sess.BroadcastCh), Equals, 1) | 677 | c.Assert(len(s.sess.BroadcastCh), Equals, 1) |
3905 | 701 | c.Check(<-s.sess.BroadcastCh, DeepEquals, &BroadcastNotification{ | 678 | c.Check(<-s.sess.BroadcastCh, DeepEquals, &BroadcastNotification{ |
3906 | 702 | TopLevel: 2, | 679 | TopLevel: 2, |
3907 | @@ -725,11 +702,11 @@ | |||
3908 | 725 | TopLevel: 2, | 702 | TopLevel: 2, |
3909 | 726 | Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)}, | 703 | Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)}, |
3910 | 727 | } | 704 | } |
3912 | 728 | go func() { s.errCh <- s.sess.handleBroadcast(msg) }() | 705 | go func() { s.sess.errCh <- s.sess.handleBroadcast(msg) }() |
3913 | 729 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) | 706 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) |
3914 | 730 | failure := errors.New("ACK ACK ACK") | 707 | failure := errors.New("ACK ACK ACK") |
3915 | 731 | s.upCh <- failure | 708 | s.upCh <- failure |
3917 | 732 | c.Assert(<-s.errCh, Equals, failure) | 709 | c.Assert(<-s.sess.errCh, Equals, failure) |
3918 | 733 | c.Check(s.sess.State(), Equals, Error) | 710 | c.Check(s.sess.State(), Equals, Error) |
3919 | 734 | } | 711 | } |
3920 | 735 | 712 | ||
3921 | @@ -743,10 +720,10 @@ | |||
3922 | 743 | TopLevel: 2, | 720 | TopLevel: 2, |
3923 | 744 | Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)}, | 721 | Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)}, |
3924 | 745 | } | 722 | } |
3926 | 746 | go func() { s.errCh <- s.sess.handleBroadcast(msg) }() | 723 | go func() { s.sess.errCh <- s.sess.handleBroadcast(msg) }() |
3927 | 747 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) | 724 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) |
3928 | 748 | s.upCh <- nil // ack ok | 725 | s.upCh <- nil // ack ok |
3930 | 749 | c.Check(<-s.errCh, IsNil) | 726 | c.Check(<-s.sess.errCh, IsNil) |
3931 | 750 | c.Check(len(s.sess.BroadcastCh), Equals, 0) | 727 | c.Check(len(s.sess.BroadcastCh), Equals, 0) |
3932 | 751 | } | 728 | } |
3933 | 752 | 729 | ||
3934 | @@ -761,10 +738,10 @@ | |||
3935 | 761 | TopLevel: 2, | 738 | TopLevel: 2, |
3936 | 762 | Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)}, | 739 | Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)}, |
3937 | 763 | } | 740 | } |
3939 | 764 | go func() { s.errCh <- s.sess.handleBroadcast(msg) }() | 741 | go func() { s.sess.errCh <- s.sess.handleBroadcast(msg) }() |
3940 | 765 | s.upCh <- nil // ack ok | 742 | s.upCh <- nil // ack ok |
3941 | 766 | // start returns with error | 743 | // start returns with error |
3943 | 767 | c.Check(<-s.errCh, Not(Equals), nil) | 744 | c.Check(<-s.sess.errCh, Not(Equals), nil) |
3944 | 768 | c.Check(s.sess.State(), Equals, Error) | 745 | c.Check(s.sess.State(), Equals, Error) |
3945 | 769 | // no message sent out | 746 | // no message sent out |
3946 | 770 | c.Check(len(s.sess.BroadcastCh), Equals, 0) | 747 | c.Check(len(s.sess.BroadcastCh), Equals, 0) |
3947 | @@ -777,10 +754,10 @@ | |||
3948 | 777 | s.sess.setShouldDelay() | 754 | s.sess.setShouldDelay() |
3949 | 778 | 755 | ||
3950 | 779 | msg := &serverMsg{Type: "broadcast"} | 756 | msg := &serverMsg{Type: "broadcast"} |
3952 | 780 | go func() { s.errCh <- s.sess.handleBroadcast(msg) }() | 757 | go func() { s.sess.errCh <- s.sess.handleBroadcast(msg) }() |
3953 | 781 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) | 758 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) |
3954 | 782 | s.upCh <- nil // ack ok | 759 | s.upCh <- nil // ack ok |
3956 | 783 | c.Check(<-s.errCh, IsNil) | 760 | c.Check(<-s.sess.errCh, IsNil) |
3957 | 784 | 761 | ||
3958 | 785 | c.Check(s.sess.ShouldDelay(), Equals, false) | 762 | c.Check(s.sess.ShouldDelay(), Equals, false) |
3959 | 786 | } | 763 | } |
3960 | @@ -789,10 +766,10 @@ | |||
3961 | 789 | s.sess.setShouldDelay() | 766 | s.sess.setShouldDelay() |
3962 | 790 | 767 | ||
3963 | 791 | msg := &serverMsg{Type: "broadcast"} | 768 | msg := &serverMsg{Type: "broadcast"} |
3965 | 792 | go func() { s.errCh <- s.sess.handleBroadcast(msg) }() | 769 | go func() { s.sess.errCh <- s.sess.handleBroadcast(msg) }() |
3966 | 793 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) | 770 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) |
3967 | 794 | s.upCh <- errors.New("bcast") | 771 | s.upCh <- errors.New("bcast") |
3969 | 795 | c.Check(<-s.errCh, NotNil) | 772 | c.Check(<-s.sess.errCh, NotNil) |
3970 | 796 | 773 | ||
3971 | 797 | c.Check(s.sess.ShouldDelay(), Equals, true) | 774 | c.Check(s.sess.ShouldDelay(), Equals, true) |
3972 | 798 | } | 775 | } |
3973 | @@ -842,10 +819,10 @@ | |||
3974 | 842 | msg.NotificationsMsg = protocol.NotificationsMsg{ | 819 | msg.NotificationsMsg = protocol.NotificationsMsg{ |
3975 | 843 | Notifications: []protocol.Notification{n1, n2}, | 820 | Notifications: []protocol.Notification{n1, n2}, |
3976 | 844 | } | 821 | } |
3978 | 845 | go func() { s.errCh <- s.sess.handleNotifications(msg) }() | 822 | go func() { s.sess.errCh <- s.sess.handleNotifications(msg) }() |
3979 | 846 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) | 823 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) |
3980 | 847 | s.upCh <- nil // ack ok | 824 | s.upCh <- nil // ack ok |
3982 | 848 | c.Check(<-s.errCh, Equals, nil) | 825 | c.Check(<-s.sess.errCh, Equals, nil) |
3983 | 849 | c.Check(s.sess.ShouldDelay(), Equals, false) | 826 | c.Check(s.sess.ShouldDelay(), Equals, false) |
3984 | 850 | c.Assert(s.sess.NotificationsCh, HasLen, 2) | 827 | c.Assert(s.sess.NotificationsCh, HasLen, 2) |
3985 | 851 | app1, err := click.ParseAppId("com.example.app1_app1") | 828 | app1, err := click.ParseAppId("com.example.app1_app1") |
3986 | @@ -888,10 +865,10 @@ | |||
3987 | 888 | msg.NotificationsMsg = protocol.NotificationsMsg{ | 865 | msg.NotificationsMsg = protocol.NotificationsMsg{ |
3988 | 889 | Notifications: []protocol.Notification{n1, n2}, | 866 | Notifications: []protocol.Notification{n1, n2}, |
3989 | 890 | } | 867 | } |
3991 | 891 | go func() { s.errCh <- s.sess.handleNotifications(msg) }() | 868 | go func() { s.sess.errCh <- s.sess.handleNotifications(msg) }() |
3992 | 892 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) | 869 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) |
3993 | 893 | s.upCh <- nil // ack ok | 870 | s.upCh <- nil // ack ok |
3995 | 894 | c.Check(<-s.errCh, Equals, nil) | 871 | c.Check(<-s.sess.errCh, Equals, nil) |
3996 | 895 | c.Check(s.sess.ShouldDelay(), Equals, false) | 872 | c.Check(s.sess.ShouldDelay(), Equals, false) |
3997 | 896 | c.Assert(s.sess.NotificationsCh, HasLen, 1) | 873 | c.Assert(s.sess.NotificationsCh, HasLen, 1) |
3998 | 897 | app2, err := click.ParseAppId("com.example.app2_app2") | 874 | app2, err := click.ParseAppId("com.example.app2_app2") |
3999 | @@ -923,10 +900,10 @@ | |||
4000 | 923 | msg.NotificationsMsg = protocol.NotificationsMsg{ | 900 | msg.NotificationsMsg = protocol.NotificationsMsg{ |
4001 | 924 | Notifications: []protocol.Notification{n1, n2}, | 901 | Notifications: []protocol.Notification{n1, n2}, |
4002 | 925 | } | 902 | } |
4004 | 926 | go func() { s.errCh <- s.sess.handleNotifications(msg) }() | 903 | go func() { s.sess.errCh <- s.sess.handleNotifications(msg) }() |
4005 | 927 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) | 904 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) |
4006 | 928 | s.upCh <- nil // ack ok | 905 | s.upCh <- nil // ack ok |
4008 | 929 | c.Check(<-s.errCh, Equals, nil) | 906 | c.Check(<-s.sess.errCh, Equals, nil) |
4009 | 930 | c.Assert(s.sess.NotificationsCh, HasLen, 2) | 907 | c.Assert(s.sess.NotificationsCh, HasLen, 2) |
4010 | 931 | app1, err := click.ParseAppId("com.example.app1_app1") | 908 | app1, err := click.ParseAppId("com.example.app1_app1") |
4011 | 932 | c.Assert(err, IsNil) | 909 | c.Assert(err, IsNil) |
4012 | @@ -943,10 +920,10 @@ | |||
4013 | 943 | c.Check(ac.ops, HasLen, 3) | 920 | c.Check(ac.ops, HasLen, 3) |
4014 | 944 | 921 | ||
4015 | 945 | // second time they get ignored | 922 | // second time they get ignored |
4017 | 946 | go func() { s.errCh <- s.sess.handleNotifications(msg) }() | 923 | go func() { s.sess.errCh <- s.sess.handleNotifications(msg) }() |
4018 | 947 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) | 924 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) |
4019 | 948 | s.upCh <- nil // ack ok | 925 | s.upCh <- nil // ack ok |
4021 | 949 | c.Check(<-s.errCh, Equals, nil) | 926 | c.Check(<-s.sess.errCh, Equals, nil) |
4022 | 950 | c.Assert(s.sess.NotificationsCh, HasLen, 0) | 927 | c.Assert(s.sess.NotificationsCh, HasLen, 0) |
4023 | 951 | c.Check(ac.ops, HasLen, 4) | 928 | c.Check(ac.ops, HasLen, 4) |
4024 | 952 | } | 929 | } |
4025 | @@ -963,11 +940,11 @@ | |||
4026 | 963 | msg.NotificationsMsg = protocol.NotificationsMsg{ | 940 | msg.NotificationsMsg = protocol.NotificationsMsg{ |
4027 | 964 | Notifications: []protocol.Notification{n1}, | 941 | Notifications: []protocol.Notification{n1}, |
4028 | 965 | } | 942 | } |
4030 | 966 | go func() { s.errCh <- s.sess.handleNotifications(msg) }() | 943 | go func() { s.sess.errCh <- s.sess.handleNotifications(msg) }() |
4031 | 967 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) | 944 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) |
4032 | 968 | failure := errors.New("ACK ACK ACK") | 945 | failure := errors.New("ACK ACK ACK") |
4033 | 969 | s.upCh <- failure | 946 | s.upCh <- failure |
4035 | 970 | c.Assert(<-s.errCh, Equals, failure) | 947 | c.Assert(<-s.sess.errCh, Equals, failure) |
4036 | 971 | c.Check(s.sess.State(), Equals, Error) | 948 | c.Check(s.sess.State(), Equals, Error) |
4037 | 972 | // didn't get to clear | 949 | // didn't get to clear |
4038 | 973 | c.Check(s.sess.ShouldDelay(), Equals, true) | 950 | c.Check(s.sess.ShouldDelay(), Equals, true) |
4039 | @@ -986,10 +963,10 @@ | |||
4040 | 986 | msg.NotificationsMsg = protocol.NotificationsMsg{ | 963 | msg.NotificationsMsg = protocol.NotificationsMsg{ |
4041 | 987 | Notifications: []protocol.Notification{n1}, | 964 | Notifications: []protocol.Notification{n1}, |
4042 | 988 | } | 965 | } |
4044 | 989 | go func() { s.errCh <- s.sess.handleNotifications(msg) }() | 966 | go func() { s.sess.errCh <- s.sess.handleNotifications(msg) }() |
4045 | 990 | s.upCh <- nil // ack ok | 967 | s.upCh <- nil // ack ok |
4046 | 991 | // start returns with error | 968 | // start returns with error |
4048 | 992 | c.Check(<-s.errCh, Not(Equals), nil) | 969 | c.Check(<-s.sess.errCh, Not(Equals), nil) |
4049 | 993 | c.Check(s.sess.State(), Equals, Error) | 970 | c.Check(s.sess.State(), Equals, Error) |
4050 | 994 | // no message sent out | 971 | // no message sent out |
4051 | 995 | c.Check(len(s.sess.NotificationsCh), Equals, 0) | 972 | c.Check(len(s.sess.NotificationsCh), Equals, 0) |
4052 | @@ -1010,8 +987,8 @@ | |||
4053 | 1010 | msg.ConnBrokenMsg = protocol.ConnBrokenMsg{ | 987 | msg.ConnBrokenMsg = protocol.ConnBrokenMsg{ |
4054 | 1011 | Reason: "REASON", | 988 | Reason: "REASON", |
4055 | 1012 | } | 989 | } |
4058 | 1013 | go func() { s.errCh <- s.sess.handleConnBroken(msg) }() | 990 | go func() { s.sess.errCh <- s.sess.handleConnBroken(msg) }() |
4059 | 1014 | c.Check(<-s.errCh, ErrorMatches, "server broke connection: REASON") | 991 | c.Check(<-s.sess.errCh, ErrorMatches, "server broke connection: REASON") |
4060 | 1015 | c.Check(s.sess.State(), Equals, Error) | 992 | c.Check(s.sess.State(), Equals, Error) |
4061 | 1016 | } | 993 | } |
4062 | 1017 | 994 | ||
4063 | @@ -1022,8 +999,8 @@ | |||
4064 | 1022 | Reason: protocol.BrokenHostMismatch, | 999 | Reason: protocol.BrokenHostMismatch, |
4065 | 1023 | } | 1000 | } |
4066 | 1024 | s.sess.deliveryHosts = []string{"foo:443", "bar:443"} | 1001 | s.sess.deliveryHosts = []string{"foo:443", "bar:443"} |
4069 | 1025 | go func() { s.errCh <- s.sess.handleConnBroken(msg) }() | 1002 | go func() { s.sess.errCh <- s.sess.handleConnBroken(msg) }() |
4070 | 1026 | c.Check(<-s.errCh, ErrorMatches, "server broke connection: host-mismatch") | 1003 | c.Check(<-s.sess.errCh, ErrorMatches, "server broke connection: host-mismatch") |
4071 | 1027 | c.Check(s.sess.State(), Equals, Error) | 1004 | c.Check(s.sess.State(), Equals, Error) |
4072 | 1028 | // hosts were reset | 1005 | // hosts were reset |
4073 | 1029 | c.Check(s.sess.deliveryHosts, IsNil) | 1006 | c.Check(s.sess.deliveryHosts, IsNil) |
4074 | @@ -1041,14 +1018,14 @@ | |||
4075 | 1041 | (*msgSuite)(s).SetUpTest(c) | 1018 | (*msgSuite)(s).SetUpTest(c) |
4076 | 1042 | s.sess.Connection.(*testConn).Name = "TestLoop*" | 1019 | s.sess.Connection.(*testConn).Name = "TestLoop*" |
4077 | 1043 | go func() { | 1020 | go func() { |
4079 | 1044 | s.errCh <- s.sess.loop() | 1021 | s.sess.errCh <- s.sess.loop() |
4080 | 1045 | }() | 1022 | }() |
4081 | 1046 | } | 1023 | } |
4082 | 1047 | 1024 | ||
4083 | 1048 | func (s *loopSuite) TestLoopReadError(c *C) { | 1025 | func (s *loopSuite) TestLoopReadError(c *C) { |
4084 | 1049 | c.Check(s.sess.State(), Equals, Running) | 1026 | c.Check(s.sess.State(), Equals, Running) |
4085 | 1050 | s.upCh <- errors.New("Read") | 1027 | s.upCh <- errors.New("Read") |
4087 | 1051 | err := <-s.errCh | 1028 | err := <-s.sess.errCh |
4088 | 1052 | c.Check(err, ErrorMatches, "Read") | 1029 | c.Check(err, ErrorMatches, "Read") |
4089 | 1053 | c.Check(s.sess.State(), Equals, Error) | 1030 | c.Check(s.sess.State(), Equals, Error) |
4090 | 1054 | } | 1031 | } |
4091 | @@ -1060,7 +1037,7 @@ | |||
4092 | 1060 | c.Check(takeNext(s.downCh), Equals, protocol.PingPongMsg{Type: "pong"}) | 1037 | c.Check(takeNext(s.downCh), Equals, protocol.PingPongMsg{Type: "pong"}) |
4093 | 1061 | failure := errors.New("pong") | 1038 | failure := errors.New("pong") |
4094 | 1062 | s.upCh <- failure | 1039 | s.upCh <- failure |
4096 | 1063 | c.Check(<-s.errCh, Equals, failure) | 1040 | c.Check(<-s.sess.errCh, Equals, failure) |
4097 | 1064 | } | 1041 | } |
4098 | 1065 | 1042 | ||
4099 | 1066 | func (s *loopSuite) TestLoopLoopsDaLoop(c *C) { | 1043 | func (s *loopSuite) TestLoopLoopsDaLoop(c *C) { |
4100 | @@ -1073,7 +1050,7 @@ | |||
4101 | 1073 | } | 1050 | } |
4102 | 1074 | failure := errors.New("pong") | 1051 | failure := errors.New("pong") |
4103 | 1075 | s.upCh <- failure | 1052 | s.upCh <- failure |
4105 | 1076 | c.Check(<-s.errCh, Equals, failure) | 1053 | c.Check(<-s.sess.errCh, Equals, failure) |
4106 | 1077 | } | 1054 | } |
4107 | 1078 | 1055 | ||
4108 | 1079 | func (s *loopSuite) TestLoopBroadcast(c *C) { | 1056 | func (s *loopSuite) TestLoopBroadcast(c *C) { |
4109 | @@ -1090,7 +1067,7 @@ | |||
4110 | 1090 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) | 1067 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) |
4111 | 1091 | failure := errors.New("ack") | 1068 | failure := errors.New("ack") |
4112 | 1092 | s.upCh <- failure | 1069 | s.upCh <- failure |
4114 | 1093 | c.Check(<-s.errCh, Equals, failure) | 1070 | c.Check(<-s.sess.errCh, Equals, failure) |
4115 | 1094 | } | 1071 | } |
4116 | 1095 | 1072 | ||
4117 | 1096 | func (s *loopSuite) TestLoopNotifications(c *C) { | 1073 | func (s *loopSuite) TestLoopNotifications(c *C) { |
4118 | @@ -1110,7 +1087,7 @@ | |||
4119 | 1110 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) | 1087 | c.Check(takeNext(s.downCh), Equals, protocol.AckMsg{"ack"}) |
4120 | 1111 | failure := errors.New("ack") | 1088 | failure := errors.New("ack") |
4121 | 1112 | s.upCh <- failure | 1089 | s.upCh <- failure |
4123 | 1113 | c.Check(<-s.errCh, Equals, failure) | 1090 | c.Check(<-s.sess.errCh, Equals, failure) |
4124 | 1114 | } | 1091 | } |
4125 | 1115 | 1092 | ||
4126 | 1116 | func (s *loopSuite) TestLoopSetParams(c *C) { | 1093 | func (s *loopSuite) TestLoopSetParams(c *C) { |
4127 | @@ -1123,7 +1100,7 @@ | |||
4128 | 1123 | s.upCh <- setParams | 1100 | s.upCh <- setParams |
4129 | 1124 | failure := errors.New("fail") | 1101 | failure := errors.New("fail") |
4130 | 1125 | s.upCh <- failure | 1102 | s.upCh <- failure |
4132 | 1126 | c.Assert(<-s.errCh, Equals, failure) | 1103 | c.Assert(<-s.sess.errCh, Equals, failure) |
4133 | 1127 | c.Check(s.sess.getCookie(), Equals, "COOKIE") | 1104 | c.Check(s.sess.getCookie(), Equals, "COOKIE") |
4134 | 1128 | } | 1105 | } |
4135 | 1129 | 1106 | ||
4136 | @@ -1135,7 +1112,7 @@ | |||
4137 | 1135 | } | 1112 | } |
4138 | 1136 | c.Check(takeNext(s.downCh), Equals, "deadline 1ms") | 1113 | c.Check(takeNext(s.downCh), Equals, "deadline 1ms") |
4139 | 1137 | s.upCh <- broken | 1114 | s.upCh <- broken |
4141 | 1138 | c.Check(<-s.errCh, NotNil) | 1115 | c.Check(<-s.sess.errCh, NotNil) |
4142 | 1139 | } | 1116 | } |
4143 | 1140 | 1117 | ||
4144 | 1141 | func (s *loopSuite) TestLoopConnWarn(c *C) { | 1118 | func (s *loopSuite) TestLoopConnWarn(c *C) { |
4145 | @@ -1156,7 +1133,7 @@ | |||
4146 | 1156 | s.upCh <- warn | 1133 | s.upCh <- warn |
4147 | 1157 | s.upCh <- connwarn | 1134 | s.upCh <- connwarn |
4148 | 1158 | s.upCh <- failure | 1135 | s.upCh <- failure |
4150 | 1159 | c.Check(<-s.errCh, Equals, failure) | 1136 | c.Check(<-s.sess.errCh, Equals, failure) |
4151 | 1160 | c.Check(log.Captured(), | 1137 | c.Check(log.Captured(), |
4152 | 1161 | Matches, `(?ms).* warning: XXX$.*`) | 1138 | Matches, `(?ms).* warning: XXX$.*`) |
4153 | 1162 | c.Check(log.Captured(), | 1139 | c.Check(log.Captured(), |
4154 | @@ -1167,7 +1144,7 @@ | |||
4155 | 1167 | start() tests | 1144 | start() tests |
4156 | 1168 | ****************************************************************/ | 1145 | ****************************************************************/ |
4157 | 1169 | func (cs *clientSessionSuite) TestStartFailsIfSetDeadlineFails(c *C) { | 1146 | func (cs *clientSessionSuite) TestStartFailsIfSetDeadlineFails(c *C) { |
4159 | 1170 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 1147 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
4160 | 1171 | c.Assert(err, IsNil) | 1148 | c.Assert(err, IsNil) |
4161 | 1172 | sess.Connection = &testConn{Name: "TestStartFailsIfSetDeadlineFails", | 1149 | sess.Connection = &testConn{Name: "TestStartFailsIfSetDeadlineFails", |
4162 | 1173 | DeadlineCondition: condition.Work(false)} // setdeadline will fail | 1150 | DeadlineCondition: condition.Work(false)} // setdeadline will fail |
4163 | @@ -1177,7 +1154,7 @@ | |||
4164 | 1177 | } | 1154 | } |
4165 | 1178 | 1155 | ||
4166 | 1179 | func (cs *clientSessionSuite) TestStartFailsIfWriteFails(c *C) { | 1156 | func (cs *clientSessionSuite) TestStartFailsIfWriteFails(c *C) { |
4168 | 1180 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 1157 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
4169 | 1181 | c.Assert(err, IsNil) | 1158 | c.Assert(err, IsNil) |
4170 | 1182 | sess.Connection = &testConn{Name: "TestStartFailsIfWriteFails", | 1159 | sess.Connection = &testConn{Name: "TestStartFailsIfWriteFails", |
4171 | 1183 | WriteCondition: condition.Work(false)} // write will fail | 1160 | WriteCondition: condition.Work(false)} // write will fail |
4172 | @@ -1187,7 +1164,7 @@ | |||
4173 | 1187 | } | 1164 | } |
4174 | 1188 | 1165 | ||
4175 | 1189 | func (cs *clientSessionSuite) TestStartFailsIfGetLevelsFails(c *C) { | 1166 | func (cs *clientSessionSuite) TestStartFailsIfGetLevelsFails(c *C) { |
4177 | 1190 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 1167 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
4178 | 1191 | c.Assert(err, IsNil) | 1168 | c.Assert(err, IsNil) |
4179 | 1192 | sess.SeenState = &brokenSeenState{} | 1169 | sess.SeenState = &brokenSeenState{} |
4180 | 1193 | sess.Connection = &testConn{Name: "TestStartConnectMessageFails"} | 1170 | sess.Connection = &testConn{Name: "TestStartConnectMessageFails"} |
4181 | @@ -1207,7 +1184,7 @@ | |||
4182 | 1207 | } | 1184 | } |
4183 | 1208 | 1185 | ||
4184 | 1209 | func (cs *clientSessionSuite) TestStartConnectMessageFails(c *C) { | 1186 | func (cs *clientSessionSuite) TestStartConnectMessageFails(c *C) { |
4186 | 1210 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 1187 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
4187 | 1211 | c.Assert(err, IsNil) | 1188 | c.Assert(err, IsNil) |
4188 | 1212 | sess.Connection = &testConn{Name: "TestStartConnectMessageFails"} | 1189 | sess.Connection = &testConn{Name: "TestStartConnectMessageFails"} |
4189 | 1213 | errCh := make(chan error, 1) | 1190 | errCh := make(chan error, 1) |
4190 | @@ -1234,7 +1211,7 @@ | |||
4191 | 1234 | } | 1211 | } |
4192 | 1235 | 1212 | ||
4193 | 1236 | func (cs *clientSessionSuite) TestStartConnackReadError(c *C) { | 1213 | func (cs *clientSessionSuite) TestStartConnackReadError(c *C) { |
4195 | 1237 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 1214 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
4196 | 1238 | c.Assert(err, IsNil) | 1215 | c.Assert(err, IsNil) |
4197 | 1239 | sess.Connection = &testConn{Name: "TestStartConnackReadError"} | 1216 | sess.Connection = &testConn{Name: "TestStartConnackReadError"} |
4198 | 1240 | errCh := make(chan error, 1) | 1217 | errCh := make(chan error, 1) |
4199 | @@ -1258,7 +1235,7 @@ | |||
4200 | 1258 | } | 1235 | } |
4201 | 1259 | 1236 | ||
4202 | 1260 | func (cs *clientSessionSuite) TestStartBadConnack(c *C) { | 1237 | func (cs *clientSessionSuite) TestStartBadConnack(c *C) { |
4204 | 1261 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 1238 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
4205 | 1262 | c.Assert(err, IsNil) | 1239 | c.Assert(err, IsNil) |
4206 | 1263 | sess.Connection = &testConn{Name: "TestStartBadConnack"} | 1240 | sess.Connection = &testConn{Name: "TestStartBadConnack"} |
4207 | 1264 | errCh := make(chan error, 1) | 1241 | errCh := make(chan error, 1) |
4208 | @@ -1282,7 +1259,7 @@ | |||
4209 | 1282 | } | 1259 | } |
4210 | 1283 | 1260 | ||
4211 | 1284 | func (cs *clientSessionSuite) TestStartNotConnack(c *C) { | 1261 | func (cs *clientSessionSuite) TestStartNotConnack(c *C) { |
4213 | 1285 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 1262 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
4214 | 1286 | c.Assert(err, IsNil) | 1263 | c.Assert(err, IsNil) |
4215 | 1287 | sess.Connection = &testConn{Name: "TestStartBadConnack"} | 1264 | sess.Connection = &testConn{Name: "TestStartBadConnack"} |
4216 | 1288 | errCh := make(chan error, 1) | 1265 | errCh := make(chan error, 1) |
4217 | @@ -1349,13 +1326,31 @@ | |||
4218 | 1349 | run() tests | 1326 | run() tests |
4219 | 1350 | ****************************************************************/ | 1327 | ****************************************************************/ |
4220 | 1351 | 1328 | ||
4221 | 1329 | func (cs *clientSessionSuite) TestRunCallsCloserWithFalse(c *C) { | ||
4222 | 1330 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) | ||
4223 | 1331 | c.Assert(err, IsNil) | ||
4224 | 1332 | failure := errors.New("bail") | ||
4225 | 1333 | has_closed := false | ||
4226 | 1334 | with_false := false | ||
4227 | 1335 | err = sess.run( | ||
4228 | 1336 | func(b bool) { has_closed = true; with_false = !b }, | ||
4229 | 1337 | func() error { return failure }, | ||
4230 | 1338 | nil, | ||
4231 | 1339 | nil, | ||
4232 | 1340 | nil, | ||
4233 | 1341 | nil) | ||
4234 | 1342 | c.Check(err, Equals, failure) | ||
4235 | 1343 | c.Check(has_closed, Equals, true) | ||
4236 | 1344 | c.Check(with_false, Equals, true) | ||
4237 | 1345 | } | ||
4238 | 1346 | |||
4239 | 1352 | func (cs *clientSessionSuite) TestRunBailsIfAuthCheckFails(c *C) { | 1347 | func (cs *clientSessionSuite) TestRunBailsIfAuthCheckFails(c *C) { |
4241 | 1353 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 1348 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
4242 | 1354 | c.Assert(err, IsNil) | 1349 | c.Assert(err, IsNil) |
4243 | 1355 | failure := errors.New("TestRunBailsIfAuthCheckFails") | 1350 | failure := errors.New("TestRunBailsIfAuthCheckFails") |
4244 | 1356 | has_closed := false | 1351 | has_closed := false |
4245 | 1357 | err = sess.run( | 1352 | err = sess.run( |
4247 | 1358 | func() { has_closed = true }, | 1353 | func(bool) { has_closed = true }, |
4248 | 1359 | func() error { return failure }, | 1354 | func() error { return failure }, |
4249 | 1360 | nil, | 1355 | nil, |
4250 | 1361 | nil, | 1356 | nil, |
4251 | @@ -1366,12 +1361,12 @@ | |||
4252 | 1366 | } | 1361 | } |
4253 | 1367 | 1362 | ||
4254 | 1368 | func (cs *clientSessionSuite) TestRunBailsIfHostGetterFails(c *C) { | 1363 | func (cs *clientSessionSuite) TestRunBailsIfHostGetterFails(c *C) { |
4256 | 1369 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 1364 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
4257 | 1370 | c.Assert(err, IsNil) | 1365 | c.Assert(err, IsNil) |
4258 | 1371 | failure := errors.New("TestRunBailsIfHostGetterFails") | 1366 | failure := errors.New("TestRunBailsIfHostGetterFails") |
4259 | 1372 | has_closed := false | 1367 | has_closed := false |
4260 | 1373 | err = sess.run( | 1368 | err = sess.run( |
4262 | 1374 | func() { has_closed = true }, | 1369 | func(bool) { has_closed = true }, |
4263 | 1375 | func() error { return nil }, | 1370 | func() error { return nil }, |
4264 | 1376 | func() error { return failure }, | 1371 | func() error { return failure }, |
4265 | 1377 | nil, | 1372 | nil, |
4266 | @@ -1382,11 +1377,11 @@ | |||
4267 | 1382 | } | 1377 | } |
4268 | 1383 | 1378 | ||
4269 | 1384 | func (cs *clientSessionSuite) TestRunBailsIfConnectFails(c *C) { | 1379 | func (cs *clientSessionSuite) TestRunBailsIfConnectFails(c *C) { |
4271 | 1385 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 1380 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
4272 | 1386 | c.Assert(err, IsNil) | 1381 | c.Assert(err, IsNil) |
4273 | 1387 | failure := errors.New("TestRunBailsIfConnectFails") | 1382 | failure := errors.New("TestRunBailsIfConnectFails") |
4274 | 1388 | err = sess.run( | 1383 | err = sess.run( |
4276 | 1389 | func() {}, | 1384 | func(bool) {}, |
4277 | 1390 | func() error { return nil }, | 1385 | func() error { return nil }, |
4278 | 1391 | func() error { return nil }, | 1386 | func() error { return nil }, |
4279 | 1392 | func() error { return failure }, | 1387 | func() error { return failure }, |
4280 | @@ -1396,11 +1391,11 @@ | |||
4281 | 1396 | } | 1391 | } |
4282 | 1397 | 1392 | ||
4283 | 1398 | func (cs *clientSessionSuite) TestRunBailsIfStartFails(c *C) { | 1393 | func (cs *clientSessionSuite) TestRunBailsIfStartFails(c *C) { |
4285 | 1399 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 1394 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
4286 | 1400 | c.Assert(err, IsNil) | 1395 | c.Assert(err, IsNil) |
4287 | 1401 | failure := errors.New("TestRunBailsIfStartFails") | 1396 | failure := errors.New("TestRunBailsIfStartFails") |
4288 | 1402 | err = sess.run( | 1397 | err = sess.run( |
4290 | 1403 | func() {}, | 1398 | func(bool) {}, |
4291 | 1404 | func() error { return nil }, | 1399 | func() error { return nil }, |
4292 | 1405 | func() error { return nil }, | 1400 | func() error { return nil }, |
4293 | 1406 | func() error { return nil }, | 1401 | func() error { return nil }, |
4294 | @@ -1410,16 +1405,12 @@ | |||
4295 | 1410 | } | 1405 | } |
4296 | 1411 | 1406 | ||
4297 | 1412 | func (cs *clientSessionSuite) TestRunRunsEvenIfLoopFails(c *C) { | 1407 | func (cs *clientSessionSuite) TestRunRunsEvenIfLoopFails(c *C) { |
4299 | 1413 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 1408 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
4300 | 1414 | c.Assert(err, IsNil) | 1409 | c.Assert(err, IsNil) |
4301 | 1415 | // just to make a point: until here we haven't set ErrCh & BroadcastCh (no | ||
4302 | 1416 | // biggie if this stops being true) | ||
4303 | 1417 | c.Check(sess.ErrCh, IsNil) | ||
4304 | 1418 | c.Check(sess.BroadcastCh, IsNil) | ||
4305 | 1419 | failureCh := make(chan error) // must be unbuffered | 1410 | failureCh := make(chan error) // must be unbuffered |
4306 | 1420 | notf := &BroadcastNotification{} | 1411 | notf := &BroadcastNotification{} |
4307 | 1421 | err = sess.run( | 1412 | err = sess.run( |
4309 | 1422 | func() {}, | 1413 | func(bool) {}, |
4310 | 1423 | func() error { return nil }, | 1414 | func() error { return nil }, |
4311 | 1424 | func() error { return nil }, | 1415 | func() error { return nil }, |
4312 | 1425 | func() error { return nil }, | 1416 | func() error { return nil }, |
4313 | @@ -1427,12 +1418,12 @@ | |||
4314 | 1427 | func() error { sess.BroadcastCh <- notf; return <-failureCh }) | 1418 | func() error { sess.BroadcastCh <- notf; return <-failureCh }) |
4315 | 1428 | c.Check(err, Equals, nil) | 1419 | c.Check(err, Equals, nil) |
4316 | 1429 | // if run doesn't error it sets up the channels | 1420 | // if run doesn't error it sets up the channels |
4318 | 1430 | c.Assert(sess.ErrCh, NotNil) | 1421 | c.Assert(sess.errCh, NotNil) |
4319 | 1431 | c.Assert(sess.BroadcastCh, NotNil) | 1422 | c.Assert(sess.BroadcastCh, NotNil) |
4320 | 1432 | c.Check(<-sess.BroadcastCh, Equals, notf) | 1423 | c.Check(<-sess.BroadcastCh, Equals, notf) |
4321 | 1433 | failure := errors.New("TestRunRunsEvenIfLoopFails") | 1424 | failure := errors.New("TestRunRunsEvenIfLoopFails") |
4322 | 1434 | failureCh <- failure | 1425 | failureCh <- failure |
4324 | 1435 | c.Check(<-sess.ErrCh, Equals, failure) | 1426 | c.Check(<-sess.errCh, Equals, failure) |
4325 | 1436 | // so now you know it was running in a goroutine :) | 1427 | // so now you know it was running in a goroutine :) |
4326 | 1437 | } | 1428 | } |
4327 | 1438 | 1429 | ||
4328 | @@ -1441,7 +1432,7 @@ | |||
4329 | 1441 | ****************************************************************/ | 1432 | ****************************************************************/ |
4330 | 1442 | 1433 | ||
4331 | 1443 | func (cs *clientSessionSuite) TestJitter(c *C) { | 1434 | func (cs *clientSessionSuite) TestJitter(c *C) { |
4333 | 1444 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 1435 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
4334 | 1445 | c.Assert(err, IsNil) | 1436 | c.Assert(err, IsNil) |
4335 | 1446 | num_tries := 20 // should do the math | 1437 | num_tries := 20 // should do the math |
4336 | 1447 | spread := time.Second // | 1438 | spread := time.Second // |
4337 | @@ -1473,20 +1464,23 @@ | |||
4338 | 1473 | 1464 | ||
4339 | 1474 | func (cs *clientSessionSuite) TestDialPanics(c *C) { | 1465 | func (cs *clientSessionSuite) TestDialPanics(c *C) { |
4340 | 1475 | // one last unhappy test | 1466 | // one last unhappy test |
4342 | 1476 | sess, err := NewSession("", dummyConf, "wah", cs.lvls, cs.log) | 1467 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) |
4343 | 1477 | c.Assert(err, IsNil) | 1468 | c.Assert(err, IsNil) |
4344 | 1478 | sess.Protocolator = nil | 1469 | sess.Protocolator = nil |
4345 | 1479 | c.Check(sess.Dial, PanicMatches, ".*protocol constructor.") | 1470 | c.Check(sess.Dial, PanicMatches, ".*protocol constructor.") |
4346 | 1480 | } | 1471 | } |
4347 | 1481 | 1472 | ||
4348 | 1482 | var ( | 1473 | var ( |
4354 | 1483 | dialTestTimeout = 100 * time.Millisecond | 1474 | dialTestTimeout = 300 * time.Millisecond |
4350 | 1484 | dialTestConf = ClientSessionConfig{ | ||
4351 | 1485 | ExchangeTimeout: dialTestTimeout, | ||
4352 | 1486 | PEM: helpers.TestCertPEMBlock, | ||
4353 | 1487 | } | ||
4355 | 1488 | ) | 1475 | ) |
4356 | 1489 | 1476 | ||
4357 | 1477 | func dialTestConf() ClientSessionConfig { | ||
4358 | 1478 | conf := dummyConf() | ||
4359 | 1479 | conf.ExchangeTimeout = dialTestTimeout | ||
4360 | 1480 | conf.PEM = helpers.TestCertPEMBlock | ||
4361 | 1481 | return conf | ||
4362 | 1482 | } | ||
4363 | 1483 | |||
4364 | 1490 | func (cs *clientSessionSuite) TestDialBadServerName(c *C) { | 1484 | func (cs *clientSessionSuite) TestDialBadServerName(c *C) { |
4365 | 1491 | // a borked server name | 1485 | // a borked server name |
4366 | 1492 | lst, err := tls.Listen("tcp", "localhost:0", helpers.TestTLSServerConfig) | 1486 | lst, err := tls.Listen("tcp", "localhost:0", helpers.TestTLSServerConfig) |
4367 | @@ -1505,7 +1499,7 @@ | |||
4368 | 1505 | })) | 1499 | })) |
4369 | 1506 | defer ts.Close() | 1500 | defer ts.Close() |
4370 | 1507 | 1501 | ||
4372 | 1508 | sess, err := NewSession(ts.URL, dialTestConf, "wah", cs.lvls, cs.log) | 1502 | sess, err := NewSession(ts.URL, dialTestConf(), "wah", cs.lvls, cs.log) |
4373 | 1509 | c.Assert(err, IsNil) | 1503 | c.Assert(err, IsNil) |
4374 | 1510 | tconn := &testConn{} | 1504 | tconn := &testConn{} |
4375 | 1511 | sess.Connection = tconn | 1505 | sess.Connection = tconn |
4376 | @@ -1550,7 +1544,7 @@ | |||
4377 | 1550 | })) | 1544 | })) |
4378 | 1551 | defer ts.Close() | 1545 | defer ts.Close() |
4379 | 1552 | 1546 | ||
4381 | 1553 | sess, err := NewSession(ts.URL, dialTestConf, "wah", cs.lvls, cs.log) | 1547 | sess, err := NewSession(ts.URL, dialTestConf(), "wah", cs.lvls, cs.log) |
4382 | 1554 | c.Assert(err, IsNil) | 1548 | c.Assert(err, IsNil) |
4383 | 1555 | tconn := &testConn{CloseCondition: condition.Fail2Work(10)} | 1549 | tconn := &testConn{CloseCondition: condition.Fail2Work(10)} |
4384 | 1556 | sess.Connection = tconn | 1550 | sess.Connection = tconn |
4385 | @@ -1584,7 +1578,7 @@ | |||
4386 | 1584 | 1578 | ||
4387 | 1585 | // 2. "connect" (but on the fake protcol above! woo) | 1579 | // 2. "connect" (but on the fake protcol above! woo) |
4388 | 1586 | 1580 | ||
4390 | 1587 | c.Check(takeNext(downCh), Equals, "deadline 100ms") | 1581 | c.Check(takeNext(downCh), Equals, fmt.Sprintf("deadline %v", dialTestTimeout)) |
4391 | 1588 | _, ok := takeNext(downCh).(protocol.ConnectMsg) | 1582 | _, ok := takeNext(downCh).(protocol.ConnectMsg) |
4392 | 1589 | c.Check(ok, Equals, true) | 1583 | c.Check(ok, Equals, true) |
4393 | 1590 | upCh <- nil // no error | 1584 | upCh <- nil // no error |
4394 | @@ -1597,7 +1591,7 @@ | |||
4395 | 1597 | // 3. "loop" | 1591 | // 3. "loop" |
4396 | 1598 | 1592 | ||
4397 | 1599 | // ping works, | 1593 | // ping works, |
4399 | 1600 | c.Check(takeNext(downCh), Equals, "deadline 110ms") | 1594 | c.Check(takeNext(downCh), Equals, fmt.Sprintf("deadline %v", dialTestTimeout+10*time.Millisecond)) |
4400 | 1601 | upCh <- protocol.PingPongMsg{Type: "ping"} | 1595 | upCh <- protocol.PingPongMsg{Type: "ping"} |
4401 | 1602 | c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"}) | 1596 | c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"}) |
4402 | 1603 | upCh <- nil | 1597 | upCh <- nil |
4403 | @@ -1613,7 +1607,7 @@ | |||
4404 | 1613 | TopLevel: 2, | 1607 | TopLevel: 2, |
4405 | 1614 | Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)}, | 1608 | Payloads: []json.RawMessage{json.RawMessage(`{"b":1}`)}, |
4406 | 1615 | } | 1609 | } |
4408 | 1616 | c.Check(takeNext(downCh), Equals, "deadline 110ms") | 1610 | c.Check(takeNext(downCh), Equals, fmt.Sprintf("deadline %v", dialTestTimeout+10*time.Millisecond)) |
4409 | 1617 | upCh <- b | 1611 | upCh <- b |
4410 | 1618 | c.Check(takeNext(downCh), Equals, protocol.AckMsg{"ack"}) | 1612 | c.Check(takeNext(downCh), Equals, protocol.AckMsg{"ack"}) |
4411 | 1619 | upCh <- nil | 1613 | upCh <- nil |
4412 | @@ -1625,21 +1619,21 @@ | |||
4413 | 1625 | c.Check(levels, DeepEquals, map[string]int64{"0": 2}) | 1619 | c.Check(levels, DeepEquals, map[string]int64{"0": 2}) |
4414 | 1626 | 1620 | ||
4415 | 1627 | // and ping still work even after that. | 1621 | // and ping still work even after that. |
4417 | 1628 | c.Check(takeNext(downCh), Equals, "deadline 110ms") | 1622 | c.Check(takeNext(downCh), Equals, fmt.Sprintf("deadline %v", dialTestTimeout+10*time.Millisecond)) |
4418 | 1629 | upCh <- protocol.PingPongMsg{Type: "ping"} | 1623 | upCh <- protocol.PingPongMsg{Type: "ping"} |
4419 | 1630 | c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"}) | 1624 | c.Check(takeNext(downCh), Equals, protocol.PingPongMsg{Type: "pong"}) |
4420 | 1631 | failure := errors.New("pongs") | 1625 | failure := errors.New("pongs") |
4421 | 1632 | upCh <- failure | 1626 | upCh <- failure |
4423 | 1633 | c.Check(<-sess.ErrCh, Equals, failure) | 1627 | c.Check(<-sess.errCh, Equals, failure) |
4424 | 1634 | } | 1628 | } |
4425 | 1635 | 1629 | ||
4426 | 1636 | func (cs *clientSessionSuite) TestDialWorksDirect(c *C) { | 1630 | func (cs *clientSessionSuite) TestDialWorksDirect(c *C) { |
4427 | 1637 | // happy path thoughts | 1631 | // happy path thoughts |
4428 | 1638 | lst, err := tls.Listen("tcp", "localhost:0", helpers.TestTLSServerConfig) | 1632 | lst, err := tls.Listen("tcp", "localhost:0", helpers.TestTLSServerConfig) |
4429 | 1639 | c.Assert(err, IsNil) | 1633 | c.Assert(err, IsNil) |
4431 | 1640 | sess, err := NewSession(lst.Addr().String(), dialTestConf, "wah", cs.lvls, cs.log) | 1634 | sess, err := NewSession(lst.Addr().String(), dialTestConf(), "wah", cs.lvls, cs.log) |
4432 | 1641 | c.Assert(err, IsNil) | 1635 | c.Assert(err, IsNil) |
4434 | 1642 | defer sess.Close() | 1636 | defer sess.StopKeepConnection() |
4435 | 1643 | 1637 | ||
4436 | 1644 | upCh := make(chan interface{}, 5) | 1638 | upCh := make(chan interface{}, 5) |
4437 | 1645 | downCh := make(chan interface{}, 5) | 1639 | downCh := make(chan interface{}, 5) |
4438 | @@ -1658,7 +1652,7 @@ | |||
4439 | 1658 | ****************************************************************/ | 1652 | ****************************************************************/ |
4440 | 1659 | 1653 | ||
4441 | 1660 | func (cs *clientSessionSuite) TestShouldDelay(c *C) { | 1654 | func (cs *clientSessionSuite) TestShouldDelay(c *C) { |
4443 | 1661 | sess, err := NewSession("foo:443", dummyConf, "", cs.lvls, cs.log) | 1655 | sess, err := NewSession("foo:443", dummyConf(), "", cs.lvls, cs.log) |
4444 | 1662 | c.Assert(err, IsNil) | 1656 | c.Assert(err, IsNil) |
4445 | 1663 | c.Check(sess.ShouldDelay(), Equals, false) | 1657 | c.Check(sess.ShouldDelay(), Equals, false) |
4446 | 1664 | sess.setShouldDelay() | 1658 | sess.setShouldDelay() |
4447 | @@ -1668,7 +1662,7 @@ | |||
4448 | 1668 | } | 1662 | } |
4449 | 1669 | 1663 | ||
4450 | 1670 | func (cs *clientSessionSuite) TestRedialDelay(c *C) { | 1664 | func (cs *clientSessionSuite) TestRedialDelay(c *C) { |
4452 | 1671 | sess, err := NewSession("foo:443", dummyConf, "", cs.lvls, cs.log) | 1665 | sess, err := NewSession("foo:443", dummyConf(), "", cs.lvls, cs.log) |
4453 | 1672 | c.Assert(err, IsNil) | 1666 | c.Assert(err, IsNil) |
4454 | 1673 | sess.redialDelays = []time.Duration{17, 42} | 1667 | sess.redialDelays = []time.Duration{17, 42} |
4455 | 1674 | n := 0 | 1668 | n := 0 |
4456 | @@ -1689,15 +1683,207 @@ | |||
4457 | 1689 | } | 1683 | } |
4458 | 1690 | 1684 | ||
4459 | 1691 | /**************************************************************** | 1685 | /**************************************************************** |
4461 | 1692 | ClearCookie() tests | 1686 | ResetCookie() tests |
4462 | 1693 | ****************************************************************/ | 1687 | ****************************************************************/ |
4463 | 1694 | 1688 | ||
4466 | 1695 | func (cs *clientSessionSuite) TestClearCookie(c *C) { | 1689 | func (cs *clientSessionSuite) TestResetCookie(c *C) { |
4467 | 1696 | sess, err := NewSession("foo:443", dummyConf, "", cs.lvls, cs.log) | 1690 | sess, err := NewSession("foo:443", dummyConf(), "", cs.lvls, cs.log) |
4468 | 1697 | c.Assert(err, IsNil) | 1691 | c.Assert(err, IsNil) |
4469 | 1692 | c.Assert(sess.KeepConnection(), IsNil) | ||
4470 | 1693 | defer sess.StopKeepConnection() | ||
4471 | 1698 | c.Check(sess.getCookie(), Equals, "") | 1694 | c.Check(sess.getCookie(), Equals, "") |
4472 | 1699 | sess.setCookie("COOKIE") | 1695 | sess.setCookie("COOKIE") |
4473 | 1700 | c.Check(sess.getCookie(), Equals, "COOKIE") | 1696 | c.Check(sess.getCookie(), Equals, "COOKIE") |
4475 | 1701 | sess.ClearCookie() | 1697 | sess.ResetCookie() |
4476 | 1702 | c.Check(sess.getCookie(), Equals, "") | 1698 | c.Check(sess.getCookie(), Equals, "") |
4477 | 1703 | } | 1699 | } |
4478 | 1700 | |||
4479 | 1701 | /**************************************************************** | ||
4480 | 1702 | KeepConnection() (and related) tests | ||
4481 | 1703 | ****************************************************************/ | ||
4482 | 1704 | |||
4483 | 1705 | func (cs *clientSessionSuite) TestKeepConnectionDoesNothingIfNotConnected(c *C) { | ||
4484 | 1706 | // how do you test "does nothing?" | ||
4485 | 1707 | sess, err := NewSession("foo:443", dummyConf(), "", cs.lvls, cs.log) | ||
4486 | 1708 | c.Assert(err, IsNil) | ||
4487 | 1709 | c.Assert(sess, NotNil) | ||
4488 | 1710 | c.Assert(sess.State(), Equals, Pristine) | ||
4489 | 1711 | c.Assert(sess.KeepConnection(), IsNil) | ||
4490 | 1712 | defer sess.StopKeepConnection() | ||
4491 | 1713 | // stopCh is meant to be used just for closing it, but abusing | ||
4492 | 1714 | // it for testing seems the right thing to do: this ensures | ||
4493 | 1715 | // the thing is ticking along before we check the state of | ||
4494 | 1716 | // stuff. | ||
4495 | 1717 | sess.stopCh <- struct{}{} | ||
4496 | 1718 | c.Check(sess.State(), Equals, Disconnected) | ||
4497 | 1719 | } | ||
4498 | 1720 | |||
4499 | 1721 | func (cs *clientSessionSuite) TestYouCantCallKeepConnectionTwice(c *C) { | ||
4500 | 1722 | sess, err := NewSession("foo:443", dummyConf(), "", cs.lvls, cs.log) | ||
4501 | 1723 | c.Assert(err, IsNil) | ||
4502 | 1724 | c.Assert(sess, NotNil) | ||
4503 | 1725 | c.Assert(sess.State(), Equals, Pristine) | ||
4504 | 1726 | c.Assert(sess.KeepConnection(), IsNil) | ||
4505 | 1727 | defer sess.StopKeepConnection() | ||
4506 | 1728 | c.Check(sess.KeepConnection(), NotNil) | ||
4507 | 1729 | } | ||
4508 | 1730 | |||
4509 | 1731 | func (cs *clientSessionSuite) TestStopKeepConnectionShutsdown(c *C) { | ||
4510 | 1732 | sess, err := NewSession("foo:443", dummyConf(), "", cs.lvls, cs.log) | ||
4511 | 1733 | c.Assert(err, IsNil) | ||
4512 | 1734 | c.Assert(sess, NotNil) | ||
4513 | 1735 | sess.StopKeepConnection() | ||
4514 | 1736 | c.Check(sess.State(), Equals, Shutdown) | ||
4515 | 1737 | } | ||
4516 | 1738 | |||
4517 | 1739 | func (cs *clientSessionSuite) TestHasConnectivityTriggersConnectivityHandler(c *C) { | ||
4518 | 1740 | sess, err := NewSession("foo:443", dummyConf(), "", cs.lvls, cs.log) | ||
4519 | 1741 | c.Assert(err, IsNil) | ||
4520 | 1742 | c.Assert(sess, NotNil) | ||
4521 | 1743 | testCh := make(chan bool) | ||
4522 | 1744 | sess.connHandler = func(p bool) { testCh <- p } | ||
4523 | 1745 | go sess.doKeepConnection() | ||
4524 | 1746 | defer sess.StopKeepConnection() | ||
4525 | 1747 | sess.HasConnectivity(true) | ||
4526 | 1748 | c.Check(<-testCh, Equals, true) | ||
4527 | 1749 | sess.HasConnectivity(false) | ||
4528 | 1750 | c.Check(<-testCh, Equals, false) | ||
4529 | 1751 | } | ||
4530 | 1752 | |||
4531 | 1753 | func (cs *clientSessionSuite) TestDoneChIsEmptiedAndLogged(c *C) { | ||
4532 | 1754 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) | ||
4533 | 1755 | c.Assert(err, IsNil) | ||
4534 | 1756 | sess.doneCh = make(chan uint32) // unbuffered | ||
4535 | 1757 | |||
4536 | 1758 | sess.KeepConnection() | ||
4537 | 1759 | defer sess.StopKeepConnection() | ||
4538 | 1760 | |||
4539 | 1761 | sess.doneCh <- 23 | ||
4540 | 1762 | sess.doneCh <- 24 // makes sure the first one has been processed before checking | ||
4541 | 1763 | |||
4542 | 1764 | c.Check(cs.log.Captured(), | ||
4543 | 1765 | Matches, `(?ms).* connected after 23 attempts\.`) | ||
4544 | 1766 | } | ||
4545 | 1767 | |||
4546 | 1768 | func (cs *clientSessionSuite) TestErrChIsEmptiedAndLoggedAndAutoRedial(c *C) { | ||
4547 | 1769 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) | ||
4548 | 1770 | c.Assert(err, IsNil) | ||
4549 | 1771 | ch := make(chan struct{}, 1) | ||
4550 | 1772 | sess.errCh = make(chan error) // unbuffered | ||
4551 | 1773 | sess.redialDelay = func(sess *clientSession) time.Duration { ch <- struct{}{}; return 0 } | ||
4552 | 1774 | sess.lastConn = true // -> autoRedial, if the session is in Disconnected | ||
4553 | 1775 | |||
4554 | 1776 | sess.KeepConnection() | ||
4555 | 1777 | defer sess.StopKeepConnection() | ||
4556 | 1778 | |||
4557 | 1779 | sess.setState(Error) | ||
4558 | 1780 | sess.errCh <- errors.New("potato") | ||
4559 | 1781 | select { | ||
4560 | 1782 | case <-ch: | ||
4561 | 1783 | // all ok | ||
4562 | 1784 | case <-time.After(100 * time.Millisecond): | ||
4563 | 1785 | c.Fatalf("redialDelay not called (-> autoRedial not called)?") | ||
4564 | 1786 | } | ||
4565 | 1787 | |||
4566 | 1788 | c.Check(cs.log.Captured(), | ||
4567 | 1789 | Matches, `(?ms).* session error.*potato`) | ||
4568 | 1790 | } | ||
4569 | 1791 | |||
4570 | 1792 | func (cs *clientSessionSuite) TestErrChIsEmptiedAndLoggedNoAutoRedial(c *C) { | ||
4571 | 1793 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) | ||
4572 | 1794 | c.Assert(err, IsNil) | ||
4573 | 1795 | ch := make(chan struct{}, 1) | ||
4574 | 1796 | sess.errCh = make(chan error) // unbuffered | ||
4575 | 1797 | sess.redialDelay = func(sess *clientSession) time.Duration { ch <- struct{}{}; return 0 } | ||
4576 | 1798 | sess.connHandler = func(bool) {} | ||
4577 | 1799 | sess.lastConn = false // so, no autoredial | ||
4578 | 1800 | |||
4579 | 1801 | sess.KeepConnection() | ||
4580 | 1802 | defer sess.StopKeepConnection() | ||
4581 | 1803 | |||
4582 | 1804 | sess.errCh <- errors.New("potato") | ||
4583 | 1805 | c.Assert(sess.State(), Equals, Disconnected) | ||
4584 | 1806 | select { | ||
4585 | 1807 | case <-ch: | ||
4586 | 1808 | c.Fatalf("redialDelay called (-> autoRedial called) when disconnected?") | ||
4587 | 1809 | case <-time.After(100 * time.Millisecond): | ||
4588 | 1810 | // all ok | ||
4589 | 1811 | } | ||
4590 | 1812 | |||
4591 | 1813 | c.Check(cs.log.Captured(), | ||
4592 | 1814 | Matches, `(?ms).* session error.*potato`) | ||
4593 | 1815 | } | ||
4594 | 1816 | |||
4595 | 1817 | func (cs *clientSessionSuite) TestHandleConnConnFromConnected(c *C) { | ||
4596 | 1818 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) | ||
4597 | 1819 | c.Assert(err, IsNil) | ||
4598 | 1820 | ch := make(chan struct{}, 1) | ||
4599 | 1821 | sess.redialDelay = func(sess *clientSession) time.Duration { ch <- struct{}{}; return 0 } | ||
4600 | 1822 | sess.state = Connected | ||
4601 | 1823 | sess.lastConn = true | ||
4602 | 1824 | sess.handleConn(true) | ||
4603 | 1825 | c.Check(sess.lastConn, Equals, true) | ||
4604 | 1826 | |||
4605 | 1827 | select { | ||
4606 | 1828 | case <-ch: | ||
4607 | 1829 | // all ok | ||
4608 | 1830 | case <-time.After(100 * time.Millisecond): | ||
4609 | 1831 | c.Fatalf("redialDelay not called (-> autoRedial not called)?") | ||
4610 | 1832 | } | ||
4611 | 1833 | } | ||
4612 | 1834 | |||
4613 | 1835 | func (cs *clientSessionSuite) TestHandleConnConnFromDisconnected(c *C) { | ||
4614 | 1836 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) | ||
4615 | 1837 | c.Assert(err, IsNil) | ||
4616 | 1838 | ch := make(chan struct{}, 1) | ||
4617 | 1839 | sess.redialDelay = func(sess *clientSession) time.Duration { ch <- struct{}{}; return 0 } | ||
4618 | 1840 | sess.state = Disconnected | ||
4619 | 1841 | sess.lastConn = false | ||
4620 | 1842 | sess.handleConn(true) | ||
4621 | 1843 | c.Check(sess.lastConn, Equals, true) | ||
4622 | 1844 | |||
4623 | 1845 | select { | ||
4624 | 1846 | case <-ch: | ||
4625 | 1847 | // all ok | ||
4626 | 1848 | case <-time.After(100 * time.Millisecond): | ||
4627 | 1849 | c.Fatalf("redialDelay not called (-> autoRedial not called)?") | ||
4628 | 1850 | } | ||
4629 | 1851 | } | ||
4630 | 1852 | |||
4631 | 1853 | func (cs *clientSessionSuite) TestHandleConnNotConnFromDisconnected(c *C) { | ||
4632 | 1854 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) | ||
4633 | 1855 | c.Assert(err, IsNil) | ||
4634 | 1856 | ch := make(chan struct{}, 1) | ||
4635 | 1857 | sess.redialDelay = func(sess *clientSession) time.Duration { ch <- struct{}{}; return 0 } | ||
4636 | 1858 | sess.state = Disconnected | ||
4637 | 1859 | sess.lastConn = false | ||
4638 | 1860 | sess.handleConn(false) | ||
4639 | 1861 | c.Check(sess.lastConn, Equals, false) | ||
4640 | 1862 | |||
4641 | 1863 | select { | ||
4642 | 1864 | case <-ch: | ||
4643 | 1865 | c.Fatalf("redialDelay called (-> autoRedial called)?") | ||
4644 | 1866 | case <-time.After(100 * time.Millisecond): | ||
4645 | 1867 | // all ok | ||
4646 | 1868 | } | ||
4647 | 1869 | c.Check(cs.log.Captured(), Matches, `(?ms).*-> Disconnected`) | ||
4648 | 1870 | } | ||
4649 | 1871 | |||
4650 | 1872 | func (cs *clientSessionSuite) TestHandleConnNotConnFromConnected(c *C) { | ||
4651 | 1873 | sess, err := NewSession("", dummyConf(), "wah", cs.lvls, cs.log) | ||
4652 | 1874 | c.Assert(err, IsNil) | ||
4653 | 1875 | ch := make(chan struct{}, 1) | ||
4654 | 1876 | sess.redialDelay = func(sess *clientSession) time.Duration { ch <- struct{}{}; return 0 } | ||
4655 | 1877 | sess.state = Connected | ||
4656 | 1878 | sess.lastConn = true | ||
4657 | 1879 | sess.handleConn(false) | ||
4658 | 1880 | c.Check(sess.lastConn, Equals, false) | ||
4659 | 1881 | |||
4660 | 1882 | select { | ||
4661 | 1883 | case <-ch: | ||
4662 | 1884 | c.Fatalf("redialDelay called (-> autoRedial called)?") | ||
4663 | 1885 | case <-time.After(100 * time.Millisecond): | ||
4664 | 1886 | // all ok | ||
4665 | 1887 | } | ||
4666 | 1888 | c.Check(cs.log.Captured(), Matches, `(?ms).*-> Disconnected`) | ||
4667 | 1889 | } | ||
4668 | 1704 | 1890 | ||
4669 | === added file 'docs/Makefile' | |||
4670 | --- docs/Makefile 1970-01-01 00:00:00 +0000 | |||
4671 | +++ docs/Makefile 2015-03-26 16:42:21 +0000 | |||
4672 | @@ -0,0 +1,4 @@ | |||
4673 | 1 | all: *txt *svg | ||
4674 | 2 | rst2html --link-stylesheet highlevel.txt highlevel.html | ||
4675 | 3 | rst2html --link-stylesheet lowlevel.txt lowlevel.html | ||
4676 | 4 | |||
4677 | 0 | 5 | ||
4678 | === modified file 'docs/_common.txt' | |||
4679 | --- docs/_common.txt 2014-09-05 14:49:44 +0000 | |||
4680 | +++ docs/_common.txt 2015-03-26 16:42:21 +0000 | |||
4681 | @@ -7,46 +7,22 @@ | |||
4682 | 7 | The helper receives two arguments ``infile`` and ``outfile``. The message is delivered via ``infile`` and the transformed | 7 | The helper receives two arguments ``infile`` and ``outfile``. The message is delivered via ``infile`` and the transformed |
4683 | 8 | version is placed in ``outfile``. | 8 | version is placed in ``outfile``. |
4684 | 9 | 9 | ||
4714 | 10 | This is the simplest possible useful helper, which simply passes the message through unchanged:: | 10 | This is the simplest possible useful helper, which simply passes the message through unchanged: |
4715 | 11 | 11 | ||
4716 | 12 | #!/usr/bin/python3 | 12 | .. include:: example-client/helloHelper |
4717 | 13 | 13 | :literal: | |
4718 | 14 | import sys | 14 | |
4719 | 15 | f1, f2 = sys.argv[1:3] | 15 | Helpers need to be added to the click package manifest: |
4720 | 16 | open(f2, "w").write(open(f1).read()) | 16 | |
4721 | 17 | 17 | .. include:: example-client/manifest.json | |
4722 | 18 | Helpers need to be added to the click package manifest:: | 18 | :literal: |
4694 | 19 | |||
4695 | 20 | { | ||
4696 | 21 | "name": "com.ubuntu.developer.ralsina.hello", | ||
4697 | 22 | "description": "description of hello", | ||
4698 | 23 | "framework": "ubuntu-sdk-14.10-qml-dev2", | ||
4699 | 24 | "architecture": "all", | ||
4700 | 25 | "title": "hello", | ||
4701 | 26 | "hooks": { | ||
4702 | 27 | "hello": { | ||
4703 | 28 | "apparmor": "hello.json", | ||
4704 | 29 | "desktop": "hello.desktop" | ||
4705 | 30 | }, | ||
4706 | 31 | "helloHelper": { | ||
4707 | 32 | "apparmor": "helloHelper-apparmor.json", | ||
4708 | 33 | "push-helper": "helloHelper.json" | ||
4709 | 34 | } | ||
4710 | 35 | }, | ||
4711 | 36 | "version": "0.2", | ||
4712 | 37 | "maintainer": "Roberto Alsina <roberto.alsina@canonical.com>" | ||
4713 | 38 | } | ||
4723 | 39 | 19 | ||
4724 | 40 | Here, we created a helloHelper entry in hooks that has an apparmor profile and an additional JSON file for the push-helper hook. | 20 | Here, we created a helloHelper entry in hooks that has an apparmor profile and an additional JSON file for the push-helper hook. |
4725 | 41 | 21 | ||
4727 | 42 | helloHelper-apparmor.json must contain **only** the push-notification-client policy group:: | 22 | helloHelper-apparmor.json must contain **only** the push-notification-client policy group and the ubuntu-push-helper template: |
4728 | 43 | 23 | ||
4735 | 44 | { | 24 | .. include:: example-client/helloHelper-apparmor.json |
4736 | 45 | "policy_groups": [ | 25 | :literal: |
4731 | 46 | "push-notification-client" | ||
4732 | 47 | ], | ||
4733 | 48 | "policy_version": 1.2 | ||
4734 | 49 | } | ||
4737 | 50 | 26 | ||
4738 | 51 | And helloHelper.json must have at least a exec key with the path to the helper executable relative to the json, and optionally | 27 | And helloHelper.json must have at least a exec key with the path to the helper executable relative to the json, and optionally |
4739 | 52 | an app_id key containing the short id of one of the apps in the package (in the format packagename_appname without a version). | 28 | an app_id key containing the short id of one of the apps in the package (in the format packagename_appname without a version). |
4740 | @@ -138,15 +114,10 @@ | |||
4741 | 138 | Security | 114 | Security |
4742 | 139 | ~~~~~~~~ | 115 | ~~~~~~~~ |
4743 | 140 | 116 | ||
4745 | 141 | To use the push API, applications need to request permission in their security profile, using something like this:: | 117 | To use the push API, applications need to request permission in their security profile, using something like this: |
4746 | 142 | 118 | ||
4754 | 143 | { | 119 | .. include:: example-client/hello.json |
4755 | 144 | "policy_groups": [ | 120 | :literal: |
4749 | 145 | "networking", | ||
4750 | 146 | "push-notification-client" | ||
4751 | 147 | ], | ||
4752 | 148 | "policy_version": 1.2 | ||
4753 | 149 | } | ||
4756 | 150 | 121 | ||
4757 | 151 | 122 | ||
4758 | 152 | Ubuntu Push Server API | 123 | Ubuntu Push Server API |
4759 | @@ -184,3 +155,46 @@ | |||
4760 | 184 | :clear_pending: Discards all previous pending notifications. Usually in response to getting a "too-many-pending" error. | 155 | :clear_pending: Discards all previous pending notifications. Usually in response to getting a "too-many-pending" error. |
4761 | 185 | :replace_tag: If there's a pending notification with the same tag, delete it before queuing this new one. | 156 | :replace_tag: If there's a pending notification with the same tag, delete it before queuing this new one. |
4762 | 186 | :data: A JSON object. | 157 | :data: A JSON object. |
4763 | 158 | |||
4764 | 159 | Limitations of the Server API | ||
4765 | 160 | ----------------------------- | ||
4766 | 161 | |||
4767 | 162 | The push notification infrastructure is meant to help ensuring timely | ||
4768 | 163 | delivery of application notifications if the device is online or | ||
4769 | 164 | timely informing the device user about application notifications that | ||
4770 | 165 | were pending when the device comes back online. This in the face of | ||
4771 | 166 | applications not being allowed to be running all the time, and | ||
4772 | 167 | avoiding the resource cost of many applications all polling different services | ||
4773 | 168 | frequently. | ||
4774 | 169 | |||
4775 | 170 | The push notification infrastructure is architected to guarantee at | ||
4776 | 171 | least best-effort with respect to these goals and beyond it, on the | ||
4777 | 172 | other end applications should not expect to be able to use and only | ||
4778 | 173 | rely on the push notification infrastructure to store application | ||
4779 | 174 | messages if they want ensure all their notification or messages are | ||
4780 | 175 | delivered, the infrastructure is not intended to be the only long term | ||
4781 | 176 | "inbox" storage for an application. | ||
4782 | 177 | |||
4783 | 178 | To preserve overall throughput the infrastructure imposes some limits | ||
4784 | 179 | on applications: | ||
4785 | 180 | |||
4786 | 181 | * message data payload is limited to 2K | ||
4787 | 182 | |||
4788 | 183 | * when inserted all messages need to specify an expiration date after | ||
4789 | 184 | which they can be dropped and not delivered | ||
4790 | 185 | |||
4791 | 186 | * an application is limited in the number of messages per token | ||
4792 | 187 | (application/user/device combination) that can be undelivered/pending at the | ||
4793 | 188 | same time (100 currently) | ||
4794 | 189 | |||
4795 | 190 | replace_tag can be used to implement notifications for which the newest | ||
4796 | 191 | one replace the previous one if pending. | ||
4797 | 192 | |||
4798 | 193 | clear_pending can be used to be deal with a pending message limit | ||
4799 | 194 | reached, possibly substituting the current undelivered messages with a | ||
4800 | 195 | more generic one. | ||
4801 | 196 | |||
4802 | 197 | Applications using the push notification HTTP API should be robust | ||
4803 | 198 | against receiving 503 errors, retrying after waiting with increasing | ||
4804 | 199 | back-off. Later rate limits (signaled with the 429 status) may also come | ||
4805 | 200 | into play. | ||
4806 | 187 | 201 | ||
4807 | === modified file 'docs/example-client/components/ChatClient.qml' | |||
4808 | --- docs/example-client/components/ChatClient.qml 2014-09-05 14:40:39 +0000 | |||
4809 | +++ docs/example-client/components/ChatClient.qml 2015-03-26 16:42:21 +0000 | |||
4810 | @@ -60,8 +60,8 @@ | |||
4811 | 60 | if (options["enabled"]) { | 60 | if (options["enabled"]) { |
4812 | 61 | data["data"]["notification"] = { | 61 | data["data"]["notification"] = { |
4813 | 62 | "card": { | 62 | "card": { |
4816 | 63 | "summary": nick + " says: " + message["message"], | 63 | "summary": nick + " says:", |
4817 | 64 | "body": "", | 64 | "body": message["message"], |
4818 | 65 | "popup": options["popup"], | 65 | "popup": options["popup"], |
4819 | 66 | "persist": options["persist"], | 66 | "persist": options["persist"], |
4820 | 67 | "actions": ["appid://com.ubuntu.developer.ralsina.hello/hello/current-user-version"] | 67 | "actions": ["appid://com.ubuntu.developer.ralsina.hello/hello/current-user-version"] |
4821 | 68 | 68 | ||
4822 | === modified file 'docs/example-client/helloHelper-apparmor.json' | |||
4823 | --- docs/example-client/helloHelper-apparmor.json 2014-09-05 14:40:39 +0000 | |||
4824 | +++ docs/example-client/helloHelper-apparmor.json 2015-03-26 16:42:21 +0000 | |||
4825 | @@ -1,4 +1,5 @@ | |||
4826 | 1 | { | 1 | { |
4827 | 2 | "template": "ubuntu-push-helper", | ||
4828 | 2 | "policy_groups": [ | 3 | "policy_groups": [ |
4829 | 3 | "push-notification-client" | 4 | "push-notification-client" |
4830 | 4 | ], | 5 | ], |
4831 | 5 | 6 | ||
4832 | === modified file 'docs/example-client/main.qml' | |||
4833 | --- docs/example-client/main.qml 2014-09-10 14:38:40 +0000 | |||
4834 | +++ docs/example-client/main.qml 2015-03-26 16:42:21 +0000 | |||
4835 | @@ -26,9 +26,42 @@ | |||
4836 | 26 | property alias nickEnabled: nickEdit.enabled | 26 | property alias nickEnabled: nickEdit.enabled |
4837 | 27 | } | 27 | } |
4838 | 28 | 28 | ||
4839 | 29 | states: [ | ||
4840 | 30 | State { | ||
4841 | 31 | name: "no-push-token" | ||
4842 | 32 | when: (pushClient.token == "") | ||
4843 | 33 | PropertyChanges { target: nickEdit; readOnly: true} | ||
4844 | 34 | PropertyChanges { target: nickEdit; focus: true} | ||
4845 | 35 | PropertyChanges { target: messageEdit; enabled: false} | ||
4846 | 36 | PropertyChanges { target: loginButton; enabled: false} | ||
4847 | 37 | PropertyChanges { target: loginButton; text: "Login"} | ||
4848 | 38 | }, | ||
4849 | 39 | State { | ||
4850 | 40 | name: "push-token-not-registered" | ||
4851 | 41 | when: ((pushClient.token != "") && (chatClient.registered == false)) | ||
4852 | 42 | PropertyChanges { target: nickEdit; readOnly: false} | ||
4853 | 43 | PropertyChanges { target: nickEdit; text: ""} | ||
4854 | 44 | PropertyChanges { target: nickEdit; focus: true} | ||
4855 | 45 | PropertyChanges { target: messageEdit; enabled: false} | ||
4856 | 46 | PropertyChanges { target: loginButton; enabled: true} | ||
4857 | 47 | PropertyChanges { target: loginButton; text: "Login"} | ||
4858 | 48 | }, | ||
4859 | 49 | State { | ||
4860 | 50 | name: "registered" | ||
4861 | 51 | when: ((pushClient.token != "") && (chatClient.registered == true)) | ||
4862 | 52 | PropertyChanges { target: nickEdit; readOnly: true} | ||
4863 | 53 | PropertyChanges { target: nickEdit; text: "Your nick is " + chatClient.nick} | ||
4864 | 54 | PropertyChanges { target: messageEdit; focus: true} | ||
4865 | 55 | PropertyChanges { target: messageEdit; enabled: true} | ||
4866 | 56 | PropertyChanges { target: loginButton; enabled: true} | ||
4867 | 57 | PropertyChanges { target: loginButton; text: "Logout"} | ||
4868 | 58 | } | ||
4869 | 59 | ] | ||
4870 | 60 | |||
4871 | 61 | state: "no-push-token" | ||
4872 | 62 | |||
4873 | 29 | ChatClient { | 63 | ChatClient { |
4874 | 30 | id: chatClient | 64 | id: chatClient |
4875 | 31 | onRegisteredChanged: {nickEdit.registered()} | ||
4876 | 32 | onError: {messageList.handle_error(msg)} | 65 | onError: {messageList.handle_error(msg)} |
4877 | 33 | token: pushClient.token | 66 | token: pushClient.token |
4878 | 34 | } | 67 | } |
4879 | @@ -38,13 +71,16 @@ | |||
4880 | 38 | Component.onCompleted: { | 71 | Component.onCompleted: { |
4881 | 39 | notificationsChanged.connect(messageList.handle_notifications) | 72 | notificationsChanged.connect(messageList.handle_notifications) |
4882 | 40 | error.connect(messageList.handle_error) | 73 | error.connect(messageList.handle_error) |
4883 | 74 | onTokenChanged: { | ||
4884 | 75 | console.log("foooooo") | ||
4885 | 76 | } | ||
4886 | 41 | } | 77 | } |
4887 | 42 | appId: "com.ubuntu.developer.ralsina.hello_hello" | 78 | appId: "com.ubuntu.developer.ralsina.hello_hello" |
4888 | 79 | |||
4889 | 43 | } | 80 | } |
4890 | 44 | 81 | ||
4891 | 45 | TextField { | 82 | TextField { |
4892 | 46 | id: nickEdit | 83 | id: nickEdit |
4893 | 47 | focus: true | ||
4894 | 48 | placeholderText: "Your nickname" | 84 | placeholderText: "Your nickname" |
4895 | 49 | inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText | Qt.ImhPreferLowercase | 85 | inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText | Qt.ImhPreferLowercase |
4896 | 50 | anchors.left: parent.left | 86 | anchors.left: parent.left |
4897 | @@ -53,31 +89,17 @@ | |||
4898 | 53 | anchors.leftMargin: units.gu(.5) | 89 | anchors.leftMargin: units.gu(.5) |
4899 | 54 | anchors.rightMargin: units.gu(1) | 90 | anchors.rightMargin: units.gu(1) |
4900 | 55 | anchors.topMargin: units.gu(.5) | 91 | anchors.topMargin: units.gu(.5) |
4901 | 56 | function registered() { | ||
4902 | 57 | readOnly = true | ||
4903 | 58 | text = "Your nick is " + chatClient.nick | ||
4904 | 59 | messageEdit.focus = true | ||
4905 | 60 | messageEdit.enabled = true | ||
4906 | 61 | loginButton.text = "Logout" | ||
4907 | 62 | } | ||
4908 | 63 | onAccepted: { loginButton.clicked() } | 92 | onAccepted: { loginButton.clicked() } |
4909 | 64 | } | 93 | } |
4910 | 65 | 94 | ||
4911 | 66 | Button { | 95 | Button { |
4912 | 67 | id: loginButton | 96 | id: loginButton |
4913 | 68 | text: chatClient.rgistered? "Logout": "Login" | ||
4914 | 69 | anchors.top: nickEdit.top | 97 | anchors.top: nickEdit.top |
4915 | 70 | anchors.right: parent.right | 98 | anchors.right: parent.right |
4916 | 71 | anchors.rightMargin: units.gu(.5) | 99 | anchors.rightMargin: units.gu(.5) |
4917 | 72 | onClicked: { | 100 | onClicked: { |
4918 | 73 | if (chatClient.nick) { // logout | 101 | if (chatClient.nick) { // logout |
4919 | 74 | chatClient.nick = "" | 102 | chatClient.nick = "" |
4920 | 75 | text = "Login" | ||
4921 | 76 | nickEdit.enabled = true | ||
4922 | 77 | nickEdit.readOnly = false | ||
4923 | 78 | nickEdit.text = "" | ||
4924 | 79 | nickEdit.focus = true | ||
4925 | 80 | messageEdit.enabled = false | ||
4926 | 81 | } else { // login | 103 | } else { // login |
4927 | 82 | chatClient.nick = nickEdit.text | 104 | chatClient.nick = nickEdit.text |
4928 | 83 | } | 105 | } |
4929 | @@ -94,7 +116,6 @@ | |||
4930 | 94 | anchors.rightMargin: units.gu(.5) | 116 | anchors.rightMargin: units.gu(.5) |
4931 | 95 | anchors.leftMargin: units.gu(.5) | 117 | anchors.leftMargin: units.gu(.5) |
4932 | 96 | placeholderText: "Your message" | 118 | placeholderText: "Your message" |
4933 | 97 | enabled: false | ||
4934 | 98 | onAccepted: { | 119 | onAccepted: { |
4935 | 99 | console.log("sending " + text) | 120 | console.log("sending " + text) |
4936 | 100 | var idx = text.indexOf(":") | 121 | var idx = text.indexOf(":") |
4937 | @@ -210,7 +231,7 @@ | |||
4938 | 210 | right: parent.right | 231 | right: parent.right |
4939 | 211 | bottom: parent.bottom | 232 | bottom: parent.bottom |
4940 | 212 | } | 233 | } |
4942 | 213 | height: item1.height * 7 | 234 | height: item1.height * 9 |
4943 | 214 | UbuntuShape { | 235 | UbuntuShape { |
4944 | 215 | anchors.fill: parent | 236 | anchors.fill: parent |
4945 | 216 | color: Theme.palette.normal.overlay | 237 | color: Theme.palette.normal.overlay |
4946 | @@ -268,6 +289,14 @@ | |||
4947 | 268 | value: 42 | 289 | value: 42 |
4948 | 269 | } | 290 | } |
4949 | 270 | } | 291 | } |
4950 | 292 | Button { | ||
4951 | 293 | text: "Set Counter Via Plugin" | ||
4952 | 294 | onClicked: { pushClient.count = counterSlider.value; } | ||
4953 | 295 | } | ||
4954 | 296 | Button { | ||
4955 | 297 | text: "Clear Persistent Notifications" | ||
4956 | 298 | onClicked: { pushClient.clearPersistent([]); } | ||
4957 | 299 | } | ||
4958 | 271 | } | 300 | } |
4959 | 272 | } | 301 | } |
4960 | 273 | } | 302 | } |
4961 | 274 | 303 | ||
4962 | === modified file 'docs/example-client/manifest.json' | |||
4963 | --- docs/example-client/manifest.json 2014-09-10 14:38:31 +0000 | |||
4964 | +++ docs/example-client/manifest.json 2015-03-26 16:42:21 +0000 | |||
4965 | @@ -1,7 +1,7 @@ | |||
4966 | 1 | { | 1 | { |
4967 | 2 | "architecture": "all", | 2 | "architecture": "all", |
4968 | 3 | "description": "Example app for Ubuntu push notifications.", | 3 | "description": "Example app for Ubuntu push notifications.", |
4970 | 4 | "framework": "ubuntu-sdk-14.10-dev2", | 4 | "framework": "ubuntu-sdk-14.10", |
4971 | 5 | "hooks": { | 5 | "hooks": { |
4972 | 6 | "hello": { | 6 | "hello": { |
4973 | 7 | "apparmor": "hello.json", | 7 | "apparmor": "hello.json", |
4974 | @@ -15,5 +15,5 @@ | |||
4975 | 15 | "maintainer": "Roberto Alsina <roberto.alsina@canonical.com>", | 15 | "maintainer": "Roberto Alsina <roberto.alsina@canonical.com>", |
4976 | 16 | "name": "com.ubuntu.developer.ralsina.hello", | 16 | "name": "com.ubuntu.developer.ralsina.hello", |
4977 | 17 | "title": "Hello", | 17 | "title": "Hello", |
4979 | 18 | "version": "0.4.2" | 18 | "version": "0.4.4" |
4980 | 19 | } | 19 | } |
4981 | 20 | 20 | ||
4982 | === modified file 'docs/example-server/app.js' | |||
4983 | --- docs/example-server/app.js 2014-09-05 14:57:17 +0000 | |||
4984 | +++ docs/example-server/app.js 2015-03-26 16:42:21 +0000 | |||
4985 | @@ -182,22 +182,40 @@ | |||
4986 | 182 | */ | 182 | */ |
4987 | 183 | if (cfg.play_notify_form) { | 183 | if (cfg.play_notify_form) { |
4988 | 184 | app.post("/play-notify-form", function(req, resp) { | 184 | app.post("/play-notify-form", function(req, resp) { |
5000 | 185 | resp.type('text/plain') | 185 | if (!req.body.message||!req.body.nick) { |
4990 | 186 | if (!req.body.data||!req.body.nick) { | ||
4991 | 187 | resp.send(400, "invalid/empty fields\n") | ||
4992 | 188 | return | ||
4993 | 189 | } | ||
4994 | 190 | var data | ||
4995 | 191 | try { | ||
4996 | 192 | data = JSON.parse(req.body.data) | ||
4997 | 193 | } catch(e) { | ||
4998 | 194 | resp.send(400, "data is not JSON\n") | ||
4999 | 195 | return |