Merge lp:~ralsina/ubuntu-push/merge-automatic into lp:ubuntu-push

Proposed by Roberto Alsina
Status: Merged
Approved by: Roberto Alsina
Approved revision: no longer in the source branch.
Merged at revision: 119
Proposed branch: lp:~ralsina/ubuntu-push/merge-automatic
Merge into: lp:ubuntu-push
Diff against target: 3047 lines (+1300/-534)
41 files modified
Makefile (+6/-4)
bus/haptic/haptic.go (+10/-11)
bus/haptic/haptic_test.go (+26/-37)
click/click.go (+3/-3)
click/click_test.go (+1/-1)
client/client.go (+23/-10)
client/client_test.go (+37/-7)
client/service/common.go (+3/-3)
client/service/common_test.go (+12/-3)
client/service/postal.go (+16/-4)
client/service/postal_test.go (+57/-47)
client/service/service_test.go (+2/-1)
debian/changelog (+22/-0)
debian/config.json (+3/-1)
debian/ubuntu-push-client.conf (+4/-2)
docs/highlevel.txt (+325/-0)
docs/lowlevel.txt (+11/-12)
identifier/identifier.go (+59/-0)
identifier/identifier_test.go (+54/-0)
identifier/testing/testing.go (+58/-0)
identifier/testing/testing_test.go (+57/-0)
launch_helper/helper_output.go (+70/-15)
launch_helper/helper_output_test.go (+96/-0)
launch_helper/helper_test.go (+1/-1)
launch_helper/kindpool_test.go (+2/-1)
messaging/cmessaging/cmessaging.go (+1/-1)
scripts/connect-many.py (+29/-0)
scripts/goctest (+46/-0)
server/listener/listener_test.go (+34/-22)
sounds/sounds.go (+30/-6)
sounds/sounds_test.go (+45/-5)
tests/autopilot/push_notifications/tests/__init__.py (+51/-1)
tests/autopilot/push_notifications/tests/test_broadcast_notifications.py (+61/-14)
tests/autopilot/push_notifications/tests/test_unicast_notifications.py (+24/-21)
tests/autopilot/run.sh (+6/-2)
tests/autopilot/setup.sh (+15/-5)
whoopsie/doc.go (+0/-18)
whoopsie/identifier/identifier.go (+0/-78)
whoopsie/identifier/identifier_test.go (+0/-58)
whoopsie/identifier/testing/testing.go (+0/-70)
whoopsie/identifier/testing/testing_test.go (+0/-70)
To merge this branch: bzr merge lp:~ralsina/ubuntu-push/merge-automatic
Reviewer Review Type Date Requested Status
Ubuntu Push Hackers Pending
Review via email: mp+230396@code.launchpad.net

Commit message

Merge current automatic branch (see debian/changelog for details)

Description of the change

Merge current automatic branch (see debian/changelog for details)

To post a comment you must log in.
119. By Roberto Alsina

Roberto's branch + fixes to tests that failed in chroot.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Makefile'
--- Makefile 2014-06-19 21:31:23 +0000
+++ Makefile 2014-08-11 21:30:50 +0000
@@ -13,6 +13,8 @@
13GODEPS += code.google.com/p/gosqlite/sqlite313GODEPS += code.google.com/p/gosqlite/sqlite3
14GODEPS += code.google.com/p/go-uuid/uuid14GODEPS += code.google.com/p/go-uuid/uuid
1515
16GOTEST := ./scripts/goctest
17
16TOTEST = $(shell env GOPATH=$(GOPATH) go list $(PROJECT)/...|grep -v acceptance|grep -v http13client )18TOTEST = $(shell env GOPATH=$(GOPATH) go list $(PROJECT)/...|grep -v acceptance|grep -v http13client )
17TOBUILD = $(shell grep -lr '^package main')19TOBUILD = $(shell grep -lr '^package main')
1820
@@ -40,10 +42,10 @@
40 $(GOPATH)/bin/godeps -t $(TOTEST) $(foreach i,$(TOBUILD),$(dir $(PROJECT)/$(i))) 2>/dev/null | cat > $@42 $(GOPATH)/bin/godeps -t $(TOTEST) $(foreach i,$(TOBUILD),$(dir $(PROJECT)/$(i))) 2>/dev/null | cat > $@
4143
42check:44check:
43 go test $(TESTFLAGS) $(TOTEST)45 $(GOTEST) $(TESTFLAGS) $(TOTEST)
4446
45check-race:47check-race:
46 go test $(TESTFLAGS) -race $(TOTEST)48 $(GOTEST) $(TESTFLAGS) -race $(TOTEST)
4749
48acceptance:50acceptance:
49 cd server/acceptance; ./acceptance.sh51 cd server/acceptance; ./acceptance.sh
@@ -83,14 +85,14 @@
83 bzr clean-tree --verbose --ignored --force85 bzr clean-tree --verbose --ignored --force
8486
85coverage-summary:87coverage-summary:
86 go test $(TESTFLAGS) -a -cover $(TOTEST)88 $(GOTEST) $(TESTFLAGS) -a -cover $(TOTEST)
8789
88coverage-html:90coverage-html:
89 mkdir -p coverhtml91 mkdir -p coverhtml
90 for pkg in $(TOTEST); do \92 for pkg in $(TOTEST); do \
91 relname="$${pkg#$(PROJECT)/}" ; \93 relname="$${pkg#$(PROJECT)/}" ; \
92 mkdir -p coverhtml/$$(dirname $${relname}) ; \94 mkdir -p coverhtml/$$(dirname $${relname}) ; \
93 go test $(TESTFLAGS) -a -coverprofile=coverhtml/$${relname}.out $$pkg ; \95 $(GOTEST) $(TESTFLAGS) -a -coverprofile=coverhtml/$${relname}.out $$pkg ; \
94 if [ -f coverhtml/$${relname}.out ] ; then \96 if [ -f coverhtml/$${relname}.out ] ; then \
95 go tool cover -html=coverhtml/$${relname}.out -o coverhtml/$${relname}.html ; \97 go tool cover -html=coverhtml/$${relname}.out -o coverhtml/$${relname}.html ; \
96 go tool cover -func=coverhtml/$${relname}.out -o coverhtml/$${relname}.txt ; \98 go tool cover -func=coverhtml/$${relname}.out -o coverhtml/$${relname}.txt ; \
9799
=== modified file 'bus/haptic/haptic.go'
--- bus/haptic/haptic.go 2014-07-25 10:25:59 +0000
+++ bus/haptic/haptic.go 2014-08-11 21:30:50 +0000
@@ -34,13 +34,14 @@
3434
35// Haptic encapsulates info needed to call out to usensord/haptic35// Haptic encapsulates info needed to call out to usensord/haptic
36type Haptic struct {36type Haptic struct {
37 bus bus.Endpoint37 bus bus.Endpoint
38 log logger.Logger38 log logger.Logger
39 fallback *launch_helper.Vibration
39}40}
4041
41// New returns a new Haptic that'll use the provided bus.Endpoint42// New returns a new Haptic that'll use the provided bus.Endpoint
42func New(endp bus.Endpoint, log logger.Logger) *Haptic {43func New(endp bus.Endpoint, log logger.Logger, fallback *launch_helper.Vibration) *Haptic {
43 return &Haptic{endp, log}44 return &Haptic{endp, log, fallback}
44}45}
4546
46// Present presents the notification via a vibrate pattern47// Present presents the notification via a vibrate pattern
@@ -49,18 +50,16 @@
49 panic("please check notification is not nil before calling present")50 panic("please check notification is not nil before calling present")
50 }51 }
5152
52 if notification.Vibrate == nil {53 vib := notification.Vibration(haptic.fallback)
53 haptic.log.Debugf("[%s] notification has no Vibrate: %#v", nid, notification.Vibrate)54 if vib == nil {
55 haptic.log.Debugf("[%s] notification has no Vibrate.", nid)
54 return false56 return false
55 }57 }
56 pattern := notification.Vibrate.Pattern58 pattern := vib.Pattern
57 repeat := notification.Vibrate.Repeat59 repeat := vib.Repeat
58 if repeat == 0 {60 if repeat == 0 {
59 repeat = 161 repeat = 1
60 }62 }
61 if notification.Vibrate.Duration != 0 {
62 pattern = []uint32{notification.Vibrate.Duration}
63 }
64 if len(pattern) == 0 {63 if len(pattern) == 0 {
65 haptic.log.Debugf("[%s] not enough information in the Vibrate to create a pattern", nid)64 haptic.log.Debugf("[%s] not enough information in the Vibrate to create a pattern", nid)
66 return false65 return false
6766
=== modified file 'bus/haptic/haptic_test.go'
--- bus/haptic/haptic_test.go 2014-07-25 10:25:59 +0000
+++ bus/haptic/haptic_test.go 2014-08-11 21:30:50 +0000
@@ -17,6 +17,7 @@
17package haptic17package haptic
1818
19import (19import (
20 "encoding/json"
20 "testing"21 "testing"
2122
22 . "launchpad.net/gocheck"23 . "launchpad.net/gocheck"
@@ -47,8 +48,8 @@
47func (hs *hapticSuite) TestPresentPresents(c *C) {48func (hs *hapticSuite) TestPresentPresents(c *C) {
48 endp := testibus.NewTestingEndpoint(nil, condition.Work(true))49 endp := testibus.NewTestingEndpoint(nil, condition.Work(true))
4950
50 ec := New(endp, hs.log)51 ec := New(endp, hs.log, nil)
51 notif := launch_helper.Notification{Vibrate: &launch_helper.Vibration{Pattern: []uint32{200, 100}, Repeat: 2}}52 notif := launch_helper.Notification{RawVibration: json.RawMessage(`{"pattern": [200, 100], "repeat": 2}`)}
52 c.Check(ec.Present(hs.app, "nid", &notif), Equals, true)53 c.Check(ec.Present(hs.app, "nid", &notif), Equals, true)
53 callArgs := testibus.GetCallArgs(endp)54 callArgs := testibus.GetCallArgs(endp)
54 c.Assert(callArgs, HasLen, 1)55 c.Assert(callArgs, HasLen, 1)
@@ -60,9 +61,9 @@
60func (hs *hapticSuite) TestPresentDefaultsRepeatTo1(c *C) {61func (hs *hapticSuite) TestPresentDefaultsRepeatTo1(c *C) {
61 endp := testibus.NewTestingEndpoint(nil, condition.Work(true))62 endp := testibus.NewTestingEndpoint(nil, condition.Work(true))
6263
63 ec := New(endp, hs.log)64 ec := New(endp, hs.log, nil)
64 // note: no Repeat:65 // note: no Repeat:
65 notif := launch_helper.Notification{Vibrate: &launch_helper.Vibration{Pattern: []uint32{200, 100}}}66 notif := launch_helper.Notification{RawVibration: json.RawMessage(`{"pattern": [200, 100]}`)}
66 c.Check(ec.Present(hs.app, "nid", &notif), Equals, true)67 c.Check(ec.Present(hs.app, "nid", &notif), Equals, true)
67 callArgs := testibus.GetCallArgs(endp)68 callArgs := testibus.GetCallArgs(endp)
68 c.Assert(callArgs, HasLen, 1)69 c.Assert(callArgs, HasLen, 1)
@@ -71,52 +72,40 @@
71 c.Check(callArgs[0].Args, DeepEquals, []interface{}{[]uint32{200, 100}, uint32(1)})72 c.Check(callArgs[0].Args, DeepEquals, []interface{}{[]uint32{200, 100}, uint32(1)})
72}73}
7374
74// check that Present() makes a Pattern of [Duration] if Duration is given
75func (hs *hapticSuite) TestPresentBuildsPatternWithDuration(c *C) {
76 endp := testibus.NewTestingEndpoint(nil, condition.Work(true))
77
78 ec := New(endp, hs.log)
79 // note: no Repeat, no Pattern, just Duration:
80 notif := launch_helper.Notification{Vibrate: &launch_helper.Vibration{Duration: 200}}
81 c.Check(ec.Present(hs.app, "nid", &notif), Equals, true)
82 callArgs := testibus.GetCallArgs(endp)
83 c.Assert(callArgs, HasLen, 1)
84 c.Check(callArgs[0].Member, Equals, "VibratePattern")
85 // note: Pattern of [Duration], Repeat of 1:
86 c.Check(callArgs[0].Args, DeepEquals, []interface{}{[]uint32{200}, uint32(1)})
87}
88
89// check that Present() ignores Pattern and makes a Pattern of [Duration] if Duration is given
90func (hs *hapticSuite) TestPresentOverrides(c *C) {
91 endp := testibus.NewTestingEndpoint(nil, condition.Work(true))
92
93 ec := New(endp, hs.log)
94 // note: Duration given, as well as Pattern; Repeat given as 0:
95 notif := launch_helper.Notification{Vibrate: &launch_helper.Vibration{Duration: 200, Pattern: []uint32{500}, Repeat: 0}}
96 c.Check(ec.Present(hs.app, "nid", &notif), Equals, true)
97 callArgs := testibus.GetCallArgs(endp)
98 c.Assert(callArgs, HasLen, 1)
99 c.Check(callArgs[0].Member, Equals, "VibratePattern")
100 // note: Pattern of [Duration], Repeat of 1:
101 c.Check(callArgs[0].Args, DeepEquals, []interface{}{[]uint32{200}, uint32(1)})
102}
103
104// check that Present() doesn't call VibratePattern if things are not right75// check that Present() doesn't call VibratePattern if things are not right
105func (hs *hapticSuite) TestSkipIfMissing(c *C) {76func (hs *hapticSuite) TestSkipIfMissing(c *C) {
106 endp := testibus.NewTestingEndpoint(nil, condition.Work(true))77 endp := testibus.NewTestingEndpoint(nil, condition.Work(true))
10778
108 ec := New(endp, hs.log)79 ec := New(endp, hs.log, nil)
109 // no Vibration in the notificaton80 // no Vibration in the notificaton
110 c.Check(ec.Present(hs.app, "", &launch_helper.Notification{}), Equals, false)81 c.Check(ec.Present(hs.app, "", &launch_helper.Notification{}), Equals, false)
111 // empty Vibration82 // empty Vibration
112 c.Check(ec.Present(hs.app, "", &launch_helper.Notification{Vibrate: &launch_helper.Vibration{}}), Equals, false)83 c.Check(ec.Present(hs.app, "", &launch_helper.Notification{RawVibration: nil}), Equals, false)
84 // empty empty vibration
85 c.Check(ec.Present(hs.app, "", &launch_helper.Notification{RawVibration: json.RawMessage(`{}`)}), Equals, false)
113}86}
11487
115// check that Present() panics if the notification is nil88// check that Present() panics if the notification is nil
116func (hs *hapticSuite) TestPanicsIfNil(c *C) {89func (hs *hapticSuite) TestPanicsIfNil(c *C) {
117 endp := testibus.NewTestingEndpoint(nil, condition.Work(true))90 endp := testibus.NewTestingEndpoint(nil, condition.Work(true))
11891
119 ec := New(endp, hs.log)92 ec := New(endp, hs.log, nil)
120 // no notification at all93 // no notification at all
121 c.Check(func() { ec.Present(hs.app, "", nil) }, Panics, `please check notification is not nil before calling present`)94 c.Check(func() { ec.Present(hs.app, "", nil) }, Panics, `please check notification is not nil before calling present`)
122}95}
96
97// check that Present() uses the fallback if appropriate
98func (hs *hapticSuite) TestPresentPresentsFallback(c *C) {
99 endp := testibus.NewTestingEndpoint(nil, condition.Work(true))
100 fallback := &launch_helper.Vibration{Pattern: []uint32{200, 100}, Repeat: 2}
101
102 ec := New(endp, hs.log, fallback)
103 notif := launch_helper.Notification{RawVibration: json.RawMessage(`false`)}
104 c.Check(ec.Present(hs.app, "nid", &notif), Equals, false)
105 notif = launch_helper.Notification{RawVibration: json.RawMessage(`true`)}
106 c.Check(ec.Present(hs.app, "nid", &notif), Equals, true)
107 callArgs := testibus.GetCallArgs(endp)
108 c.Assert(callArgs, HasLen, 1)
109 c.Check(callArgs[0].Member, Equals, "VibratePattern")
110 c.Check(callArgs[0].Args, DeepEquals, []interface{}{[]uint32{200, 100}, uint32(2)})
111}
123112
=== modified file 'click/click.go'
--- click/click.go 2014-07-29 15:36:00 +0000
+++ click/click.go 2014-08-11 21:30:50 +0000
@@ -51,7 +51,7 @@
5151
52var (52var (
53 ErrInvalidAppId = errors.New("invalid application id")53 ErrInvalidAppId = errors.New("invalid application id")
54 ErrMissingAppId = errors.New("missing application id")54 ErrMissingApp = errors.New("application not installed")
55)55)
5656
57func ParseAppId(id string) (*AppId, error) {57func ParseAppId(id string) (*AppId, error) {
@@ -161,14 +161,14 @@
161161
162// ParseAndVerifyAppId parses the given app id and checks if the162// ParseAndVerifyAppId parses the given app id and checks if the
163// corresponding app is installed, returning the parsed id or163// corresponding app is installed, returning the parsed id or
164// ErrInvalidAppId, or the parsed id and ErrMissingAppId respectively.164// ErrInvalidAppId, or the parsed id and ErrMissingApp respectively.
165func ParseAndVerifyAppId(id string, installedChecker InstalledChecker) (*AppId, error) {165func ParseAndVerifyAppId(id string, installedChecker InstalledChecker) (*AppId, error) {
166 app, err := ParseAppId(id)166 app, err := ParseAppId(id)
167 if err != nil {167 if err != nil {
168 return nil, err168 return nil, err
169 }169 }
170 if installedChecker != nil && !installedChecker.Installed(app, true) {170 if installedChecker != nil && !installedChecker.Installed(app, true) {
171 return app, ErrMissingAppId171 return app, ErrMissingApp
172 }172 }
173 return app, nil173 return app, nil
174}174}
175175
=== modified file 'click/click_test.go'
--- click/click_test.go 2014-07-29 15:36:00 +0000
+++ click/click_test.go 2014-08-11 21:30:50 +0000
@@ -178,7 +178,7 @@
178 c.Check(app.Application, Equals, "baz")178 c.Check(app.Application, Equals, "baz")
179179
180 app, err = ParseAndVerifyAppId("_non-existent-app", u)180 app, err = ParseAndVerifyAppId("_non-existent-app", u)
181 c.Assert(err, Equals, ErrMissingAppId)181 c.Assert(err, Equals, ErrMissingApp)
182 c.Check(app, NotNil)182 c.Check(app, NotNil)
183 c.Check(app.Original(), Equals, "_non-existent-app")183 c.Check(app.Original(), Equals, "_non-existent-app")
184}184}
185185
=== modified file 'client/client.go'
--- client/client.go 2014-07-21 17:38:28 +0000
+++ client/client.go 2014-08-11 21:30:50 +0000
@@ -41,10 +41,11 @@
41 "launchpad.net/ubuntu-push/client/session"41 "launchpad.net/ubuntu-push/client/session"
42 "launchpad.net/ubuntu-push/client/session/seenstate"42 "launchpad.net/ubuntu-push/client/session/seenstate"
43 "launchpad.net/ubuntu-push/config"43 "launchpad.net/ubuntu-push/config"
44 "launchpad.net/ubuntu-push/identifier"
45 "launchpad.net/ubuntu-push/launch_helper"
44 "launchpad.net/ubuntu-push/logger"46 "launchpad.net/ubuntu-push/logger"
45 "launchpad.net/ubuntu-push/protocol"47 "launchpad.net/ubuntu-push/protocol"
46 "launchpad.net/ubuntu-push/util"48 "launchpad.net/ubuntu-push/util"
47 "launchpad.net/ubuntu-push/whoopsie/identifier"
48)49)
4950
50// ClientConfig holds the client configuration51// ClientConfig holds the client configuration
@@ -67,6 +68,9 @@
67 RegistrationURL string `json:"registration_url"`68 RegistrationURL string `json:"registration_url"`
68 // The logging level (one of "debug", "info", "error")69 // The logging level (one of "debug", "info", "error")
69 LogLevel logger.ConfigLogLevel `json:"log_level"`70 LogLevel logger.ConfigLogLevel `json:"log_level"`
71 // fallback values for simplified notification usage
72 FallbackVibration *launch_helper.Vibration `json:"fallback_vibration"`
73 FallbackSound string `json:"fallback_sound"`
70}74}
7175
72// PushService is the interface we use of service.PushService.76// PushService is the interface we use of service.PushService.
@@ -152,7 +156,10 @@
152 client.unregisterCh = make(chan *click.AppId, 10)156 client.unregisterCh = make(chan *click.AppId, 10)
153157
154 // overridden for testing158 // overridden for testing
155 client.idder = identifier.New()159 client.idder, err = identifier.New()
160 if err != nil {
161 return err
162 }
156 client.connectivityEndp = bus.SystemBus.Endpoint(networkmanager.BusAddress, client.log)163 client.connectivityEndp = bus.SystemBus.Endpoint(networkmanager.BusAddress, client.log)
157 client.systemImageEndp = bus.SystemBus.Endpoint(systemimage.BusAddress, client.log)164 client.systemImageEndp = bus.SystemBus.Endpoint(systemimage.BusAddress, client.log)
158165
@@ -203,6 +210,15 @@
203 return setup, nil210 return setup, nil
204}211}
205212
213// derivePostalServiceSetup derives the service setup from the client configuration bits.
214func (client *PushClient) derivePostalServiceSetup() *service.PostalServiceSetup {
215 return &service.PostalServiceSetup{
216 InstalledChecker: client.installedChecker,
217 FallbackVibration: client.config.FallbackVibration,
218 FallbackSound: client.config.FallbackSound,
219 }
220}
221
206// getAuthorization gets the authorization blob to send to the server222// getAuthorization gets the authorization blob to send to the server
207func (client *PushClient) getAuthorization(url string) string {223func (client *PushClient) getAuthorization(url string) string {
208 client.log.Debugf("getting authorization for %s", url)224 client.log.Debugf("getting authorization for %s", url)
@@ -222,16 +238,12 @@
222 }238 }
223}239}
224240
225// getDeviceId gets the whoopsie identifier for the device241// getDeviceId gets the identifier for the device
226func (client *PushClient) getDeviceId() error {242func (client *PushClient) getDeviceId() error {
227 err := client.idder.Generate()
228 if err != nil {
229 return err
230 }
231 baseId := client.idder.String()243 baseId := client.idder.String()
232 b, err := hex.DecodeString(baseId)244 b, err := hex.DecodeString(baseId)
233 if err != nil {245 if err != nil {
234 return fmt.Errorf("whoopsie id should be hex: %v", err)246 return fmt.Errorf("machine-id should be hex: %v", err)
235 }247 }
236 h := sha256.Sum224(b)248 h := sha256.Sum224(b)
237 client.deviceId = base64.StdEncoding.EncodeToString(h[:])249 client.deviceId = base64.StdEncoding.EncodeToString(h[:])
@@ -294,7 +306,7 @@
294 switch err {306 switch err {
295 default:307 default:
296 client.log.Debugf("notification %#v for invalid app id %#v.", notif.MsgId, notif.AppId)308 client.log.Debugf("notification %#v for invalid app id %#v.", notif.MsgId, notif.AppId)
297 case click.ErrMissingAppId:309 case click.ErrMissingApp:
298 client.log.Debugf("notification %#v for missing app id %#v.", notif.MsgId, notif.AppId)310 client.log.Debugf("notification %#v for missing app id %#v.", notif.MsgId, notif.AppId)
299 client.unregisterCh <- parsed311 client.unregisterCh <- parsed
300 parsed = nil312 parsed = nil
@@ -473,7 +485,8 @@
473}485}
474486
475func (client *PushClient) setupPostalService() error {487func (client *PushClient) setupPostalService() error {
476 client.postalService = service.NewPostalService(client.installedChecker, client.log)488 setup := client.derivePostalServiceSetup()
489 client.postalService = service.NewPostalService(setup, client.log)
477 return nil490 return nil
478}491}
479492
480493
=== modified file 'client/client_test.go'
--- client/client_test.go 2014-07-21 17:38:28 +0000
+++ client/client_test.go 2014-08-11 21:30:50 +0000
@@ -42,12 +42,13 @@
42 "launchpad.net/ubuntu-push/client/session"42 "launchpad.net/ubuntu-push/client/session"
43 "launchpad.net/ubuntu-push/client/session/seenstate"43 "launchpad.net/ubuntu-push/client/session/seenstate"
44 "launchpad.net/ubuntu-push/config"44 "launchpad.net/ubuntu-push/config"
45 "launchpad.net/ubuntu-push/identifier"
46 idtesting "launchpad.net/ubuntu-push/identifier/testing"
47 "launchpad.net/ubuntu-push/launch_helper"
45 "launchpad.net/ubuntu-push/protocol"48 "launchpad.net/ubuntu-push/protocol"
46 helpers "launchpad.net/ubuntu-push/testing"49 helpers "launchpad.net/ubuntu-push/testing"
47 "launchpad.net/ubuntu-push/testing/condition"50 "launchpad.net/ubuntu-push/testing/condition"
48 "launchpad.net/ubuntu-push/util"51 "launchpad.net/ubuntu-push/util"
49 "launchpad.net/ubuntu-push/whoopsie/identifier"
50 idtesting "launchpad.net/ubuntu-push/whoopsie/identifier/testing"
51)52)
5253
53func TestClient(t *testing.T) { TestingT(t) }54func TestClient(t *testing.T) { TestingT(t) }
@@ -154,6 +155,8 @@
154func (cs *clientSuite) writeTestConfig(overrides map[string]interface{}) {155func (cs *clientSuite) writeTestConfig(overrides map[string]interface{}) {
155 pem_file := helpers.SourceRelative("../server/acceptance/ssl/testing.cert")156 pem_file := helpers.SourceRelative("../server/acceptance/ssl/testing.cert")
156 cfgMap := map[string]interface{}{157 cfgMap := map[string]interface{}{
158 "fallback_vibration": &launch_helper.Vibration{Pattern: []uint32{1}},
159 "fallback_sound": "sounds/ubuntu/notifications/Blip.ogg",
157 "connect_timeout": "7ms",160 "connect_timeout": "7ms",
158 "exchange_timeout": "10ms",161 "exchange_timeout": "10ms",
159 "hosts_cache_expiry": "1h",162 "hosts_cache_expiry": "1h",
@@ -239,7 +242,8 @@
239 c.Check(cli.idder, IsNil)242 c.Check(cli.idder, IsNil)
240 err := cli.configure()243 err := cli.configure()
241 c.Assert(err, IsNil)244 c.Assert(err, IsNil)
242 c.Assert(cli.idder, FitsTypeOf, identifier.New())245 newIdder, err := identifier.New()
246 c.Assert(cli.idder, FitsTypeOf, newIdder)
243}247}
244248
245func (cs *clientSuite) TestConfigureSetsUpEndpoints(c *C) {249func (cs *clientSuite) TestConfigureSetsUpEndpoints(c *C) {
@@ -471,6 +475,32 @@
471}475}
472476
473/*****************************************************************477/*****************************************************************
478 derivePostalConfig tests
479******************************************************************/
480func (cs *clientSuite) TestDerivePostalServiceSetup(c *C) {
481 cs.writeTestConfig(map[string]interface{}{})
482 cli := NewPushClient(cs.configPath, cs.leveldbPath)
483 err := cli.configure()
484 c.Assert(err, IsNil)
485 expected := &service.PostalServiceSetup{
486 InstalledChecker: cli.installedChecker,
487 FallbackVibration: cli.config.FallbackVibration,
488 FallbackSound: cli.config.FallbackSound,
489 }
490 // sanity check that we are looking at all fields
491 vExpected := reflect.ValueOf(expected).Elem()
492 nf := vExpected.NumField()
493 for i := 0; i < nf; i++ {
494 fv := vExpected.Field(i)
495 // field isn't empty/zero
496 c.Assert(fv.Interface(), Not(DeepEquals), reflect.Zero(fv.Type()).Interface(), Commentf("forgot about: %s", vExpected.Type().Field(i).Name))
497 }
498 // finally compare
499 setup := cli.derivePostalServiceSetup()
500 c.Check(setup, DeepEquals, expected)
501}
502
503/*****************************************************************
474 startService tests504 startService tests
475******************************************************************/505******************************************************************/
476506
@@ -536,7 +566,7 @@
536func (cs *clientSuite) TestGetDeviceIdWorks(c *C) {566func (cs *clientSuite) TestGetDeviceIdWorks(c *C) {
537 cli := NewPushClient(cs.configPath, cs.leveldbPath)567 cli := NewPushClient(cs.configPath, cs.leveldbPath)
538 cli.log = cs.log568 cli.log = cs.log
539 cli.idder = identifier.New()569 cli.idder, _ = identifier.New()
540 c.Check(cli.deviceId, Equals, "")570 c.Check(cli.deviceId, Equals, "")
541 c.Check(cli.getDeviceId(), IsNil)571 c.Check(cli.getDeviceId(), IsNil)
542 c.Check(cli.deviceId, HasLen, 40)572 c.Check(cli.deviceId, HasLen, 40)
@@ -550,14 +580,14 @@
550 c.Check(cli.getDeviceId(), NotNil)580 c.Check(cli.getDeviceId(), NotNil)
551}581}
552582
553func (cs *clientSuite) TestGetDeviceIdWhoopsieDoesTheUnexpected(c *C) {583func (cs *clientSuite) TestGetDeviceIdIdentifierDoesTheUnexpected(c *C) {
554 cli := NewPushClient(cs.configPath, cs.leveldbPath)584 cli := NewPushClient(cs.configPath, cs.leveldbPath)
555 cli.log = cs.log585 cli.log = cs.log
556 settable := idtesting.Settable()586 settable := idtesting.Settable()
557 cli.idder = settable587 cli.idder = settable
558 settable.Set("not-hex")588 settable.Set("not-hex")
559 c.Check(cli.deviceId, Equals, "")589 c.Check(cli.deviceId, Equals, "")
560 c.Check(cli.getDeviceId(), ErrorMatches, "whoopsie id should be hex: .*")590 c.Check(cli.getDeviceId(), ErrorMatches, "machine-id should be hex: .*")
561}591}
562592
563/*****************************************************************593/*****************************************************************
@@ -1111,7 +1141,7 @@
1111 // and now everthing is better! We have a config,1141 // and now everthing is better! We have a config,
1112 c.Check(string(cli.config.Addr), Equals, ":0")1142 c.Check(string(cli.config.Addr), Equals, ":0")
1113 // and a device id,1143 // and a device id,
1114 c.Check(cli.deviceId, HasLen, 40)1144 c.Check(cli.deviceId, HasLen, 32)
1115 // and a session,1145 // and a session,
1116 c.Check(cli.session, NotNil)1146 c.Check(cli.session, NotNil)
1117 // and a bus,1147 // and a bus,
11181148
=== modified file 'client/service/common.go'
--- client/service/common.go 2014-07-15 20:53:41 +0000
+++ client/service/common.go 2014-08-11 21:30:50 +0000
@@ -52,7 +52,7 @@
52 ErrBadArgCount = errors.New("wrong number of arguments")52 ErrBadArgCount = errors.New("wrong number of arguments")
53 ErrBadArgType = errors.New("bad argument type")53 ErrBadArgType = errors.New("bad argument type")
54 ErrBadJSON = errors.New("bad json data")54 ErrBadJSON = errors.New("bad json data")
55 ErrBadAppId = errors.New("package must be prefix of app id")55 ErrAppIdMismatch = errors.New("package must be prefix of app id")
56)56)
5757
58// IsRunning() returns whether the service's state is StateRunning58// IsRunning() returns whether the service's state is StateRunning
@@ -118,10 +118,10 @@
118 pkgname := string(nih.Unquote([]byte(path[strings.LastIndex(path, "/")+1:])))118 pkgname := string(nih.Unquote([]byte(path[strings.LastIndex(path, "/")+1:])))
119 app, err = click.ParseAndVerifyAppId(id, svc.installedChecker)119 app, err = click.ParseAndVerifyAppId(id, svc.installedChecker)
120 if err != nil {120 if err != nil {
121 return nil, ErrBadAppId121 return nil, err
122 }122 }
123 if !app.InPackage(pkgname) {123 if !app.InPackage(pkgname) {
124 return nil, ErrBadAppId124 return nil, ErrAppIdMismatch
125 }125 }
126 return126 return
127}127}
128128
=== modified file 'client/service/common_test.go'
--- client/service/common_test.go 2014-07-08 08:32:32 +0000
+++ client/service/common_test.go 2014-08-11 21:30:50 +0000
@@ -18,6 +18,8 @@
1818
19import (19import (
20 . "launchpad.net/gocheck"20 . "launchpad.net/gocheck"
21
22 "launchpad.net/ubuntu-push/click"
21)23)
2224
23type commonSuite struct{}25type commonSuite struct{}
@@ -35,10 +37,16 @@
35 c.Check(app.Original(), Equals, anAppId)37 c.Check(app.Original(), Equals, anAppId)
36}38}
3739
40type fakeInstalledChecker struct{}
41
42func (fakeInstalledChecker) Installed(app *click.AppId, setVersion bool) bool {
43 return app.Original()[0] == 'c'
44}
45
38func (cs *commonSuite) TestGrabDBusPackageAndAppIdFails(c *C) {46func (cs *commonSuite) TestGrabDBusPackageAndAppIdFails(c *C) {
39 svc := new(DBusService)47 svc := new(DBusService)
48 svc.installedChecker = fakeInstalledChecker{}
40 aDBusPath := "/com/ubuntu/Postal/com_2eexample_2etest"49 aDBusPath := "/com/ubuntu/Postal/com_2eexample_2etest"
41 aDBusPath2 := "/com/ubuntu/Postal/com_2efoo_2ebar"
42 aPackage := "com.example.test"50 aPackage := "com.example.test"
43 anAppId := aPackage + "_test"51 anAppId := aPackage + "_test"
4452
@@ -52,8 +60,9 @@
52 {aDBusPath, []interface{}{anAppId}, 1, ErrBadArgCount},60 {aDBusPath, []interface{}{anAppId}, 1, ErrBadArgCount},
53 {aDBusPath, []interface{}{anAppId, anAppId}, 0, ErrBadArgCount},61 {aDBusPath, []interface{}{anAppId, anAppId}, 0, ErrBadArgCount},
54 {aDBusPath, []interface{}{1}, 0, ErrBadArgType},62 {aDBusPath, []interface{}{1}, 0, ErrBadArgType},
55 {aDBusPath, []interface{}{aPackage}, 0, ErrBadAppId},63 {aDBusPath, []interface{}{aPackage}, 0, click.ErrInvalidAppId},
56 {aDBusPath2, []interface{}{anAppId}, 0, ErrBadAppId},64 {aDBusPath, []interface{}{"x" + anAppId}, 0, click.ErrMissingApp},
65 {aDBusPath, []interface{}{"c" + anAppId}, 0, ErrAppIdMismatch},
57 } {66 } {
58 comment := Commentf("iteration #%d", i)67 comment := Commentf("iteration #%d", i)
59 app, err := svc.grabDBusPackageAndAppId(s.path, s.args, s.numExtra)68 app, err := svc.grabDBusPackageAndAppId(s.path, s.args, s.numExtra)
6069
=== modified file 'client/service/postal.go'
--- client/service/postal.go 2014-08-01 14:17:04 +0000
+++ client/service/postal.go 2014-08-11 21:30:50 +0000
@@ -56,6 +56,13 @@
56 Clear(*click.AppId, ...string) int56 Clear(*click.AppId, ...string) int
57}57}
5858
59// PostalServiceSetup is a configuration object for the service
60type PostalServiceSetup struct {
61 InstalledChecker click.InstalledChecker
62 FallbackVibration *launch_helper.Vibration
63 FallbackSound string
64}
65
59// PostalService is the dbus api66// PostalService is the dbus api
60type PostalService struct {67type PostalService struct {
61 DBusService68 DBusService
@@ -80,6 +87,9 @@
80 // the url dispatcher, used for stuff.87 // the url dispatcher, used for stuff.
81 urlDispatcher urldispatcher.URLDispatcher88 urlDispatcher urldispatcher.URLDispatcher
82 windowStack *windowstack.WindowStack89 windowStack *windowstack.WindowStack
90 // fallback values for simplified notification usage
91 fallbackVibration *launch_helper.Vibration
92 fallbackSound string
83}93}
8494
85var (95var (
@@ -96,11 +106,13 @@
96)106)
97107
98// NewPostalService() builds a new service and returns it.108// NewPostalService() builds a new service and returns it.
99func NewPostalService(installedChecker click.InstalledChecker, log logger.Logger) *PostalService {109func NewPostalService(setup *PostalServiceSetup, log logger.Logger) *PostalService {
100 var svc = &PostalService{}110 var svc = &PostalService{}
101 svc.Log = log111 svc.Log = log
102 svc.Bus = bus.SessionBus.Endpoint(PostalServiceBusAddress, log)112 svc.Bus = bus.SessionBus.Endpoint(PostalServiceBusAddress, log)
103 svc.installedChecker = installedChecker113 svc.installedChecker = setup.InstalledChecker
114 svc.fallbackVibration = setup.FallbackVibration
115 svc.fallbackSound = setup.FallbackSound
104 svc.NotificationsEndp = bus.SessionBus.Endpoint(notifications.BusAddress, log)116 svc.NotificationsEndp = bus.SessionBus.Endpoint(notifications.BusAddress, log)
105 svc.EmblemCounterEndp = bus.SessionBus.Endpoint(emblemcounter.BusAddress, log)117 svc.EmblemCounterEndp = bus.SessionBus.Endpoint(emblemcounter.BusAddress, log)
106 svc.HapticEndp = bus.SessionBus.Endpoint(haptic.BusAddress, log)118 svc.HapticEndp = bus.SessionBus.Endpoint(haptic.BusAddress, log)
@@ -144,8 +156,8 @@
144 svc.urlDispatcher = urldispatcher.New(svc.URLDispatcherEndp, svc.Log)156 svc.urlDispatcher = urldispatcher.New(svc.URLDispatcherEndp, svc.Log)
145 svc.notifications = notifications.Raw(svc.NotificationsEndp, svc.Log)157 svc.notifications = notifications.Raw(svc.NotificationsEndp, svc.Log)
146 svc.emblemCounter = emblemcounter.New(svc.EmblemCounterEndp, svc.Log)158 svc.emblemCounter = emblemcounter.New(svc.EmblemCounterEndp, svc.Log)
147 svc.haptic = haptic.New(svc.HapticEndp, svc.Log)159 svc.haptic = haptic.New(svc.HapticEndp, svc.Log, svc.fallbackVibration)
148 svc.sound = sounds.New(svc.Log)160 svc.sound = sounds.New(svc.Log, svc.fallbackSound)
149 svc.messagingMenu = messaging.New(svc.Log)161 svc.messagingMenu = messaging.New(svc.Log)
150 svc.Presenters = []Presenter{162 svc.Presenters = []Presenter{
151 svc.notifications,163 svc.notifications,
152164
=== modified file 'client/service/postal_test.go'
--- client/service/postal_test.go 2014-08-01 14:17:04 +0000
+++ client/service/postal_test.go 2014-08-11 21:30:50 +0000
@@ -133,6 +133,7 @@
133133
134type postalSuite struct {134type postalSuite struct {
135 log *helpers.TestLogger135 log *helpers.TestLogger
136 cfg *PostalServiceSetup
136 bus bus.Endpoint137 bus bus.Endpoint
137 notifBus bus.Endpoint138 notifBus bus.Endpoint
138 counterBus bus.Endpoint139 counterBus bus.Endpoint
@@ -160,6 +161,7 @@
160 ps.oldIsBlisted = isBlacklisted161 ps.oldIsBlisted = isBlacklisted
161 isBlacklisted = func(*click.AppId) bool { return ps.blacklisted }162 isBlacklisted = func(*click.AppId) bool { return ps.blacklisted }
162 ps.log = helpers.NewTestLogger(c, "debug")163 ps.log = helpers.NewTestLogger(c, "debug")
164 ps.cfg = &PostalServiceSetup{}
163 ps.bus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true))165 ps.bus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true))
164 ps.notifBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true))166 ps.notifBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true))
165 ps.counterBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true))167 ps.counterBus = testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true))
@@ -204,7 +206,7 @@
204}206}
205207
206func (ps *postalSuite) TestStart(c *C) {208func (ps *postalSuite) TestStart(c *C) {
207 svc := ps.replaceBuses(NewPostalService(nil, ps.log))209 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
208 c.Check(svc.IsRunning(), Equals, false)210 c.Check(svc.IsRunning(), Equals, false)
209 c.Check(svc.Start(), IsNil)211 c.Check(svc.Start(), IsNil)
210 c.Check(svc.IsRunning(), Equals, true)212 c.Check(svc.IsRunning(), Equals, true)
@@ -212,30 +214,30 @@
212}214}
213215
214func (ps *postalSuite) TestStartTwice(c *C) {216func (ps *postalSuite) TestStartTwice(c *C) {
215 svc := ps.replaceBuses(NewPostalService(nil, ps.log))217 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
216 c.Check(svc.Start(), IsNil)218 c.Check(svc.Start(), IsNil)
217 c.Check(svc.Start(), Equals, ErrAlreadyStarted)219 c.Check(svc.Start(), Equals, ErrAlreadyStarted)
218 svc.Stop()220 svc.Stop()
219}221}
220222
221func (ps *postalSuite) TestStartNoLog(c *C) {223func (ps *postalSuite) TestStartNoLog(c *C) {
222 svc := ps.replaceBuses(NewPostalService(nil, nil))224 svc := ps.replaceBuses(NewPostalService(ps.cfg, nil))
223 c.Check(svc.Start(), Equals, ErrNotConfigured)225 c.Check(svc.Start(), Equals, ErrNotConfigured)
224}226}
225227
226func (ps *postalSuite) TestStartNoBus(c *C) {228func (ps *postalSuite) TestStartNoBus(c *C) {
227 svc := ps.replaceBuses(NewPostalService(nil, ps.log))229 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
228 svc.Bus = nil230 svc.Bus = nil
229 c.Check(svc.Start(), Equals, ErrNotConfigured)231 c.Check(svc.Start(), Equals, ErrNotConfigured)
230232
231 svc = ps.replaceBuses(NewPostalService(nil, ps.log))233 svc = ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
232 svc.NotificationsEndp = nil234 svc.NotificationsEndp = nil
233 c.Check(svc.Start(), Equals, ErrNotConfigured)235 c.Check(svc.Start(), Equals, ErrNotConfigured)
234}236}
235237
236func (ps *postalSuite) TestTakeTheBusFail(c *C) {238func (ps *postalSuite) TestTakeTheBusFail(c *C) {
237 nEndp := testibus.NewMultiValuedTestingEndpoint(condition.Work(true), condition.Work(false))239 nEndp := testibus.NewMultiValuedTestingEndpoint(condition.Work(true), condition.Work(false))
238 svc := ps.replaceBuses(NewPostalService(nil, ps.log))240 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
239 svc.NotificationsEndp = nEndp241 svc.NotificationsEndp = nEndp
240 _, err := svc.takeTheBus()242 _, err := svc.takeTheBus()
241 c.Check(err, NotNil)243 c.Check(err, NotNil)
@@ -243,7 +245,7 @@
243245
244func (ps *postalSuite) TestTakeTheBusOk(c *C) {246func (ps *postalSuite) TestTakeTheBusOk(c *C) {
245 nEndp := testibus.NewMultiValuedTestingEndpoint(condition.Work(true), condition.Work(true), []interface{}{uint32(1), "hello"})247 nEndp := testibus.NewMultiValuedTestingEndpoint(condition.Work(true), condition.Work(true), []interface{}{uint32(1), "hello"})
246 svc := ps.replaceBuses(NewPostalService(nil, ps.log))248 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
247 svc.NotificationsEndp = nEndp249 svc.NotificationsEndp = nEndp
248 _, err := svc.takeTheBus()250 _, err := svc.takeTheBus()
249 c.Check(err, IsNil)251 c.Check(err, IsNil)
@@ -251,14 +253,14 @@
251253
252func (ps *postalSuite) TestStartFailsOnBusDialFailure(c *C) {254func (ps *postalSuite) TestStartFailsOnBusDialFailure(c *C) {
253 // XXX actually, we probably want to autoredial this255 // XXX actually, we probably want to autoredial this
254 svc := ps.replaceBuses(NewPostalService(nil, ps.log))256 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
255 svc.Bus = testibus.NewTestingEndpoint(condition.Work(false), nil)257 svc.Bus = testibus.NewTestingEndpoint(condition.Work(false), nil)
256 c.Check(svc.Start(), ErrorMatches, `.*(?i)cond said no.*`)258 c.Check(svc.Start(), ErrorMatches, `.*(?i)cond said no.*`)
257 svc.Stop()259 svc.Stop()
258}260}
259261
260func (ps *postalSuite) TestStartGrabsName(c *C) {262func (ps *postalSuite) TestStartGrabsName(c *C) {
261 svc := ps.replaceBuses(NewPostalService(nil, ps.log))263 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
262 c.Assert(svc.Start(), IsNil)264 c.Assert(svc.Start(), IsNil)
263 callArgs := testibus.GetCallArgs(ps.bus)265 callArgs := testibus.GetCallArgs(ps.bus)
264 defer svc.Stop()266 defer svc.Stop()
@@ -267,7 +269,7 @@
267}269}
268270
269func (ps *postalSuite) TestStopClosesBus(c *C) {271func (ps *postalSuite) TestStopClosesBus(c *C) {
270 svc := ps.replaceBuses(NewPostalService(nil, ps.log))272 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
271 c.Assert(svc.Start(), IsNil)273 c.Assert(svc.Start(), IsNil)
272 svc.Stop()274 svc.Stop()
273 callArgs := testibus.GetCallArgs(ps.bus)275 callArgs := testibus.GetCallArgs(ps.bus)
@@ -279,7 +281,7 @@
279// post() tests281// post() tests
280282
281func (ps *postalSuite) TestPostHappyPath(c *C) {283func (ps *postalSuite) TestPostHappyPath(c *C) {
282 svc := ps.replaceBuses(NewPostalService(nil, ps.log))284 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
283 svc.msgHandler = nil285 svc.msgHandler = nil
284 ch := installTickMessageHandler(svc)286 ch := installTickMessageHandler(svc)
285 svc.launchers = map[string]launch_helper.HelperLauncher{287 svc.launchers = map[string]launch_helper.HelperLauncher{
@@ -322,7 +324,7 @@
322 {[]interface{}{anAppId, "zoom"}, ErrBadJSON},324 {[]interface{}{anAppId, "zoom"}, ErrBadJSON},
323 {[]interface{}{1, "hello"}, ErrBadArgType},325 {[]interface{}{1, "hello"}, ErrBadArgType},
324 {[]interface{}{1, 2, 3}, ErrBadArgCount},326 {[]interface{}{1, 2, 3}, ErrBadArgCount},
325 {[]interface{}{"bar", "hello"}, ErrBadAppId},327 {[]interface{}{"bar", "hello"}, click.ErrInvalidAppId},
326 } {328 } {
327 reg, err := new(PostalService).post(aPackageOnBus, s.args, nil)329 reg, err := new(PostalService).post(aPackageOnBus, s.args, nil)
328 c.Check(reg, IsNil, Commentf("iteration #%d", i))330 c.Check(reg, IsNil, Commentf("iteration #%d", i))
@@ -333,7 +335,7 @@
333// Post() tests335// Post() tests
334336
335func (ps *postalSuite) TestPostWorks(c *C) {337func (ps *postalSuite) TestPostWorks(c *C) {
336 svc := ps.replaceBuses(NewPostalService(nil, ps.log))338 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
337 svc.msgHandler = nil339 svc.msgHandler = nil
338 ch := installTickMessageHandler(svc)340 ch := installTickMessageHandler(svc)
339 fakeLauncher2 := &fakeHelperLauncher{ch: make(chan []byte)}341 fakeLauncher2 := &fakeHelperLauncher{ch: make(chan []byte)}
@@ -384,7 +386,7 @@
384386
385func (ps *postalSuite) TestPostCallsMessageHandlerDetails(c *C) {387func (ps *postalSuite) TestPostCallsMessageHandlerDetails(c *C) {
386 ch := make(chan *launch_helper.HelperOutput)388 ch := make(chan *launch_helper.HelperOutput)
387 svc := ps.replaceBuses(NewPostalService(nil, ps.log))389 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
388 svc.launchers = map[string]launch_helper.HelperLauncher{390 svc.launchers = map[string]launch_helper.HelperLauncher{
389 "click": ps.fakeLauncher,391 "click": ps.fakeLauncher,
390 }392 }
@@ -410,7 +412,7 @@
410}412}
411413
412func (ps *postalSuite) TestAfterMessageHandlerSignal(c *C) {414func (ps *postalSuite) TestAfterMessageHandlerSignal(c *C) {
413 svc := ps.replaceBuses(NewPostalService(nil, ps.log))415 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
414 svc.msgHandler = nil416 svc.msgHandler = nil
415417
416 hInp := &launch_helper.HelperInput{418 hInp := &launch_helper.HelperInput{
@@ -431,7 +433,7 @@
431}433}
432434
433func (ps *postalSuite) TestFailingMessageHandlerSurvived(c *C) {435func (ps *postalSuite) TestFailingMessageHandlerSurvived(c *C) {
434 svc := ps.replaceBuses(NewPostalService(nil, ps.log))436 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
435 svc.SetMessageHandler(func(*click.AppId, string, *launch_helper.HelperOutput) bool {437 svc.SetMessageHandler(func(*click.AppId, string, *launch_helper.HelperOutput) bool {
436 return false438 return false
437 })439 })
@@ -453,7 +455,7 @@
453//455//
454// Notifications tests456// Notifications tests
455func (ps *postalSuite) TestNotificationsWorks(c *C) {457func (ps *postalSuite) TestNotificationsWorks(c *C) {
456 svc := ps.replaceBuses(NewPostalService(nil, ps.log))458 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
457 nots, err := svc.popAll(aPackageOnBus, []interface{}{anAppId}, nil)459 nots, err := svc.popAll(aPackageOnBus, []interface{}{anAppId}, nil)
458 c.Assert(err, IsNil)460 c.Assert(err, IsNil)
459 c.Assert(nots, NotNil)461 c.Assert(nots, NotNil)
@@ -487,7 +489,7 @@
487 {nil, ErrBadArgCount},489 {nil, ErrBadArgCount},
488 {[]interface{}{}, ErrBadArgCount},490 {[]interface{}{}, ErrBadArgCount},
489 {[]interface{}{1}, ErrBadArgType},491 {[]interface{}{1}, ErrBadArgType},
490 {[]interface{}{"potato"}, ErrBadAppId},492 {[]interface{}{"potato"}, click.ErrInvalidAppId},
491 } {493 } {
492 reg, err := new(PostalService).popAll(aPackageOnBus, s.args, nil)494 reg, err := new(PostalService).popAll(aPackageOnBus, s.args, nil)
493 c.Check(reg, IsNil, Commentf("iteration #%d", i))495 c.Check(reg, IsNil, Commentf("iteration #%d", i))
@@ -510,7 +512,7 @@
510512
511func (ps *postalSuite) TestMessageHandlerPresents(c *C) {513func (ps *postalSuite) TestMessageHandlerPresents(c *C) {
512 endp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), uint32(1))514 endp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), uint32(1))
513 svc := NewPostalService(nil, ps.log)515 svc := NewPostalService(ps.cfg, ps.log)
514 svc.Bus = endp516 svc.Bus = endp
515 svc.EmblemCounterEndp = endp517 svc.EmblemCounterEndp = endp
516 svc.HapticEndp = endp518 svc.HapticEndp = endp
@@ -518,13 +520,14 @@
518 svc.URLDispatcherEndp = ps.urlDispBus520 svc.URLDispatcherEndp = ps.urlDispBus
519 svc.WindowStackEndp = ps.winStackBus521 svc.WindowStackEndp = ps.winStackBus
520 svc.launchers = map[string]launch_helper.HelperLauncher{}522 svc.launchers = map[string]launch_helper.HelperLauncher{}
523 svc.fallbackVibration = &launch_helper.Vibration{Pattern: []uint32{1}}
521 c.Assert(svc.Start(), IsNil)524 c.Assert(svc.Start(), IsNil)
522525
523 // Persist is false so we just check the log526 // Persist is false so we just check the log
524 card := &launch_helper.Card{Icon: "icon-value", Summary: "summary-value", Body: "body-value", Popup: true, Persist: false}527 card := &launch_helper.Card{Icon: "icon-value", Summary: "summary-value", Body: "body-value", Popup: true, Persist: false}
525 vib := &launch_helper.Vibration{Duration: 500}528 vib := json.RawMessage(`true`)
526 emb := &launch_helper.EmblemCounter{Count: 2, Visible: true}529 emb := &launch_helper.EmblemCounter{Count: 2, Visible: true}
527 output := &launch_helper.HelperOutput{Notification: &launch_helper.Notification{Card: card, EmblemCounter: emb, Vibrate: vib}}530 output := &launch_helper.HelperOutput{Notification: &launch_helper.Notification{Card: card, EmblemCounter: emb, RawVibration: vib}}
528 b := svc.messageHandler(&click.AppId{}, "", output)531 b := svc.messageHandler(&click.AppId{}, "", output)
529 c.Assert(b, Equals, true)532 c.Assert(b, Equals, true)
530 args := testibus.GetCallArgs(endp)533 args := testibus.GetCallArgs(endp)
@@ -547,7 +550,7 @@
547550
548func (ps *postalSuite) TestMessageHandlerReportsFailedNotifies(c *C) {551func (ps *postalSuite) TestMessageHandlerReportsFailedNotifies(c *C) {
549 endp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), 1)552 endp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), 1)
550 svc := ps.replaceBuses(NewPostalService(nil, ps.log))553 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
551 svc.NotificationsEndp = endp554 svc.NotificationsEndp = endp
552 c.Assert(svc.Start(), IsNil)555 c.Assert(svc.Start(), IsNil)
553 card := &launch_helper.Card{Icon: "icon-value", Summary: "summary-value", Body: "body-value", Popup: true}556 card := &launch_helper.Card{Icon: "icon-value", Summary: "summary-value", Body: "body-value", Popup: true}
@@ -559,7 +562,7 @@
559562
560func (ps *postalSuite) TestMessageHandlerInhibition(c *C) {563func (ps *postalSuite) TestMessageHandlerInhibition(c *C) {
561 endp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), []windowstack.WindowsInfo{{0, "com.example.test_test-app", true, 0}})564 endp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), []windowstack.WindowsInfo{{0, "com.example.test_test-app", true, 0}})
562 svc := ps.replaceBuses(NewPostalService(nil, ps.log))565 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
563 svc.WindowStackEndp = endp566 svc.WindowStackEndp = endp
564 c.Assert(svc.Start(), IsNil)567 c.Assert(svc.Start(), IsNil)
565 output := &launch_helper.HelperOutput{Notification: &launch_helper.Notification{}} // Doesn't matter568 output := &launch_helper.HelperOutput{Notification: &launch_helper.Notification{}} // Doesn't matter
@@ -569,7 +572,7 @@
569}572}
570573
571func (ps *postalSuite) TestMessageHandlerReportsButIgnoresUnmarshalErrors(c *C) {574func (ps *postalSuite) TestMessageHandlerReportsButIgnoresUnmarshalErrors(c *C) {
572 svc := ps.replaceBuses(NewPostalService(nil, ps.log))575 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
573 c.Assert(svc.Start(), IsNil)576 c.Assert(svc.Start(), IsNil)
574 output := &launch_helper.HelperOutput{[]byte(`broken`), nil}577 output := &launch_helper.HelperOutput{[]byte(`broken`), nil}
575 b := svc.messageHandler(nil, "", output)578 b := svc.messageHandler(nil, "", output)
@@ -579,7 +582,7 @@
579582
580func (ps *postalSuite) TestMessageHandlerReportsButIgnoresNilNotifies(c *C) {583func (ps *postalSuite) TestMessageHandlerReportsButIgnoresNilNotifies(c *C) {
581 endp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false))584 endp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false))
582 svc := ps.replaceBuses(NewPostalService(nil, ps.log))585 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
583 c.Assert(svc.Start(), IsNil)586 c.Assert(svc.Start(), IsNil)
584 svc.NotificationsEndp = endp587 svc.NotificationsEndp = endp
585 output := &launch_helper.HelperOutput{[]byte(`{}`), nil}588 output := &launch_helper.HelperOutput{[]byte(`{}`), nil}
@@ -589,7 +592,7 @@
589}592}
590593
591func (ps *postalSuite) TestMessageHandlerInvalidAction(c *C) {594func (ps *postalSuite) TestMessageHandlerInvalidAction(c *C) {
592 svc := ps.replaceBuses(NewPostalService(nil, ps.log))595 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
593 endp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false), []string{"com.example.test_test-app"})596 endp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(false), []string{"com.example.test_test-app"})
594 svc.URLDispatcherEndp = endp597 svc.URLDispatcherEndp = endp
595 c.Assert(svc.Start(), IsNil)598 c.Assert(svc.Start(), IsNil)
@@ -601,7 +604,7 @@
601}604}
602605
603func (ps *postalSuite) TestHandleActionsDispatches(c *C) {606func (ps *postalSuite) TestHandleActionsDispatches(c *C) {
604 svc := ps.replaceBuses(NewPostalService(nil, ps.log))607 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
605 fmm := new(fakeMM)608 fmm := new(fakeMM)
606 app, _ := click.ParseAppId("com.example.test_test-app")609 app, _ := click.ParseAppId("com.example.test_test-app")
607 c.Assert(svc.Start(), IsNil)610 c.Assert(svc.Start(), IsNil)
@@ -627,7 +630,7 @@
627}630}
628631
629func (ps *postalSuite) TestHandleMMUActionsDispatches(c *C) {632func (ps *postalSuite) TestHandleMMUActionsDispatches(c *C) {
630 svc := ps.replaceBuses(NewPostalService(nil, ps.log))633 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
631 c.Assert(svc.Start(), IsNil)634 c.Assert(svc.Start(), IsNil)
632 app, _ := click.ParseAppId("com.example.test_test-app")635 app, _ := click.ParseAppId("com.example.test_test-app")
633 aCh := make(chan *notifications.RawAction)636 aCh := make(chan *notifications.RawAction)
@@ -650,7 +653,7 @@
650}653}
651654
652func (ps *postalSuite) TestValidateActions(c *C) {655func (ps *postalSuite) TestValidateActions(c *C) {
653 svc := ps.replaceBuses(NewPostalService(nil, ps.log))656 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
654 endp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), []string{"com.example.test_test-app_0"})657 endp := testibus.NewTestingEndpoint(condition.Work(true), condition.Work(true), []string{"com.example.test_test-app_0"})
655 svc.URLDispatcherEndp = endp658 svc.URLDispatcherEndp = endp
656 c.Assert(svc.Start(), IsNil)659 c.Assert(svc.Start(), IsNil)
@@ -661,7 +664,7 @@
661}664}
662665
663func (ps *postalSuite) TestValidateActionsNoActions(c *C) {666func (ps *postalSuite) TestValidateActionsNoActions(c *C) {
664 svc := ps.replaceBuses(NewPostalService(nil, ps.log))667 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
665 card := launch_helper.Card{}668 card := launch_helper.Card{}
666 notif := &launch_helper.Notification{Card: &card}669 notif := &launch_helper.Notification{Card: &card}
667 b := svc.validateActions(clickhelp.MustParseAppId("com.example.test_test-app_0"), notif)670 b := svc.validateActions(clickhelp.MustParseAppId("com.example.test_test-app_0"), notif)
@@ -669,7 +672,7 @@
669}672}
670673
671func (ps *postalSuite) TestValidateActionsNoCard(c *C) {674func (ps *postalSuite) TestValidateActionsNoCard(c *C) {
672 svc := ps.replaceBuses(NewPostalService(nil, ps.log))675 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
673 notif := &launch_helper.Notification{}676 notif := &launch_helper.Notification{}
674 b := svc.validateActions(clickhelp.MustParseAppId("com.example.test_test-app_0"), notif)677 b := svc.validateActions(clickhelp.MustParseAppId("com.example.test_test-app_0"), notif)
675 c.Check(b, Equals, true)678 c.Check(b, Equals, true)
@@ -695,7 +698,7 @@
695}698}
696699
697func (ps *postalSuite) TestListPersistent(c *C) {700func (ps *postalSuite) TestListPersistent(c *C) {
698 svc := ps.replaceBuses(NewPostalService(nil, ps.log))701 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
699 fmm := new(fakeMM)702 fmm := new(fakeMM)
700 svc.messagingMenu = fmm703 svc.messagingMenu = fmm
701704
@@ -709,17 +712,24 @@
709}712}
710713
711func (ps *postalSuite) TestListPersistentErrors(c *C) {714func (ps *postalSuite) TestListPersistentErrors(c *C) {
712 svc := ps.replaceBuses(NewPostalService(nil, ps.log))715 for i, s := range []struct {
713 _, err := svc.listPersistent(aPackageOnBus, nil, nil)716 args []interface{}
714 c.Check(err, Equals, ErrBadArgCount)717 errt error
715 _, err = svc.listPersistent(aPackageOnBus, []interface{}{42}, nil)718 }{
716 c.Check(err, Equals, ErrBadArgType)719 {nil, ErrBadArgCount},
717 _, err = svc.listPersistent(aPackageOnBus, []interface{}{"xyzzy"}, nil)720 {[]interface{}{}, ErrBadArgCount},
718 c.Check(err, Equals, ErrBadAppId)721 {[]interface{}{1}, ErrBadArgType},
722 {[]interface{}{anAppId, 2}, ErrBadArgCount},
723 {[]interface{}{"bar"}, click.ErrInvalidAppId},
724 } {
725 reg, err := new(PostalService).listPersistent(aPackageOnBus, s.args, nil)
726 c.Check(reg, IsNil, Commentf("iteration #%d", i))
727 c.Check(err, Equals, s.errt, Commentf("iteration #%d", i))
728 }
719}729}
720730
721func (ps *postalSuite) TestClearPersistent(c *C) {731func (ps *postalSuite) TestClearPersistent(c *C) {
722 svc := ps.replaceBuses(NewPostalService(nil, ps.log))732 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
723 fmm := new(fakeMM)733 fmm := new(fakeMM)
724 svc.messagingMenu = fmm734 svc.messagingMenu = fmm
725735
@@ -730,24 +740,23 @@
730}740}
731741
732func (ps *postalSuite) TestClearPersistentErrors(c *C) {742func (ps *postalSuite) TestClearPersistentErrors(c *C) {
733 svc := ps.replaceBuses(NewPostalService(nil, ps.log))
734 for i, s := range []struct {743 for i, s := range []struct {
735 args []interface{}744 args []interface{}
736 err error745 err error
737 }{746 }{
738 {[]interface{}{}, ErrBadArgCount},747 {[]interface{}{}, ErrBadArgCount},
739 {[]interface{}{42}, ErrBadArgType},748 {[]interface{}{42}, ErrBadArgType},
740 {[]interface{}{"xyzzy"}, ErrBadAppId},749 {[]interface{}{"xyzzy"}, click.ErrInvalidAppId},
741 {[]interface{}{anAppId, 42}, ErrBadArgType},750 {[]interface{}{anAppId, 42}, ErrBadArgType},
742 {[]interface{}{anAppId, "", 42}, ErrBadArgType},751 {[]interface{}{anAppId, "", 42}, ErrBadArgType},
743 } {752 } {
744 _, err := svc.clearPersistent(aPackageOnBus, s.args, nil)753 _, err := new(PostalService).clearPersistent(aPackageOnBus, s.args, nil)
745 c.Check(err, Equals, s.err, Commentf("iter %d", i))754 c.Check(err, Equals, s.err, Commentf("iter %d", i))
746 }755 }
747}756}
748757
749func (ps *postalSuite) TestSetCounter(c *C) {758func (ps *postalSuite) TestSetCounter(c *C) {
750 svc := ps.replaceBuses(NewPostalService(nil, ps.log))759 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
751 c.Check(svc.Start(), IsNil)760 c.Check(svc.Start(), IsNil)
752761
753 _, err := svc.setCounter(aPackageOnBus, []interface{}{anAppId, int32(42), true}, nil)762 _, err := svc.setCounter(aPackageOnBus, []interface{}{anAppId, int32(42), true}, nil)
@@ -765,8 +774,9 @@
765}774}
766775
767func (ps *postalSuite) TestSetCounterErrors(c *C) {776func (ps *postalSuite) TestSetCounterErrors(c *C) {
768 svc := ps.replaceBuses(NewPostalService(nil, ps.log))777 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
769 svc.Start()778 svc.Start()
779
770 for i, s := range []struct {780 for i, s := range []struct {
771 args []interface{}781 args []interface{}
772 err error782 err error
@@ -776,7 +786,7 @@
776 {[]interface{}{anAppId}, ErrBadArgCount},786 {[]interface{}{anAppId}, ErrBadArgCount},
777 {[]interface{}{anAppId, int32(42)}, ErrBadArgCount},787 {[]interface{}{anAppId, int32(42)}, ErrBadArgCount},
778 {[]interface{}{anAppId, int32(42), true, "potato"}, ErrBadArgCount},788 {[]interface{}{anAppId, int32(42), true, "potato"}, ErrBadArgCount},
779 {[]interface{}{"xyzzy", int32(42), true}, ErrBadAppId},789 {[]interface{}{"xyzzy", int32(42), true}, click.ErrInvalidAppId},
780 {[]interface{}{1234567, int32(42), true}, ErrBadArgType},790 {[]interface{}{1234567, int32(42), true}, ErrBadArgType},
781 {[]interface{}{anAppId, "potatoe", true}, ErrBadArgType},791 {[]interface{}{anAppId, "potatoe", true}, ErrBadArgType},
782 {[]interface{}{anAppId, int32(42), "ru"}, ErrBadArgType},792 {[]interface{}{anAppId, int32(42), "ru"}, ErrBadArgType},
@@ -787,7 +797,7 @@
787}797}
788798
789func (ps *postalSuite) TestBlacklisted(c *C) {799func (ps *postalSuite) TestBlacklisted(c *C) {
790 svc := ps.replaceBuses(NewPostalService(nil, ps.log))800 svc := ps.replaceBuses(NewPostalService(ps.cfg, ps.log))
791 svc.Start()801 svc.Start()
792 ps.blacklisted = false802 ps.blacklisted = false
793803
794804
=== modified file 'client/service/service_test.go'
--- client/service/service_test.go 2014-07-14 19:51:43 +0000
+++ client/service/service_test.go 2014-08-11 21:30:50 +0000
@@ -28,6 +28,7 @@
2828
29 "launchpad.net/ubuntu-push/bus"29 "launchpad.net/ubuntu-push/bus"
30 testibus "launchpad.net/ubuntu-push/bus/testing"30 testibus "launchpad.net/ubuntu-push/bus/testing"
31 "launchpad.net/ubuntu-push/click"
31 "launchpad.net/ubuntu-push/logger"32 "launchpad.net/ubuntu-push/logger"
32 "launchpad.net/ubuntu-push/nih"33 "launchpad.net/ubuntu-push/nih"
33 helpers "launchpad.net/ubuntu-push/testing"34 helpers "launchpad.net/ubuntu-push/testing"
@@ -162,7 +163,7 @@
162 {nil, ErrBadArgCount},163 {nil, ErrBadArgCount},
163 {[]interface{}{}, ErrBadArgCount},164 {[]interface{}{}, ErrBadArgCount},
164 {[]interface{}{1}, ErrBadArgType},165 {[]interface{}{1}, ErrBadArgType},
165 {[]interface{}{"foo"}, ErrBadAppId},166 {[]interface{}{"foo"}, click.ErrInvalidAppId},
166 {[]interface{}{"foo", "bar"}, ErrBadArgCount},167 {[]interface{}{"foo", "bar"}, ErrBadArgCount},
167 } {168 } {
168 reg, err := new(PushService).register("/bar", s.args, nil)169 reg, err := new(PushService).register("/bar", s.args, nil)
169170
=== modified file 'debian/changelog'
--- debian/changelog 2014-08-04 15:38:34 +0000
+++ debian/changelog 2014-08-11 21:30:50 +0000
@@ -1,3 +1,25 @@
1ubuntu-push (0.61) UNRELEASED; urgency=medium
2
3 [ Guillermo Gonzalez ]
4 * Update autopilot tests to work with 0.50, fix setup.sh issues and add new tests for the broadcast notification changes.
5 * Replace whoopsie with /var/lib/dbus/machine-id to get the device ID.
6
7 [ John R. Lenton]
8 * Support simpler sounds API.
9 * Support simpler vibrations API.
10 * Remove Vibration's confusing and redundant Duration attribute.
11 * Change PostalService's New() to take a setup object.
12 * goctest.
13 * Make messaging menu entries show current time instead of epoch for timestamp of 0.
14 * Tweak the upstart script, start after unity.
15 * Correctly report invalid app ids, missing apps, and package/app id mismatches as separate errors over dbus.
16
17 [Roberto Alsina]
18 * Check that sound paths don't go up into the tree.
19 * Initial draft of QML-based doc
20
21 -- Roberto Alsina <ralsina@yoga> Mon, 11 Aug 2014 18:23:32 -0300
22
1ubuntu-push (0.60+14.10.20140804-0ubuntu1) utopic; urgency=medium23ubuntu-push (0.60+14.10.20140804-0ubuntu1) utopic; urgency=medium
224
3 [ Guillermo Gonzalez ]25 [ Guillermo Gonzalez ]
426
=== modified file 'debian/config.json'
--- debian/config.json 2014-07-03 13:59:15 +0000
+++ debian/config.json 2014-08-11 21:30:50 +0000
@@ -12,5 +12,7 @@
12 "recheck_timeout": "10m",12 "recheck_timeout": "10m",
13 "connectivity_check_url": "http://start.ubuntu.com/connectivity-check.html",13 "connectivity_check_url": "http://start.ubuntu.com/connectivity-check.html",
14 "connectivity_check_md5": "4589f42e1546aa47ca181e5d949d310b",14 "connectivity_check_md5": "4589f42e1546aa47ca181e5d949d310b",
15 "log_level": "debug"15 "log_level": "debug",
16 "fallback_vibration": {"pattern": [100, 100], "repeat": 2},
17 "fallback_sound": "sounds/ubuntu/notifications/Slick.ogg"
16}18}
1719
=== modified file 'debian/ubuntu-push-client.conf'
--- debian/ubuntu-push-client.conf 2014-06-02 10:01:13 +0000
+++ debian/ubuntu-push-client.conf 2014-08-11 21:30:50 +0000
@@ -1,7 +1,9 @@
1description "ubuntu push notification client-side daemon"1description "ubuntu push notification client-side daemon"
22
3start on started dbus3start on started unity8
4stop on stopped dbus4stop on stopping unity8
55
6exec /usr/lib/ubuntu-push-client/ubuntu-push-client6exec /usr/lib/ubuntu-push-client/ubuntu-push-client
7respawn7respawn
8
9post-stop exec initctl emit untrusted-helper-type-end HELPER_TYPE=push-helper
810
=== added file 'docs/highlevel.txt'
--- docs/highlevel.txt 1970-01-01 00:00:00 +0000
+++ docs/highlevel.txt 2014-08-11 21:30:50 +0000
@@ -0,0 +1,325 @@
1Ubuntu Push Client Developer Guide
2==================================
3
4:Version: 0.50+
5
6Introduction
7------------
8
9This document describes how to use the Ubuntu Push Client service from the point of view of a developer writing
10a QML-based application.
11
12---------
13
14Let's describe the push system by way of an example.
15
16Alice has written a chat application called Chatter. Using it, Bob can send messages to Carol and viceversa. Alice has a
17web application for it, so the way it works now is that Bob connects to the service, posts a message, and when Carol
18connects, she gets it. If Carol leaves the browser window open, it beeps when messages arrive.
19
20Now Alice wants to create an Ubuntu Touch app for Chatter, so she implements the same architecture using a client that
21does the same thing as the web browser. Sadly, since applications on Ubuntu Touch don't run continuously, messages are
22only delivered when Carol opens the app, and the user experience suffers.
23
24Using the Ubuntu Push Server, this problem is alleviated: the Chatter server will deliver the messages to the Ubuntu
25Push Server, which in turn will send it in an efficient manner to the Ubuntu Push Client running in Bob and Carol's
26devices. The user sees a notification (all without starting the app) and then can launch it if he's interested in
27reading messages at that point.
28
29Since the app is not started and messages are delivered oportunistically, this is both battery and bandwidth-efficient.
30
31.. figure:: push.svg
32
33The Ubuntu Push system provides:
34
35* A push server which receives **push messages** from the app servers, queues them and delivers them efficiently
36 to the devices.
37* A push client which receives those messages, queues messages to the app and displays notifications to the user
38
39The full lifecycle of a push message is:
40
41* Created in a application-specific server
42* Sent to the Ubuntu Push server, targeted at a user or user+device pair
43* Delivered to one or more Ubuntu devices
44* Passed through the application helper for processing
45* Notification displayed to the user (via different mechanisms)
46* Application Message queued for the app's use
47
48If the user interacts with the notification, the application is launched and should check its queue for messages
49it has to process.
50
51For the app developer, there are several components needed:
52
53* A server that sends the **push messages** to the Ubuntu Push server
54* Support in the client app for registering with the Ubuntu Push client
55* Support in the client app to react to **notifications** displayed to the user and process **application messages**
56* A helper program with application-specific knowledge that transforms **push messages** as needed.
57
58In the following sections, we'll see how to implement all the client side parts. For the application server, see the
59`Ubuntu Push Server API section <#ubuntu-push-server-api>`__
60
61The PushClient Component
62------------------------
63
64Example::
65
66 import Ubuntu.PushNotifications 0.1
67
68 PushClient {
69 id: pushClient
70 Component.onCompleted: {
71 newNotifications.connect(messageList.handle_notifications)
72 error.connect(messageList.handle_error)
73 }
74 appId: "com.ubuntu.developer.push.hello_hello"
75 }
76
77Registration: the appId and token properties
78~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
79
80To register with the push system and start receiving notifications, set the ``appId`` property to your application's APP_ID,
81with or without version number. For this to succeed the user **must** have an Ubuntu One account configured in the device.
82
83The APP_ID is as described in the `ApplicationId documentation <https://wiki.ubuntu.com/AppStore/Interfaces/ApplicationId>`__
84except that the version is treated as optional. Therefore both ``com.ubuntu.music_music`` and ``com.ubuntu.music_music_1.3.496``
85are valid. Keep in mind that while both versioned and unversioned APP_IDs are valid, they are still different and will affect
86which notifications are delivered to the application. Unversioned IDs mean the token will be the same after updates and the application
87will receive old notifications, while versioned IDs mean the app needs to explicitly ask to get older messages delivered.
88
89Setting the same appId more than once has no effect.
90
91After you are registered, if no error occurs, the PushClient will have a value set in its ``token`` property
92which uniquely identifies the user+device combination.
93
94Receiving Notifications
95~~~~~~~~~~~~~~~~~~~~~~~
96
97When a notification is received by the Push Client, it will be delivered to your application's push helper, and then
98placed in your application's mailbox. At that point, the PushClient will emit the ``newNotifications(QStringList)`` signal
99containing your messages. You should probably connect to that signal and handle those messages.
100
101Because of the application's lifecycle, there is no guarantee that it will be running when the signal is emitted. For that
102reason, apps should check for pending notifications whenever they are activated or started. To do that, use the
103``getNotifications()`` slot. Triggering that slot will fetch notifications and trigger the
104``newNotifications(QStringList)`` signal.
105
106Error Handling
107~~~~~~~~~~~~~~
108
109Whenever PushClient suffers an error, it will emit the ``error(QString)`` signal with the error message.
110
111Persistent Notification Management
112~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
113
114Some notifications are persistent, meaning they don't disappear automatically. For those notifications, there is an API that
115allows the app to manage them without having to know the underlying details of the platform.
116
117On each notification there's an optional ``tag`` field, used for this purpose.
118
119The ``persistent`` property of PushClient contains the list of the tags of notifications with the "persist" element set
120to true that are visible to the user right now.
121
122The ``void clearPersistent(QStringList tags)`` method clears persistent notifications for that app marked by ``tags``.
123If no tag is given, match all.
124
125
126The ``count`` property sets the counter in the application's icon to the given value.
127
128
129Application Helpers
130-------------------
131
132The payload delivered to push-client will be passed onto a helper program that can modify it as needed before passing it onto
133the postal service (see `Helper Output Format <#helper-output-format>`__).
134
135The helper receives two arguments ``infile`` and ``outfile``. The message is delivered via ``infile`` and the transformed
136version is placed in ``outfile``.
137
138This is the simplest possible useful helper, which simply passes the message through unchanged::
139
140 #!/usr/bin/python3
141
142 import sys
143 f1, f2 = sys.argv[1:3]
144 open(f2, "w").write(open(f1).read())
145
146Helpers need to be added to the click package manifest::
147
148 {
149 "name": "com.ubuntu.developer.ralsina.hello",
150 "description": "description of hello",
151 "framework": "ubuntu-sdk-14.10-qml-dev2",
152 "architecture": "all",
153 "title": "hello",
154 "hooks": {
155 "hello": {
156 "apparmor": "hello.json",
157 "desktop": "hello.desktop"
158 },
159 "helloHelper": {
160 "apparmor": "helloHelper-apparmor.json",
161 "push-helper": "helloHelper.json"
162 }
163 },
164 "version": "0.2",
165 "maintainer": "Roberto Alsina <roberto.alsina@canonical.com>"
166 }
167
168Here, we created a helloHelper entry in hooks that has an apparmor profile and an additional JSON file for the push-helper hook.
169
170helloHelper-apparmor.json must contain **only** the push-notification-client policy group::
171
172 {
173 "policy_groups": [
174 "push-notification-client"
175 ],
176 "policy_version": 1.2
177 }
178
179And helloHelper.json must have at least a exec key with the path to the helper executable relative to the json, and optionally
180an app_id key containing the short id of one of the apps in the package (in the format packagename_appname without a version).
181If the app_id is not specified, the helper will be used for all apps in the package::
182
183 {
184 "exec": "helloHelper",
185 "app_id": "com.ubuntu.developer.ralsina.hello_hello"
186 }
187
188.. note:: For deb packages, helpers should be installed into /usr/lib/ubuntu-push-client/legacy-helpers/ as part of the package.
189
190Helper Output Format
191--------------------
192
193Helpers output has two parts, the postal message (in the "message" key) and a notification to be presented to the user (in the "notification" key).
194
195Here's a simple example::
196
197 {
198 "message": "foobar",
199 "notification": {
200 "tag": "foo",
201 "card": {
202 "summary": "yes",
203 "body": "hello",
204 "popup": true,
205 "persist": true,
206 "timestamp": 1407160197
207 }
208 "sound": "buzz.mp3",
209 "vibrate": {
210 "pattern": [200, 100],
211 "repeat": 2
212 }
213 "emblem-counter": {
214 "count": 12,
215 "visible": true
216 }
217 }
218 }
219
220The notification can contain a **tag** field, which can later be used by the `persistent notification management API. <#persistent-notification-management>`__
221
222:message: (optional) A JSON object that is passed as-is to the application via PopAll.
223:notification: (optional) Describes the user-facing notifications triggered by this push message.
224
225The notification can contain a **card**. A card describes a specific notification to be given to the user,
226and has the following fields:
227
228:summary: (required) a title. The card will not be presented if this is missing.
229:body: longer text, defaults to empty.
230:actions: If empty (the default), a bubble notification is non-clickable.
231 If you add a URL, then bubble notifications are clickable and launch that URL. One use for this is using a URL like
232 ``appid://com.ubuntu.developer.ralsina.hello/hello/current-user-version`` which will switch to the app or launch
233 it if it's not running. See `URLDispatcher <https://wiki.ubuntu.com/URLDispatcher>`__ for more information.
234
235:icon: An icon relating to the event being notified. Defaults to empty (no icon);
236 a secondary icon relating to the application will be shown as well, regardless of this field.
237:timestamp: Seconds since the unix epoch, only used for persist (for now). If zero or unset, defaults to current timestamp.
238:persist: Whether to show in notification centre; defaults to false
239:popup: Whether to show in a bubble. Users can disable this, and can easily miss them, so don't rely on it exclusively. Defaults to false.
240
241.. note:: Keep in mind that the precise way in which each field is presented to the user depends on factors such as
242 whether it's shown as a bubble or in the notification centre, or even the version of Ubuntu Touch the user
243 has on their device.
244
245The notification can contain a **sound** field. This is either a boolean (play a predetermined sound) or the path to a sound file. The user can disable it, so don't rely on it exclusively.
246Defaults to empty (no sound). The path is relative, and will be looked up in (a) the application's .local/share/<pkgname>, and (b)
247standard xdg dirs.
248
249The notification can contain a **vibrate** field, causing haptic feedback, which can be either a boolean (if true, vibrate a predetermined way) or an object that has the following content:
250
251:pattern: a list of integers describing a vibration pattern (duration of alternating vibration/no vibration times, in milliseconds).
252:repeat: number of times the pattern has to be repeated (defaults to 1, 0 is the same as 1).
253
254The notification can contain a **emblem-counter** field, with the following content:
255
256:count: a number to be displayed over the application's icon in the launcher.
257:visible: set to true to show the counter, or false to hide it.
258
259.. note:: Unlike other notifications, emblem-counter needs to be cleaned by the app itself.
260 Please see `the persistent notification management section. <#persistent-notification-management>`__
261
262.. FIXME crosslink to hello example app on each method
263
264Security
265~~~~~~~~
266
267To use the push API, applications need to request permission in their security profile, using something like this::
268
269 {
270 "policy_groups": [
271 "networking",
272 "push-notification-client"
273 ],
274 "policy_version": 1.2
275 }
276
277
278Ubuntu Push Server API
279----------------------
280
281The Ubuntu Push server is located at https://push.ubuntu.com and has a single endpoint: ``/notify``.
282To notify a user, your application has to do a POST with ``Content-type: application/json``.
283
284Here is an example of the POST body using all the fields::
285
286 {
287 "appid": "com.ubuntu.music_music",
288 "expire_on": "2014-10-08T14:48:00.000Z",
289 "token": "LeA4tRQG9hhEkuhngdouoA==",
290 "clear_pending": true,
291 "replace_tag": "tagname",
292 "data": {
293 "message": "foobar",
294 "notification": {
295 "card": {
296 "summary": "yes",
297 "body": "hello",
298 "popup": true,
299 "persist": true,
300 "timestamp": 1407160197
301 }
302 "sound": "buzz.mp3",
303 "tag": "foo",
304 "vibrate": {
305 "pattern": [200, 100],
306 "repeat": 2
307 }
308 "emblem-counter": {
309 "count": 12,
310 "visible": true
311 }
312 }
313 }
314 }
315
316
317:appid: ID of the application that will receive the notification, as described in the client side documentation.
318:expire_on: Expiration date/time for this message, in `ISO8601 Extendend format <http://en.wikipedia.org/wiki/ISO_8601>`__
319:token: The token identifying the user+device to which the message is directed, as described in the client side documentation.
320:clear_pending: Discards all previous pending notifications. Usually in response to getting a "too-many-pending" error.
321:replace_tag: If there's a pending notification with the same tag, delete it before queuing this new one.
322:data: A JSON object.
323
324In this example, data is `what a helper would output <#helper-output-format>`__ but that's not necessarily the case.
325The content of the data field will be passed to the helper application which **has** to produce output in that format.
0326
=== modified file 'docs/lowlevel.txt'
--- docs/lowlevel.txt 2014-07-29 17:14:22 +0000
+++ docs/lowlevel.txt 2014-08-11 21:30:50 +0000
@@ -73,7 +73,7 @@
73Each Ubuntu Touch package has to use a separate object path for security reasons, that's why the object path includes QUOTED_PKGNAME.73Each Ubuntu Touch package has to use a separate object path for security reasons, that's why the object path includes QUOTED_PKGNAME.
74For example, in the case of the music application, the package name is ``com.ubuntu.music`` and QUOTED_PKGNAME is com_2eubuntu_2emusic.74For example, in the case of the music application, the package name is ``com.ubuntu.music`` and QUOTED_PKGNAME is com_2eubuntu_2emusic.
75Everything that is not a letter or digit has to be quoted as _XX where XX are the hex digits of the character. In practice,75Everything that is not a letter or digit has to be quoted as _XX where XX are the hex digits of the character. In practice,
76this means replacing "." with "_2e" and "-" with "_2f"76this means replacing "." with "_2e" and "-" with "_2d"
7777
78.. note:: For applications that are not installed as part of click packages, the QUOTED_PKGNAME is "_" and the APP_ID when required is78.. note:: For applications that are not installed as part of click packages, the QUOTED_PKGNAME is "_" and the APP_ID when required is
79 _PACKAGENAME.79 _PACKAGENAME.
@@ -296,12 +296,12 @@
296 "summary": "yes",296 "summary": "yes",
297 "body": "hello",297 "body": "hello",
298 "popup": true,298 "popup": true,
299 "persist": true299 "persist": true,
300 "timestamp": 1407160197
300 }301 }
301 "sound": "buzz.mp3",302 "sound": "buzz.mp3",
302 "vibrate": {303 "vibrate": {
303 "pattern": [200, 100],304 "pattern": [200, 100],
304 "duration": 200,
305 "repeat": 2305 "repeat": 2
306 }306 }
307 "emblem-counter": {307 "emblem-counter": {
@@ -328,7 +328,7 @@
328328
329:icon: An icon relating to the event being notified. Defaults to empty (no icon);329:icon: An icon relating to the event being notified. Defaults to empty (no icon);
330 a secondary icon relating to the application will be shown as well, regardless of this field.330 a secondary icon relating to the application will be shown as well, regardless of this field.
331:timestamp: Seconds since the unix epoch, only used for persist (for now)331:timestamp: Seconds since the unix epoch, only used for persist (for now). If zero or unset, defaults to current timestamp.
332:persist: Whether to show in notification centre; defaults to false332:persist: Whether to show in notification centre; defaults to false
333:popup: Whether to show in a bubble. Users can disable this, and can easily miss them, so don't rely on it exclusively. Defaults to false.333:popup: Whether to show in a bubble. Users can disable this, and can easily miss them, so don't rely on it exclusively. Defaults to false.
334334
@@ -336,14 +336,13 @@
336 whether it's shown as a bubble or in the notification centre, or even the version of Ubuntu Touch the user336 whether it's shown as a bubble or in the notification centre, or even the version of Ubuntu Touch the user
337 has on their device.337 has on their device.
338338
339The notification can contain a **sound** field. This is the path to a sound file. The user can disable it, so don't rely on it exclusively.339The notification can contain a **sound** field. This is either a boolean (play a predetermined sound) or the path to a sound file. The user can disable it, so don't rely on it exclusively.
340Defaults to empty (no sound). This is a relative path, and will be looked up in (a) the application's .local/share/<pkgname>, and (b)340Defaults to empty (no sound). The path is relative, and will be looked up in (a) the application's .local/share/<pkgname>, and (b)
341standard xdg dirs.341standard xdg dirs.
342342
343The notification can contain a **vibrate** field, causing haptic feedback, that has the following content:343The notification can contain a **vibrate** field, causing haptic feedback, which can be either a boolean (if true, vibrate a predetermined way) or an object that has the following content:
344344
345:pattern: a list of integers describing a vibration pattern.345:pattern: a list of integers describing a vibration pattern (duration of alternating vibration/no vibration times, in milliseconds).
346:duration: duration in milliseconds. Is equivalent to setting pattern to [duration], and overrides pattern.
347:repeat: number of times the pattern has to be repeated (defaults to 1, 0 is the same as 1).346:repeat: number of times the pattern has to be repeated (defaults to 1, 0 is the same as 1).
348347
349The notification can contain a **emblem-counter** field, with the following content:348The notification can contain a **emblem-counter** field, with the following content:
@@ -391,13 +390,13 @@
391 "summary": "yes",390 "summary": "yes",
392 "body": "hello",391 "body": "hello",
393 "popup": true,392 "popup": true,
394 "persist": true393 "persist": true,
394 "timestamp": 1407160197
395 }395 }
396 "sound": "buzz.mp3",396 "sound": "buzz.mp3",
397 "tag": "foo",397 "tag": "foo",
398 "vibrate": {398 "vibrate": {
399 "duration": 200,399 "pattern": [200, 100],
400 "pattern": (200, 100),
401 "repeat": 2400 "repeat": 2
402 }401 }
403 "emblem-counter": {402 "emblem-counter": {
404403
=== added directory 'identifier'
=== added file 'identifier/identifier.go'
--- identifier/identifier.go 1970-01-01 00:00:00 +0000
+++ identifier/identifier.go 2014-08-11 21:30:50 +0000
@@ -0,0 +1,59 @@
1/*
2 Copyright 2013-2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17// Package identifier is the source of an anonymous and stable
18// system id (from /var/lib/dbus/machine-id) used by the Ubuntu
19// push notifications service.
20package identifier
21
22import (
23 "fmt"
24 "io/ioutil"
25)
26
27var machineIdPath = "/var/lib/dbus/machine-id"
28
29// an Id knows how to generate itself, and how to stringify itself.
30type Id interface {
31 String() string
32}
33
34// Identifier is the default Id implementation.
35type Identifier struct {
36 value string
37}
38
39func readMachineId() (string, error) {
40 value, err := ioutil.ReadFile(machineIdPath)
41 if err != nil {
42 return "", err
43 }
44 return string(value)[:len(value)-1], nil
45}
46
47// New creates an Identifier
48func New() (Id, error) {
49 value, err := readMachineId()
50 if err != nil {
51 return &Identifier{value: ""}, fmt.Errorf("Failed to read the machine id: %s", err)
52 }
53 return &Identifier{value: value}, nil
54}
55
56// String returns the system identifier as a string.
57func (id *Identifier) String() string {
58 return id.value
59}
060
=== added file 'identifier/identifier_test.go'
--- identifier/identifier_test.go 1970-01-01 00:00:00 +0000
+++ identifier/identifier_test.go 2014-08-11 21:30:50 +0000
@@ -0,0 +1,54 @@
1/*
2 Copyright 2013-2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17package identifier
18
19import (
20 . "launchpad.net/gocheck"
21 "testing"
22)
23
24// hook up gocheck
25func Test(t *testing.T) { TestingT(t) }
26
27type IdentifierSuite struct{}
28
29var _ = Suite(&IdentifierSuite{})
30
31// TestNew checks that New does not fail, and returns a
32// 32-byte string.
33func (s *IdentifierSuite) TestNew(c *C) {
34 id, err := New()
35 c.Check(err, IsNil)
36 c.Check(id.String(), HasLen, 32)
37}
38
39// TestNewFail checks that when we can't read the machine-id
40// file the error is propagated
41func (s *IdentifierSuite) TestNewFail(c *C) {
42 // replace the machine-id file path
43 machineIdPath = "/var/lib/dbus/no-such-file"
44 id, err := New()
45 c.Check(err, NotNil)
46 c.Check(err.Error(), Equals, "Failed to read the machine id: open /var/lib/dbus/no-such-file: no such file or directory")
47 c.Check(id.String(), HasLen, 0)
48}
49
50// TestIdentifierInterface checks that Identifier implements Id.
51func (s *IdentifierSuite) TestIdentifierInterface(c *C) {
52 id, _ := New()
53 _ = []Id{id}
54}
055
=== added directory 'identifier/testing'
=== added file 'identifier/testing/testing.go'
--- identifier/testing/testing.go 1970-01-01 00:00:00 +0000
+++ identifier/testing/testing.go 2014-08-11 21:30:50 +0000
@@ -0,0 +1,58 @@
1/*
2 Copyright 2013-2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17// Package testing implements a couple of Ids that are useful
18// for testing things that use identifier.
19package testing
20
21// SettableIdentifier is an Id that lets you set the value of the identifier.
22//
23// By default the identifier's value is "<Settable>", so it's visible
24// if you're misusing it.
25type SettableIdentifier struct {
26 value string
27}
28
29// Settable is the constructor for SettableIdentifier.
30func Settable() *SettableIdentifier {
31 return &SettableIdentifier{"<Settable>"}
32}
33
34// Set is the method you use to set the identifier.
35func (sid *SettableIdentifier) Set(value string) {
36 sid.value = value
37}
38
39// String returns the string you set.
40func (sid *SettableIdentifier) String() string {
41 return sid.value
42}
43
44// FailingIdentifier is an Id that always fails to generate.
45type FailingIdentifier struct{}
46
47// Failing is the constructor for FailingIdentifier.
48func Failing() *FailingIdentifier {
49 return &FailingIdentifier{}
50}
51
52// String returns "<Failing>".
53//
54// The purpose of this is to make it easy to spot if you're using it
55// by accident.
56func (*FailingIdentifier) String() string {
57 return "<Failing>"
58}
059
=== added file 'identifier/testing/testing_test.go'
--- identifier/testing/testing_test.go 1970-01-01 00:00:00 +0000
+++ identifier/testing/testing_test.go 2014-08-11 21:30:50 +0000
@@ -0,0 +1,57 @@
1/*
2 Copyright 2013-2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17package testing
18
19import (
20 identifier ".."
21 . "launchpad.net/gocheck"
22 "testing"
23)
24
25// hook up gocheck
26func Test(t *testing.T) { TestingT(t) }
27
28type IdentifierSuite struct{}
29
30var _ = Suite(&IdentifierSuite{})
31
32// TestSettableDefaultValueVisible tests that SettableIdentifier's default
33// value is notable.
34func (s *IdentifierSuite) TestSettableDefaultValueVisible(c *C) {
35 id := Settable()
36 c.Check(id.String(), Equals, "<Settable>")
37}
38
39// TestSettableSets tests that SettableIdentifier is settable.
40func (s *IdentifierSuite) TestSettableSets(c *C) {
41 id := Settable()
42 id.Set("hello")
43 c.Check(id.String(), Equals, "hello")
44}
45
46// TestFailingStringNotEmpty tests that FailingIdentifier still has a
47// non-empty string.
48func (s *IdentifierSuite) TestFailingStringNotEmpty(c *C) {
49 id := Failing()
50 c.Check(id.String(), Equals, "<Failing>")
51}
52
53// TestIdentifierInterface tests that FailingIdentifier and
54// SettableIdentifier implement Id.
55func (s *IdentifierSuite) TestIdentifierInterface(c *C) {
56 _ = []identifier.Id{Failing(), Settable()}
57}
058
=== modified file 'launch_helper/helper_output.go'
--- launch_helper/helper_output.go 2014-07-22 14:08:25 +0000
+++ launch_helper/helper_output.go 2014-08-11 21:30:50 +0000
@@ -18,6 +18,7 @@
1818
19import (19import (
20 "encoding/json"20 "encoding/json"
21 "time"
2122
22 "launchpad.net/ubuntu-push/click"23 "launchpad.net/ubuntu-push/click"
23)24)
@@ -25,13 +26,13 @@
25// a Card is the usual “visual” presentation of a notification, used26// a Card is the usual “visual” presentation of a notification, used
26// for bubbles and the notification centre (neé messaging menu)27// for bubbles and the notification centre (neé messaging menu)
27type Card struct {28type Card struct {
28 Summary string `json:"summary"` // required for the card to be presented29 Summary string `json:"summary"` // required for the card to be presented
29 Body string `json:"body"` // defaults to empty30 Body string `json:"body"` // defaults to empty
30 Actions []string `json:"actions"` // if empty (default), bubble is non-clickable. More entries change it to be clickable and (for bubbles) snap-decisions.31 Actions []string `json:"actions"` // if empty (default), bubble is non-clickable. More entries change it to be clickable and (for bubbles) snap-decisions.
31 Icon string `json:"icon"` // an icon relating to the event being notified. Defaults to empty (no icon); a secondary icon relating to the application will be shown as well, irrespectively.32 Icon string `json:"icon"` // an icon relating to the event being notified. Defaults to empty (no icon); a secondary icon relating to the application will be shown as well, irrespectively.
32 Timestamp int `json:"timestamp"` // seconds since epoch, only used for persist (for now)33 RawTimestamp int `json:"timestamp"` // seconds since epoch, only used for persist (for now). Timestamp() returns this if non-zero, current timestamp otherwise.
33 Persist bool `json:"persist"` // whether to show in notification centre; defaults to false34 Persist bool `json:"persist"` // whether to show in notification centre; defaults to false
34 Popup bool `json:"popup"` // whether to show in a bubble. Users can disable this, and can easily miss them, so don't rely on it exclusively. Defaults to false.35 Popup bool `json:"popup"` // whether to show in a bubble. Users can disable this, and can easily miss them, so don't rely on it exclusively. Defaults to false.
35}36}
3637
37// an EmblemCounter puts a number on an emblem on an app's icon in the launcher38// an EmblemCounter puts a number on an emblem on an app's icon in the launcher
@@ -43,18 +44,17 @@
43// a Vibration generates a vibration in the form of a Pattern set in44// a Vibration generates a vibration in the form of a Pattern set in
44// duration a pattern of on off states, repeated a number of times45// duration a pattern of on off states, repeated a number of times
45type Vibration struct {46type Vibration struct {
46 Duration uint32 `json:"duration"` // if Duration is present and not 0, it's like a Pattern of [Duration]; otherwise, Pattern is used.47 Pattern []uint32 `json:"pattern"`
47 Pattern []uint32 `json:"pattern"`48 Repeat uint32 `json:"repeat"` // defaults to 1. A value of zero is ignored (so it's like 1).
48 Repeat uint32 `json:"repeat"` // defaults to 1. A value of zero is ignored (so it's like 1).
49}49}
5050
51// a Notification can be any of the above51// a Notification can be any of the above
52type Notification struct {52type Notification struct {
53 Card *Card `json:"card"` // defaults to nil (no card)53 Card *Card `json:"card"` // defaults to nil (no card)
54 Sound string `json:"sound"` // a sound file. Users can disable this, so don't rely on it exclusively. Defaults to empty (no sound).54 RawSound json.RawMessage `json:"sound"` // a boolean, or the relative path to a sound file. Users can disable this, so don't rely on it exclusively. Defaults to empty (no sound).
55 Vibrate *Vibration `json:"vibrate"` // users can disable this, blah blah. Defaults to null (no vibration)55 RawVibration json.RawMessage `json:"vibrate"` // users can disable this, blah blah. Can be Vibration, or boolean. Defaults to null (no vibration)
56 EmblemCounter *EmblemCounter `json:"emblem-counter"` // puts a counter on an emblem in the launcher. Defaults to nil (no change to emblem counter).56 EmblemCounter *EmblemCounter `json:"emblem-counter"` // puts a counter on an emblem in the launcher. Defaults to nil (no change to emblem counter).
57 Tag string `json:"tag,omitempty"` // tag used for Clear/ListPersistent.57 Tag string `json:"tag,omitempty"` // tag used for Clear/ListPersistent.
58}58}
5959
60// HelperOutput is the expected output of a helper60// HelperOutput is the expected output of a helper
@@ -76,3 +76,58 @@
76 NotificationId string76 NotificationId string
77 Payload json.RawMessage77 Payload json.RawMessage
78}78}
79
80// Timestamp() returns RawTimestamp if non-zero. If it's zero, returns
81// the current time as second since epoch.
82func (card *Card) Timestamp() int64 {
83 if card.RawTimestamp == 0 {
84 return time.Now().Unix()
85 } else {
86 return int64(card.RawTimestamp)
87 }
88}
89
90func (notification *Notification) Vibration(fallback *Vibration) *Vibration {
91 var b bool
92 var vib *Vibration
93
94 if notification.RawVibration == nil {
95 return nil
96 }
97 if json.Unmarshal(notification.RawVibration, &b) == nil {
98 if !b {
99 return nil
100 } else {
101 return fallback
102 }
103 }
104 if json.Unmarshal(notification.RawVibration, &vib) != nil {
105 return nil
106 }
107 if len(vib.Pattern) == 0 {
108 return nil
109 }
110
111 return vib
112}
113
114func (notification *Notification) Sound(fallback string) string {
115 var b bool
116 var s string
117
118 if notification.RawSound == nil {
119 return ""
120 }
121 if json.Unmarshal(notification.RawSound, &b) == nil {
122 if !b {
123 return ""
124 } else {
125 return fallback
126 }
127 }
128 if json.Unmarshal(notification.RawSound, &s) != nil {
129 return ""
130 }
131
132 return s
133}
79134
=== added file 'launch_helper/helper_output_test.go'
--- launch_helper/helper_output_test.go 1970-01-01 00:00:00 +0000
+++ launch_helper/helper_output_test.go 2014-08-11 21:30:50 +0000
@@ -0,0 +1,96 @@
1/*
2 Copyright 2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17package launch_helper
18
19import (
20 "encoding/json"
21 "time"
22
23 . "launchpad.net/gocheck"
24)
25
26type outSuite struct{}
27
28var _ = Suite(&outSuite{})
29
30func (*outSuite) TestCardGetTimestamp(c *C) {
31 t := time.Now().Add(-2 * time.Second)
32 var card Card
33 err := json.Unmarshal([]byte(`{"timestamp": 12}`), &card)
34 c.Assert(err, IsNil)
35 c.Check(card, DeepEquals, Card{RawTimestamp: 12})
36 c.Check(time.Unix((&Card{}).Timestamp(), 0).After(t), Equals, true)
37 c.Check((&Card{RawTimestamp: 42}).Timestamp(), Equals, int64(42))
38}
39
40func (*outSuite) TestBadVibeBegetsNilVibe(c *C) {
41 fbck := &Vibration{Repeat: 2}
42 for _, s := range []string{
43 `{}`,
44 `{"vibrate": "foo"}`,
45 `{"vibrate": {}}`,
46 `{"vibrate": false}`, // not bad, but rather pointless
47 `{"vibrate": {"repeat": 2}}`, // no pattern
48 `{"vibrate": {"repeat": "foo"}}`,
49 `{"vibrate": {"pattern": "foo"}}`,
50 `{"vibrate": {"pattern": ["foo"]}}`,
51 `{"vibrate": {"pattern": null}}`,
52 `{"vibrate": {"pattern": [-1]}}`,
53 `{"vibrate": {"pattern": [1], "repeat": -1}}`,
54 } {
55 var notif *Notification
56 err := json.Unmarshal([]byte(s), &notif)
57 c.Assert(err, IsNil)
58 c.Assert(notif, NotNil)
59 c.Check(notif.Vibration(fbck), IsNil, Commentf("not nil Vibration() for: %s", s))
60 c.Check(notif.Vibration(fbck), IsNil, Commentf("not nil second call to Vibration() for: %s", s))
61 }
62}
63
64func (*outSuite) TestGoodVibe(c *C) {
65 var notif *Notification
66 err := json.Unmarshal([]byte(`{"vibrate": {"pattern": [1,2,3], "repeat": 2}}`), &notif)
67 c.Assert(err, IsNil)
68 c.Assert(notif, NotNil)
69 c.Check(notif.Vibration(nil), DeepEquals, &Vibration{Pattern: []uint32{1, 2, 3}, Repeat: 2})
70}
71
72func (*outSuite) TestGoodSimpleVibe(c *C) {
73 var notif *Notification
74 fallback := &Vibration{Pattern: []uint32{100, 100}, Repeat: 3}
75 err := json.Unmarshal([]byte(`{"vibrate": true}`), &notif)
76 c.Assert(err, IsNil)
77 c.Assert(notif, NotNil)
78 c.Check(notif.Vibration(fallback), Equals, fallback)
79}
80
81func (*outSuite) TestBadSoundBegetsNoSound(c *C) {
82 c.Check((&Notification{RawSound: json.RawMessage("foo")}).Sound("x"), Equals, "")
83}
84
85func (*outSuite) TestNilSoundBegetsNoSound(c *C) {
86 c.Check((&Notification{RawSound: nil}).Sound("x"), Equals, "")
87}
88
89func (*outSuite) TestGoodSound(c *C) {
90 c.Check((&Notification{RawSound: json.RawMessage(`"foo"`)}).Sound("x"), Equals, "foo")
91}
92
93func (*outSuite) TestGoodSimpleSound(c *C) {
94 c.Check((&Notification{RawSound: json.RawMessage(`true`)}).Sound("x"), Equals, "x")
95 c.Check((&Notification{RawSound: json.RawMessage(`false`)}).Sound("x"), Equals, "")
96}
097
=== modified file 'launch_helper/helper_test.go'
--- launch_helper/helper_test.go 2014-07-22 14:08:25 +0000
+++ launch_helper/helper_test.go 2014-08-11 21:30:50 +0000
@@ -43,7 +43,7 @@
43}43}
4444
45func (s *runnerSuite) TestTrivialPoolWorks(c *C) {45func (s *runnerSuite) TestTrivialPoolWorks(c *C) {
46 notif := &Notification{Sound: "42", Tag: "foo"}46 notif := &Notification{RawSound: json.RawMessage(`"42"`), Tag: "foo"}
4747
48 triv := NewTrivialHelperPool(s.testlog)48 triv := NewTrivialHelperPool(s.testlog)
49 ch := triv.Start()49 ch := triv.Start()
5050
=== modified file 'launch_helper/kindpool_test.go'
--- launch_helper/kindpool_test.go 2014-07-30 09:26:25 +0000
+++ launch_helper/kindpool_test.go 2014-08-11 21:30:50 +0000
@@ -17,6 +17,7 @@
17package launch_helper17package launch_helper
1818
19import (19import (
20 "encoding/json"
20 "fmt"21 "fmt"
21 "io/ioutil"22 "io/ioutil"
22 "os"23 "os"
@@ -285,7 +286,7 @@
285286
286 res := takeNext(ch, c)287 res := takeNext(ch, c)
287288
288 expected := HelperOutput{Notification: &Notification{Sound: "hello", Tag: "a-tag"}}289 expected := HelperOutput{Notification: &Notification{RawSound: json.RawMessage(`"hello"`), Tag: "a-tag"}}
289 c.Check(res.HelperOutput, DeepEquals, expected)290 c.Check(res.HelperOutput, DeepEquals, expected)
290 c.Check(pool.hmap, HasLen, 0)291 c.Check(pool.hmap, HasLen, 0)
291}292}
292293
=== modified file 'messaging/cmessaging/cmessaging.go'
--- messaging/cmessaging/cmessaging.go 2014-07-24 16:25:55 +0000
+++ messaging/cmessaging/cmessaging.go 2014-08-11 21:30:50 +0000
@@ -84,7 +84,7 @@
84 body := gchar(card.Body)84 body := gchar(card.Body)
85 defer gfree(body)85 defer gfree(body)
8686
87 timestamp := (C.gint64)(int64(card.Timestamp) * 1000000)87 timestamp := (C.gint64)(card.Timestamp() * 1000000)
8888
89 C.add_notification(desktop_id, notification_id, icon_path, summary, body, timestamp, nil, (C.gpointer)(payload))89 C.add_notification(desktop_id, notification_id, icon_path, summary, body, timestamp, nil, (C.gpointer)(payload))
90}90}
9191
=== added file 'scripts/connect-many.py'
--- scripts/connect-many.py 1970-01-01 00:00:00 +0000
+++ scripts/connect-many.py 2014-08-11 21:30:50 +0000
@@ -0,0 +1,29 @@
1#!/usr/bin/python3
2import sys
3import resource
4import socket
5import ssl
6import time
7
8host, port = sys.argv[1].split(":")
9addr = (host, int(port))
10soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
11# reset soft == hard
12resource.setrlimit(resource.RLIMIT_NOFILE, (hard, hard))
13
14conns = []
15t0 = time.time()
16try:
17 for i in range(soft+100):
18 s=socket.socket()
19 w = ssl.wrap_socket(s)
20 w.settimeout(1)
21 w.connect(addr)
22 conns.append(w)
23 w.send(b"x")
24except Exception as e:
25 print("%s|%d|%s" % (e, len(conns), time.time()-t0))
26 sys.exit(0)
27
28print("UNTROUBLED|%d" % len(conns))
29sys.exit(1)
030
=== added file 'scripts/goctest'
--- scripts/goctest 1970-01-01 00:00:00 +0000
+++ scripts/goctest 2014-08-11 21:30:50 +0000
@@ -0,0 +1,46 @@
1#!/usr/bin/python3
2# -*- python -*-
3# (c) 2014 John Lenton
4# MIT licensed.
5# from https://github.com/chipaca/goctest
6
7import re
8import signal
9import subprocess
10import sys
11
12ok_rx = re.compile(rb'^(PASS:?|ok\s+)')
13fail_rx = re.compile(rb'^(FAIL:?|OOPS:?)')
14panic_rx = re.compile(rb'^(PANIC:?|panic:?|\.\.\. Panic:?)')
15log_rx = re.compile(rb'^\[LOG\]|^\?\s+')
16
17class bcolors:
18 OK = b'\033[38;5;34m'
19 FAIL = b'\033[38;5;196m'
20 PANIC = b'\033[38;5;226m\033[48;5;88m'
21 OTHER = b'\033[38;5;241m'
22 WARNING = b'\033[38;5;226m'
23 ENDC = b'\033[0m'
24
25signal.signal(signal.SIGINT, lambda *_: None)
26
27if sys.stdout.isatty():
28 with subprocess.Popen(["go", "test"] + sys.argv[1:],
29 bufsize=0,
30 stderr=subprocess.STDOUT,
31 stdout=subprocess.PIPE) as proc:
32 for line in proc.stdout:
33 if panic_rx.search(line) is not None:
34 line = panic_rx.sub(bcolors.PANIC + rb'\1' + bcolors.ENDC, line)
35 elif fail_rx.search(line) is not None:
36 line = fail_rx.sub(bcolors.FAIL + rb'\1' + bcolors.ENDC, line)
37 elif ok_rx.search(line) is not None:
38 line = ok_rx.sub(bcolors.OK + rb'\1' + bcolors.ENDC, line)
39 elif log_rx.search(line) is not None:
40 line = bcolors.OTHER + line + bcolors.ENDC
41
42 sys.stdout.write(line.decode("utf-8"))
43 sys.stdout.flush()
44 sys.exit(proc.wait())
45else:
46 sys.exit(subprocess.call(["go", "test"] + sys.argv[1:]))
047
=== modified file 'server/listener/listener_test.go'
--- server/listener/listener_test.go 2014-03-06 19:21:44 +0000
+++ server/listener/listener_test.go 2014-08-11 21:30:50 +0000
@@ -20,9 +20,12 @@
20 "crypto/tls"20 "crypto/tls"
21 "crypto/x509"21 "crypto/x509"
22 "net"22 "net"
23 "os/exec"
24 "regexp"
23 "syscall"25 "syscall"
24 "testing"26 "testing"
25 "time"27 "time"
28 "unicode"
2629
27 . "launchpad.net/gocheck"30 . "launchpad.net/gocheck"
2831
@@ -37,7 +40,7 @@
3740
38var _ = Suite(&listenerSuite{})41var _ = Suite(&listenerSuite{})
3942
40const NofileMax = 50043const NofileMax = 20
4144
42func (s *listenerSuite) SetUpSuite(*C) {45func (s *listenerSuite) SetUpSuite(*C) {
43 // make it easier to get a too many open files error46 // make it easier to get a too many open files error
@@ -111,13 +114,19 @@
111114
112func testSession(conn net.Conn) error {115func testSession(conn net.Conn) error {
113 defer conn.Close()116 defer conn.Close()
114 conn.SetDeadline(time.Now().Add(2 * time.Second))117 conn.SetDeadline(time.Now().Add(10 * time.Second))
115 var buf [1]byte118 var buf [1]byte
116 _, err := conn.Read(buf[:])119 for {
117 if err != nil {120 _, err := conn.Read(buf[:])
118 return err121 if err != nil {
122 return err
123 }
124 // 1|2... send digit back
125 if unicode.IsDigit(rune(buf[0])) {
126 break
127 }
119 }128 }
120 _, err = conn.Write(buf[:])129 _, err := conn.Write(buf[:])
121 return err130 return err
122}131}
123132
@@ -165,6 +174,18 @@
165 c.Check(s.testlog.Captured(), Equals, "")174 c.Check(s.testlog.Captured(), Equals, "")
166}175}
167176
177// waitForLogs waits for the logs captured in s.testlog to match reStr.
178func (s *listenerSuite) waitForLogs(c *C, reStr string) {
179 rx := regexp.MustCompile("^" + reStr + "$")
180 for i := 0; i < 100; i++ {
181 if rx.MatchString(s.testlog.Captured()) {
182 break
183 }
184 time.Sleep(20 * time.Millisecond)
185 }
186 c.Check(s.testlog.Captured(), Matches, reStr)
187}
188
168func (s *listenerSuite) TestDeviceAcceptLoopTemporaryError(c *C) {189func (s *listenerSuite) TestDeviceAcceptLoopTemporaryError(c *C) {
169 // ENFILE is not the temp network error we want to handle this way190 // ENFILE is not the temp network error we want to handle this way
170 // but is relatively easy to generate in a controlled way191 // but is relatively easy to generate in a controlled way
@@ -177,20 +198,11 @@
177 errCh <- lst.AcceptLoop(testSession, s.testlog)198 errCh <- lst.AcceptLoop(testSession, s.testlog)
178 }()199 }()
179 listenerAddr := lst.Addr().String()200 listenerAddr := lst.Addr().String()
180 conns := make([]net.Conn, 0, NofileMax)201 connectMany := helpers.ScriptAbsPath("connect-many.py")
181 for i := 0; i < NofileMax; i++ {202 cmd := exec.Command(connectMany, listenerAddr)
182 var conn1 net.Conn203 res, err := cmd.Output()
183 conn1, err = net.Dial("tcp", listenerAddr)204 c.Assert(err, IsNil)
184 if err != nil {205 c.Assert(string(res), Matches, "(?s).*timed out.*")
185 break
186 }
187 defer conn1.Close()
188 conns = append(conns, conn1)
189 }
190 c.Assert(err, ErrorMatches, "*.too many open.*")
191 for _, conn := range conns {
192 conn.Close()
193 }
194 conn2, err := testTlsDial(c, listenerAddr)206 conn2, err := testTlsDial(c, listenerAddr)
195 c.Assert(err, IsNil)207 c.Assert(err, IsNil)
196 defer conn2.Close()208 defer conn2.Close()
@@ -198,7 +210,7 @@
198 testReadByte(c, conn2, '2')210 testReadByte(c, conn2, '2')
199 lst.Close()211 lst.Close()
200 c.Check(<-errCh, ErrorMatches, ".*use of closed.*")212 c.Check(<-errCh, ErrorMatches, ".*use of closed.*")
201 c.Check(s.testlog.Captured(), Matches, ".*device listener:.*accept.*too many open.*-- retrying\n")213 s.waitForLogs(c, "(?ms).*device listener:.*accept.*too many open.*-- retrying")
202}214}
203215
204func (s *listenerSuite) TestDeviceAcceptLoopPanic(c *C) {216func (s *listenerSuite) TestDeviceAcceptLoopPanic(c *C) {
@@ -217,7 +229,7 @@
217 c.Assert(err, Not(IsNil))229 c.Assert(err, Not(IsNil))
218 lst.Close()230 lst.Close()
219 c.Check(<-errCh, ErrorMatches, ".*use of closed.*")231 c.Check(<-errCh, ErrorMatches, ".*use of closed.*")
220 c.Check(s.testlog.Captured(), Matches, "(?s)ERROR\\(PANIC\\) terminating device connection on: session crash:.*AcceptLoop.*")232 s.waitForLogs(c, "(?s)ERROR\\(PANIC\\) terminating device connection on: session crash:.*AcceptLoop.*")
221}233}
222234
223func (s *listenerSuite) TestForeignListener(c *C) {235func (s *listenerSuite) TestForeignListener(c *C) {
224236
=== modified file 'sounds/sounds.go'
--- sounds/sounds.go 2014-07-25 10:25:59 +0000
+++ sounds/sounds.go 2014-08-11 21:30:50 +0000
@@ -17,9 +17,11 @@
17package sounds17package sounds
1818
19import (19import (
20 "errors"
20 "os"21 "os"
21 "os/exec"22 "os/exec"
22 "path/filepath"23 "path/filepath"
24 "strings"
2325
24 "launchpad.net/go-xdg/v0"26 "launchpad.net/go-xdg/v0"
2527
@@ -31,12 +33,19 @@
31type Sound struct {33type Sound struct {
32 player string34 player string
33 log logger.Logger35 log logger.Logger
36 fallback string
34 dataDirs func() []string37 dataDirs func() []string
35 dataFind func(string) (string, error)38 dataFind func(string) (string, error)
36}39}
3740
38func New(log logger.Logger) *Sound {41func New(log logger.Logger, fallback string) *Sound {
39 return &Sound{player: "paplay", log: log, dataDirs: xdg.Data.Dirs, dataFind: xdg.Data.Find}42 return &Sound{
43 player: "paplay",
44 log: log,
45 fallback: fallback,
46 dataDirs: xdg.Data.Dirs,
47 dataFind: xdg.Data.Find,
48 }
40}49}
4150
42func (snd *Sound) Present(app *click.AppId, nid string, notification *launch_helper.Notification) bool {51func (snd *Sound) Present(app *click.AppId, nid string, notification *launch_helper.Notification) bool {
@@ -44,13 +53,14 @@
44 panic("please check notification is not nil before calling present")53 panic("please check notification is not nil before calling present")
45 }54 }
4655
47 if notification.Sound == "" {56 sound := notification.Sound(snd.fallback)
48 snd.log.Debugf("[%s] notification has no Sound: %#v", nid, notification.Sound)57 if sound == "" {
58 snd.log.Debugf("[%s] notification has no Sound: %#v", nid, sound)
49 return false59 return false
50 }60 }
51 absPath := snd.findSoundFile(app, nid, notification.Sound)61 absPath := snd.findSoundFile(app, nid, sound)
52 if absPath == "" {62 if absPath == "" {
53 snd.log.Debugf("[%s] unable to find sound %s", nid, notification.Sound)63 snd.log.Debugf("[%s] unable to find sound %s", nid, sound)
54 return false64 return false
55 }65 }
56 snd.log.Debugf("[%s] playing sound %s using %s", nid, absPath, snd.player)66 snd.log.Debugf("[%s] playing sound %s using %s", nid, absPath, snd.player)
@@ -69,9 +79,23 @@
69 return true79 return true
70}80}
7181
82// Removes all cruft from path, ensures it's a "forward" path.
83func (snd *Sound) cleanPath(path string) (string, error) {
84 cleaned := filepath.Clean(path)
85 if strings.Contains(cleaned, "../") {
86 return "", errors.New("Path escaping xdg attempt")
87 }
88 return cleaned, nil
89}
90
72func (snd *Sound) findSoundFile(app *click.AppId, nid string, sound string) string {91func (snd *Sound) findSoundFile(app *click.AppId, nid string, sound string) string {
73 // XXX also support legacy appIds?92 // XXX also support legacy appIds?
74 // first, check package-specific93 // first, check package-specific
94 sound, err := snd.cleanPath(sound)
95 if err != nil {
96 // bad boy
97 return ""
98 }
75 absPath, err := snd.dataFind(filepath.Join(app.Package, sound))99 absPath, err := snd.dataFind(filepath.Join(app.Package, sound))
76 if err == nil {100 if err == nil {
77 // ffffound101 // ffffound
78102
=== modified file 'sounds/sounds_test.go'
--- sounds/sounds_test.go 2014-07-25 10:25:59 +0000
+++ sounds/sounds_test.go 2014-08-11 21:30:50 +0000
@@ -17,6 +17,7 @@
17package sounds17package sounds
1818
19import (19import (
20 "encoding/json"
20 "errors"21 "errors"
21 "os"22 "os"
22 "path"23 "path"
@@ -45,9 +46,10 @@
45}46}
4647
47func (ss *soundsSuite) TestNew(c *C) {48func (ss *soundsSuite) TestNew(c *C) {
48 s := New(ss.log)49 s := New(ss.log, "foo")
49 c.Check(s.log, Equals, ss.log)50 c.Check(s.log, Equals, ss.log)
50 c.Check(s.player, Equals, "paplay")51 c.Check(s.player, Equals, "paplay")
52 c.Check(s.fallback, Equals, "foo")
51}53}
5254
53func (ss *soundsSuite) TestPresent(c *C) {55func (ss *soundsSuite) TestPresent(c *C) {
@@ -57,10 +59,22 @@
57 }59 }
5860
59 c.Check(s.Present(ss.app, "",61 c.Check(s.Present(ss.app, "",
60 &launch_helper.Notification{Sound: "hello"}), Equals, true)62 &launch_helper.Notification{RawSound: json.RawMessage(`"hello"`)}), Equals, true)
61 c.Check(ss.log.Captured(), Matches, `(?sm).* playing sound com.example.test/hello using echo`)63 c.Check(ss.log.Captured(), Matches, `(?sm).* playing sound com.example.test/hello using echo`)
62}64}
6365
66func (ss *soundsSuite) TestPresentSimple(c *C) {
67 s := &Sound{
68 player: "echo", log: ss.log,
69 dataFind: func(s string) (string, error) { return s, nil },
70 fallback: "fallback",
71 }
72
73 c.Check(s.Present(ss.app, "",
74 &launch_helper.Notification{RawSound: json.RawMessage(`true`)}), Equals, true)
75 c.Check(ss.log.Captured(), Matches, `(?sm).* playing sound com.example.test/fallback using echo`)
76}
77
64func (ss *soundsSuite) TestPresentFails(c *C) {78func (ss *soundsSuite) TestPresentFails(c *C) {
65 s := &Sound{79 s := &Sound{
66 player: "/",80 player: "/",
@@ -74,10 +88,10 @@
74 // no Sound88 // no Sound
75 c.Check(s.Present(ss.app, "", &launch_helper.Notification{}), Equals, false)89 c.Check(s.Present(ss.app, "", &launch_helper.Notification{}), Equals, false)
76 // bad player90 // bad player
77 c.Check(s.Present(ss.app, "", &launch_helper.Notification{Sound: "hello"}), Equals, false)91 c.Check(s.Present(ss.app, "", &launch_helper.Notification{RawSound: json.RawMessage(`"hello"`)}), Equals, false)
78 s.player = "echo"92 s.player = "echo"
79 // no file found93 // no file found
80 c.Check(s.Present(ss.app, "", &launch_helper.Notification{Sound: "hello"}), Equals, false)94 c.Check(s.Present(ss.app, "", &launch_helper.Notification{RawSound: json.RawMessage(`"hello"`)}), Equals, false)
8195
82 // and now, just to prove it would've worked,96 // and now, just to prove it would've worked,
8397
@@ -86,5 +100,31 @@
86 c.Assert(err, IsNil)100 c.Assert(err, IsNil)
87 f.Close()101 f.Close()
88 s.dataDirs = func() []string { return []string{"", d} }102 s.dataDirs = func() []string { return []string{"", d} }
89 c.Check(s.Present(ss.app, "", &launch_helper.Notification{Sound: "hello"}), Equals, true)103 c.Check(s.Present(ss.app, "", &launch_helper.Notification{RawSound: json.RawMessage(`"hello"`)}), Equals, true)
104}
105
106func (ss *soundsSuite) TestBadPathFails(c *C) {
107 s := &Sound{
108 player: "/",
109 log: ss.log,
110 dataFind: func(string) (string, error) { return "", errors.New("nope") },
111 dataDirs: func() []string { return []string{""} },
112 }
113
114 sound, err := s.cleanPath("../../foo")
115 c.Check(err, NotNil)
116 c.Check(sound, Equals, "")
117}
118
119func (ss *soundsSuite) TestGoodPathSucceeds(c *C) {
120 s := &Sound{
121 player: "/",
122 log: ss.log,
123 dataFind: func(string) (string, error) { return "", errors.New("nope") },
124 dataDirs: func() []string { return []string{""} },
125 }
126
127 sound, err := s.cleanPath("foo/../bar")
128 c.Check(err, IsNil)
129 c.Check(sound, Equals, "bar")
90}130}
91131
=== modified file 'tests/autopilot/push_notifications/tests/__init__.py'
--- tests/autopilot/push_notifications/tests/__init__.py 2014-07-09 23:26:45 +0000
+++ tests/autopilot/push_notifications/tests/__init__.py 2014-08-11 21:30:50 +0000
@@ -26,6 +26,7 @@
26import evdev26import evdev
2727
28from autopilot.introspection import dbus28from autopilot.introspection import dbus
29from autopilot.exceptions import StateNotFoundError
29from push_notifications import config as push_config30from push_notifications import config as push_config
30import push_notifications.helpers.push_notifications_helper as push_helper31import push_notifications.helpers.push_notifications_helper as push_helper
31from testtools.matchers import Equals32from testtools.matchers import Equals
@@ -238,6 +239,22 @@
238 """239 """
239 self.validate_and_dismiss_notification_dialog(message, secondary_icon=False)240 self.validate_and_dismiss_notification_dialog(message, secondary_icon=False)
240241
242 def validate_and_tap_notification_dialog(self, message,
243 secondary_icon=True):
244 """
245 Validate a notification dialog is displayed and dismiss it
246 :param message: expected message displayed in summary
247 """
248 # get the dialog
249 dialog, props = self.get_notification_dialog()
250 # validate dialog
251 self.assert_notification_dialog(
252 props, summary=message, secondary_icon=secondary_icon)
253 # tap the dialog
254 self.touch.tap_object(dialog)
255 # check the dialog is no longer displayed
256 self.validate_notification_not_displayed(wait=False)
257
241 def wait_until_dialog_dismissed(self, dialog):258 def wait_until_dialog_dismissed(self, dialog):
242 """Wait for the dialog to dismiss automatically"""259 """Wait for the dialog to dismiss automatically"""
243 dialog_disappeared = False260 dialog_disappeared = False
@@ -270,7 +287,7 @@
270 self.wait_until_dialog_dismissed(dialog)287 self.wait_until_dialog_dismissed(dialog)
271288
272 # unicast messages289 # unicast messages
273 def send_unicast_notification(self, icon="messages-app",290 def send_unicast_notification(self, icon="",
274 body="A unicast message", summary="Look!",291 body="A unicast message", summary="Look!",
275 persist=False, popup=True, actions=[], emblem_counter={}):292 persist=False, popup=True, actions=[], emblem_counter={}):
276 """Build and send a push unicast message.293 """Build and send a push unicast message.
@@ -309,3 +326,36 @@
309 indicator_page = self.main_window.open_indicator_page(326 indicator_page = self.main_window.open_indicator_page(
310 "indicator-messages")327 "indicator-messages")
311 return indicator_page328 return indicator_page
329
330 def validate_mmu_notification(self, body_text, title_text):
331 # get the mmu notification and check the body and title.
332 # swipe down and show the incomming page
333 messaging = self.get_messaging_menu()
334 # get the notification and check the body and title.
335 menuItem0 = messaging.select_single('QQuickLoader',
336 objectName='menuItem0')
337 hmh = menuItem0.select_single('HeroMessageHeader')
338 body = hmh.select_single("Label", objectName='body')
339 self.assertEqual(body.text, body_text)
340 title = hmh.select_single("Label", objectName='title')
341 self.assertEqual(title.text, title_text)
342 self.clear_mmu(ignore_missing=False)
343
344 def clear_mmu(self, ignore_missing=True):
345 # get the mmu notification and check the body and title.
346 messaging = self.get_messaging_menu()
347 # clear all notifications
348 try:
349 clear_all = messaging.select_single(
350 'ButtonMenu', objectName='indicator.remove-all')
351 except StateNotFoundError:
352 if not ignore_missing:
353 raise
354 return
355 emptyLabel = messaging.select_single('Label',
356 objectName='emptyLabel')
357 self.assertFalse(emptyLabel.visible)
358 self.touch.tap_object(clear_all)
359 emptyLabel = messaging.select_single('Label',
360 objectName='emptyLabel')
361 self.assertTrue(emptyLabel.visible)
312362
=== modified file 'tests/autopilot/push_notifications/tests/test_broadcast_notifications.py'
--- tests/autopilot/push_notifications/tests/test_broadcast_notifications.py 2014-07-09 18:37:25 +0000
+++ tests/autopilot/push_notifications/tests/test_broadcast_notifications.py 2014-08-11 21:30:50 +0000
@@ -21,15 +21,19 @@
2121
22import time22import time
2323
24from autopilot import platform
25from testtools import skipIf
26
24from push_notifications.tests import PushNotificationTestBase27from push_notifications.tests import PushNotificationTestBase
2528
2629
30DEFAULT_DISPLAY_MESSAGE = "There's an updated system image."
31MMU_BODY = "Tap to open the system updater."
32MMU_TITLE = DEFAULT_DISPLAY_MESSAGE
33
34
27class TestPushClientBroadcast(PushNotificationTestBase):35class TestPushClientBroadcast(PushNotificationTestBase):
28 """36 """Test cases for broadcast push notifications"""
29 Test cases for broadcast push notifications
30 """
31
32 DEFAULT_DISPLAY_MESSAGE = 'There\'s an updated system image.'
3337
34 def test_broadcast_push_notification_screen_off(self):38 def test_broadcast_push_notification_screen_off(self):
35 """39 """
@@ -45,8 +49,12 @@
45 time.sleep(2)49 time.sleep(2)
46 # Turn display on50 # Turn display on
47 self.press_power_button()51 self.press_power_button()
48 self.validate_and_dismiss_broadcast_notification_dialog(52 # Assumes greeter starts in locked state
49 self.DEFAULT_DISPLAY_MESSAGE)53 self.unlock_greeter()
54 # check the bubble is there
55 self.validate_and_dismiss_notification_dialog(DEFAULT_DISPLAY_MESSAGE)
56 # clear the mmu
57 self.clear_mmu()
5058
51 def test_broadcast_push_notification_locked_greeter(self):59 def test_broadcast_push_notification_locked_greeter(self):
52 """60 """
@@ -54,10 +62,13 @@
54 to the client and validate that a notification message is displayed62 to the client and validate that a notification message is displayed
55 whist the greeter screen is displayed63 whist the greeter screen is displayed
56 """64 """
65 self.send_push_broadcast_message()
66 # check the bubble is there
67 self.validate_and_dismiss_notification_dialog(DEFAULT_DISPLAY_MESSAGE)
57 # Assumes greeter starts in locked state68 # Assumes greeter starts in locked state
58 self.send_push_broadcast_message()69 self.unlock_greeter()
59 self.validate_and_dismiss_broadcast_notification_dialog(70 # clear the mmu
60 self.DEFAULT_DISPLAY_MESSAGE)71 self.clear_mmu()
6172
62 def test_broadcast_push_notification(self):73 def test_broadcast_push_notification(self):
63 """74 """
@@ -67,8 +78,42 @@
67 # Assumes greeter starts in locked state78 # Assumes greeter starts in locked state
68 self.unlock_greeter()79 self.unlock_greeter()
69 self.send_push_broadcast_message()80 self.send_push_broadcast_message()
70 self.validate_and_dismiss_broadcast_notification_dialog(81 # check the bubble is there
71 self.DEFAULT_DISPLAY_MESSAGE)82 self.validate_and_dismiss_notification_dialog(DEFAULT_DISPLAY_MESSAGE)
83 # clear the mmu
84 self.clear_mmu()
85
86 @skipIf(platform.model() == 'X86 Emulator',
87 "Test not working in the emulator")
88 def test_broadcast_push_notification_is_persistent(self):
89 """
90 Positive test case to send a valid broadcast push notification
91 to the client and validate that a notification message is displayed
92 and includes a persitent mmu notification.
93 """
94 # Assumes greeter starts in locked state
95 self.unlock_greeter()
96 self.send_push_broadcast_message()
97 # check the bubble is there
98 self.validate_and_dismiss_notification_dialog(DEFAULT_DISPLAY_MESSAGE)
99 # check the mmu notification is there
100 self.validate_mmu_notification(MMU_BODY, MMU_TITLE)
101
102 def test_broadcast_push_notification_click_bubble_clears_mmu(self):
103 """
104 Positive test case to send a valid broadcast push notification
105 to the client and validate that a notification message is displayed
106 """
107 # Assumes greeter starts in locked state
108 self.unlock_greeter()
109 self.send_push_broadcast_message()
110 # check the bubble is there
111 self.validate_and_tap_notification_dialog(DEFAULT_DISPLAY_MESSAGE)
112 # swipe down and show the incomming page
113 messaging = self.get_messaging_menu()
114 emptyLabel = messaging.select_single('Label',
115 objectName='emptyLabel')
116 self.assertTrue(emptyLabel.visible)
72117
73 def test_broadcast_push_notification_on_connect(self):118 def test_broadcast_push_notification_on_connect(self):
74 """119 """
@@ -81,8 +126,10 @@
81 self.push_client_controller.stop_push_client()126 self.push_client_controller.stop_push_client()
82 self.send_push_broadcast_message()127 self.send_push_broadcast_message()
83 self.push_client_controller.start_push_client()128 self.push_client_controller.start_push_client()
84 self.validate_and_dismiss_broadcast_notification_dialog(129 # check the bubble is there
85 self.DEFAULT_DISPLAY_MESSAGE)130 self.validate_and_dismiss_notification_dialog(DEFAULT_DISPLAY_MESSAGE)
131 # clear the mmu
132 self.clear_mmu()
86133
87 def test_expired_broadcast_push_notification(self):134 def test_expired_broadcast_push_notification(self):
88 """135 """
89136
=== modified file 'tests/autopilot/push_notifications/tests/test_unicast_notifications.py'
--- tests/autopilot/push_notifications/tests/test_unicast_notifications.py 2014-07-10 01:21:45 +0000
+++ tests/autopilot/push_notifications/tests/test_unicast_notifications.py 2014-08-11 21:30:50 +0000
@@ -21,6 +21,9 @@
2121
22import os22import os
2323
24from autopilot import platform
25from testtools import skipIf
26
24from push_notifications.tests import PushNotificationTestBase27from push_notifications.tests import PushNotificationTestBase
2528
2629
@@ -30,21 +33,24 @@
30 DEFAULT_DISPLAY_MESSAGE = 'Look!'33 DEFAULT_DISPLAY_MESSAGE = 'Look!'
3134
32 scenarios = [('click_app_with_version',35 scenarios = [('click_app_with_version',
33 dict(app_name="com.ubuntu.calculator_calculator",36 dict(app_name="com.ubuntu.developer.webapps.webapp-twitter_webapp-twitter",
34 appid=None, path=None,37 appid=None, path=None,
35 desktop_dir="~/.local/share/applications/",38 desktop_dir="~/.local/share/applications/",
36 launcher_idx=4)),39 icon="twitter",
40 launcher_idx=5)),
37 ('click_app',41 ('click_app',
38 dict(app_name="com.ubuntu.calculator_calculator",42 dict(app_name="com.ubuntu.developer.webapps.webapp-twitter_webapp-twitter",
39 appid="com.ubuntu.calculator_calculator",43 appid="com.ubuntu.developer.webapps.webapp-twitter_webapp-twitter",
40 path="com_2eubuntu_2ecalculator",44 path="com_2eubuntu_2edeveloper_2ewebapps_2ewebapp_2dtwitter",
41 desktop_dir="~/.local/share/applications/",45 desktop_dir="~/.local/share/applications/",
42 launcher_idx=4)),46 icon="twitter",
47 launcher_idx=5)),
43 ('legacy_app',48 ('legacy_app',
44 dict(app_name="messaging-app",49 dict(app_name="messaging-app",
45 appid="_messaging-app",50 appid="_messaging-app",
46 path="_",51 path="_",
47 desktop_dir="/usr/share/applications/",52 desktop_dir="/usr/share/applications/",
53 icon="messages-app",
48 launcher_idx=1))]54 launcher_idx=1))]
4955
50 def setUp(self):56 def setUp(self):
@@ -63,10 +69,13 @@
63 fname.endswith(".desktop"):69 fname.endswith(".desktop"):
64 # remove .desktop extension, only need the name.70 # remove .desktop extension, only need the name.
65 appid = os.path.splitext(fname)[0]71 appid = os.path.splitext(fname)[0]
66 path = appid.split("_")[0].replace(".", "_2e")72 path = appid.split("_")[0]
73 path = path.replace(".", "_2e").replace("-", "_2d")
67 return appid, path74 return appid, path
68 return self.appid, self.path75 return self.appid, self.path
6976
77 @skipIf(platform.model() == 'X86 Emulator',
78 "Test not working in the emulator")
70 def test_unicast_push_notification_persistent(self):79 def test_unicast_push_notification_persistent(self):
71 """Send a persistent unicast push notification.80 """Send a persistent unicast push notification.
7281
@@ -76,17 +85,9 @@
76 # Assumes greeter starts in locked state85 # Assumes greeter starts in locked state
77 self.unlock_greeter()86 self.unlock_greeter()
78 # send message87 # send message
79 self.send_unicast_notification(persist=True)88 self.send_unicast_notification(persist=True, popup=False,
80 # swipe down and show the incomming page89 icon=self.icon)
81 messaging = self.get_messaging_menu()90 self.validate_mmu_notification("A unicast message", "Look!")
82 # get the notification and check the body and title.
83 menuItem0 = messaging.select_single('QQuickLoader',
84 objectName='menuItem0')
85 hmh = menuItem0.select_single('HeroMessageHeader')
86 body = hmh.select_single("Label", objectName='body')
87 self.assertEqual(body.text, 'A unicast message')
88 title = hmh.select_single("Label", objectName='title')
89 self.assertEqual(title.text, 'Look!')
9091
91 def get_running_app_launcher_icon(self):92 def get_running_app_launcher_icon(self):
92 launcher = self.main_window.get_launcher()93 launcher = self.main_window.get_launcher()
@@ -118,6 +119,7 @@
118 # send message, only showing emblem counter119 # send message, only showing emblem counter
119 emblem_counter = {'count': 42, 'visible': True}120 emblem_counter = {'count': 42, 'visible': True}
120 self.send_unicast_notification(persist=False, popup=False,121 self.send_unicast_notification(persist=False, popup=False,
122 icon=self.icon,
121 emblem_counter=emblem_counter)123 emblem_counter=emblem_counter)
122 # show the dash and check the emblem count.124 # show the dash and check the emblem count.
123 self.main_window.show_dash_swiping()125 self.main_window.show_dash_swiping()
@@ -131,14 +133,15 @@
131 The notification should be displayed on top of the greeter.133 The notification should be displayed on top of the greeter.
132 """134 """
133 # Assumes greeter starts in locked state135 # Assumes greeter starts in locked state
134 self.send_unicast_notification(summary="Locked greeter")136 self.send_unicast_notification(summary="Locked greeter",
137 icon=self.icon)
135 self.validate_and_dismiss_notification_dialog("Locked greeter")138 self.validate_and_dismiss_notification_dialog("Locked greeter")
136139
137 def test_unicast_push_notification(self):140 def test_unicast_push_notification(self):
138 """Send a push notificationn and validate it's displayed."""141 """Send a push notificationn and validate it's displayed."""
139 # Assumes greeter starts in locked state142 # Assumes greeter starts in locked state
140 self.unlock_greeter()143 self.unlock_greeter()
141 self.send_unicast_notification()144 self.send_unicast_notification(icon=self.icon)
142 self.validate_and_dismiss_notification_dialog(145 self.validate_and_dismiss_notification_dialog(
143 self.DEFAULT_DISPLAY_MESSAGE)146 self.DEFAULT_DISPLAY_MESSAGE)
144147
@@ -151,7 +154,7 @@
151 # Assumes greeter starts in locked state154 # Assumes greeter starts in locked state
152 self.unlock_greeter()155 self.unlock_greeter()
153 self.push_client_controller.stop_push_client()156 self.push_client_controller.stop_push_client()
154 self.send_unicast_notification()157 self.send_unicast_notification(icon=self.icon)
155 self.push_client_controller.start_push_client()158 self.push_client_controller.start_push_client()
156 self.validate_and_dismiss_notification_dialog(159 self.validate_and_dismiss_notification_dialog(
157 self.DEFAULT_DISPLAY_MESSAGE)160 self.DEFAULT_DISPLAY_MESSAGE)
158161
=== modified file 'tests/autopilot/run.sh'
--- tests/autopilot/run.sh 2014-07-10 13:25:57 +0000
+++ tests/autopilot/run.sh 2014-08-11 21:30:50 +0000
@@ -8,15 +8,17 @@
8usage: $0 options8usage: $0 options
99
10OPTIONS:10OPTIONS:
11 -t fqn of the tests to run (module, class or method)
11 -d adb device_id 12 -d adb device_id
12 -v run tests in verbose mode13 -v run tests in verbose mode
13EOF14EOF
14}15}
1516
16while getopts "d:v" opt; do17while getopts "t:d:v" opt; do
17 case $opt in18 case $opt in
18 d) DEVICE_ID=$OPTARG;;19 d) DEVICE_ID=$OPTARG;;
19 v) VERBOSITY="-v";;20 v) VERBOSITY="-v";;
21 t) TESTS=$OPTARG;;
20 *) usage22 *) usage
21 exit 123 exit 1
22 ;;24 ;;
@@ -24,7 +26,9 @@
24done26done
2527
2628
29VERBOSITY=${VERBOSITY:-""}
30TESTS=${TESTS:-"push_notifications"}
27DEVICE_ID=${DEVICE_ID:-"emulator-5554"}31DEVICE_ID=${DEVICE_ID:-"emulator-5554"}
28BASE_DIR="ubuntu-push/src/launchpad.net"32BASE_DIR="ubuntu-push/src/launchpad.net"
29BRANCH_DIR="$BASE_DIR/ubuntu-push"33BRANCH_DIR="$BASE_DIR/ubuntu-push"
30adb -s ${DEVICE_ID} shell "su - phablet bash -c 'cd ${BRANCH_DIR}/tests/autopilot/; /sbin/initctl stop unity8; autopilot3 run ${VERBOSITY} push_notifications'"34adb -s ${DEVICE_ID} shell "su - phablet bash -c 'cd ${BRANCH_DIR}/tests/autopilot/; /sbin/initctl stop unity8; autopilot3 run ${VERBOSITY} ${TESTS}'"
3135
=== modified file 'tests/autopilot/setup.sh'
--- tests/autopilot/setup.sh 2014-07-10 20:54:12 +0000
+++ tests/autopilot/setup.sh 2014-08-11 21:30:50 +0000
@@ -15,11 +15,11 @@
15EOF15EOF
16}16}
1717
18while getopts "H:b:c:u" opt; do18while getopts "H:b:d:u" opt; do
19 case $opt in19 case $opt in
20 H) PUSH_SERVER=$OPTARG;;20 H) PUSH_SERVER=$OPTARG;;
21 b) BRANCH_URL=$OPTARG;;21 b) BRANCH_URL=$OPTARG;;
22 c) DEVICE_ID=$OPTARG;; 22 d) DEVICE_ID=$OPTARG;;
23 u) APT_UPDATE="1";;23 u) APT_UPDATE="1";;
24 *) usage24 *) usage
25 exit 125 exit 1
@@ -54,18 +54,28 @@
54 adb -s ${DEVICE_ID} shell "touch autopilot-deps.ok"54 adb -s ${DEVICE_ID} shell "touch autopilot-deps.ok"
55fi55fi
56# fetch the code56# fetch the code
57BASE_DIR="ubuntu-push/src/launchpad.net"57BASE_DIR="/home/phablet/ubuntu-push/src/launchpad.net"
58BRANCH_DIR="$BASE_DIR/ubuntu-push"58BRANCH_DIR="$BASE_DIR/ubuntu-push"
59BRANCH_OK=$(adb -s ${DEVICE_ID} shell "su - phablet bash -c '[ ! -d "${BRANCH_DIR}/tests/autopilot" ] && echo 1 || echo 0'")59BRANCH_OK=$(adb -s ${DEVICE_ID} shell "su - phablet bash -c '[ ! -d "${BRANCH_DIR}/tests/autopilot" ] && echo 1 || echo 0'")
60if [[ "${BRANCH_OK:0:1}" == 1 ]] 60if [[ "${BRANCH_OK:0:1}" == 1 ]] || [[ "${BRANCH_URL}" != "lp:ubuntu-push/automatic" ]]
61then61then
62 adb -s ${DEVICE_ID} shell "su - phablet bash -c 'rm -Rf ${BRANCH_DIR}'"
62 echo "fetching code."63 echo "fetching code."
63 adb -s ${DEVICE_ID} shell "su - phablet bash -c 'mkdir -p ${BASE_DIR}'"64 adb -s ${DEVICE_ID} shell "su - phablet bash -c 'mkdir -p ${BASE_DIR}'"
64 adb -s ${DEVICE_ID} shell "su - phablet bash -c 'bzr branch ${BRANCH_URL} ${BRANCH_DIR}'"65 adb -s ${DEVICE_ID} shell "su - phablet bash -c 'bzr branch ${BRANCH_URL} ${BRANCH_DIR}'"
65fi66fi
66adb -s ${DEVICE_ID} shell "su - phablet bash -c 'sed -i 's/192.168.1.3/${PUSH_SERVER}/' ${BRANCH_DIR}/tests/autopilot/push_notifications/config/push.conf'"67adb -s ${DEVICE_ID} shell "su - phablet bash -c \"sed -i 's/addr =.*/addr = ${PUSH_SERVER}/' ${BRANCH_DIR}/tests/autopilot/push_notifications/config/push.conf\""
68
69# copy the trivial-helper.sh as the heper for the messaging-app (used in the tests)
70HELPER_OK=$(adb -s ${DEVICE_ID} shell "[ ! -f /usr/lib/ubuntu-push-client/legacy-helpers/messaging-app ] && echo 1 || echo 0")
71if [[ "${HELPER_OK:0:1}" == 1 ]]
72then
73 adb -s ${DEVICE_ID} shell "cp ${BRANCH_DIR}/scripts/trivial-helper.sh /usr/lib/ubuntu-push-client/legacy-helpers/messaging-app"
74fi
6775
68# change the local/dev server config, listen in all interfaces76# change the local/dev server config, listen in all interfaces
69sed -i 's/127.0.0.1/0.0.0.0/g' ${ROOT_DIR}/sampleconfigs/dev.json77sed -i 's/127.0.0.1/0.0.0.0/g' ${ROOT_DIR}/sampleconfigs/dev.json
70# and start it78# and start it
71cd ${ROOT_DIR}; make run-server-dev 79cd ${ROOT_DIR}; make run-server-dev
80# remove the trivial helper for the messaging-app
81adb -s ${DEVICE_ID} shell "rm /usr/lib/ubuntu-push-client/legacy-helpers/messaging-app"
7282
=== removed directory 'whoopsie'
=== removed file 'whoopsie/doc.go'
--- whoopsie/doc.go 2014-02-21 16:17:28 +0000
+++ whoopsie/doc.go 1970-01-01 00:00:00 +0000
@@ -1,18 +0,0 @@
1/*
2 Copyright 2013-2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17// Package whoopsie wraps libwhoopsie.
18package whoopsie
190
=== removed directory 'whoopsie/identifier'
=== removed file 'whoopsie/identifier/identifier.go'
--- whoopsie/identifier/identifier.go 2014-07-03 10:51:36 +0000
+++ whoopsie/identifier/identifier.go 1970-01-01 00:00:00 +0000
@@ -1,78 +0,0 @@
1/*
2 Copyright 2013-2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17// Package identifier wraps libwhoopsie, and is thus the
18// source of an anonymous and stable system id used by the Ubuntu
19// error tracker and the Ubuntu push notifications service.
20package identifier
21
22/*
23#cgo pkg-config: libwhoopsie
24#include <glib.h>
25#include <libwhoopsie/identifier.h>
26*/
27import "C"
28import "unsafe"
29import "errors"
30import "time"
31
32// an Id knows how to generate itself, and how to stringify itself.
33type Id interface {
34 Generate() error
35 String() string
36}
37
38// Identifier is the default Id implementation.
39type Identifier struct {
40 value string
41 generator func(**C.char, **C.GError)
42}
43
44func generator(csp **C.char, errp **C.GError) {
45 C.whoopsie_identifier_generate(csp, errp)
46}
47
48// New creates an Identifier, but does not call Generate() on it.
49func New() Id {
50 return &Identifier{generator: generator}
51}
52
53// Generate makes the Identifier create the identifier itself.
54func (id *Identifier) Generate() error {
55 var gerr *C.GError
56 var cs *C.char
57 defer C.g_free((C.gpointer)(unsafe.Pointer(cs)))
58
59 for i := 0; i < 200; i++ {
60 id.generator(&cs, &gerr)
61
62 if gerr == nil && cs != nil {
63 goto Success
64 }
65 C.g_clear_error(&gerr)
66 time.Sleep(600 * time.Millisecond)
67 }
68 return errors.New("whoopsie_identifier_generate still bad after 2m; giving up")
69
70Success:
71 id.value = C.GoString(cs)
72 return nil
73}
74
75// String returns the system identifier as a string.
76func (id *Identifier) String() string {
77 return id.value
78}
790
=== removed file 'whoopsie/identifier/identifier_test.go'
--- whoopsie/identifier/identifier_test.go 2014-05-02 10:42:16 +0000
+++ whoopsie/identifier/identifier_test.go 1970-01-01 00:00:00 +0000
@@ -1,58 +0,0 @@
1/*
2 Copyright 2013-2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17package identifier
18
19import (
20 . "launchpad.net/gocheck"
21 "testing"
22)
23
24// hook up gocheck
25func Test(t *testing.T) { TestingT(t) }
26
27type IdentifierSuite struct{}
28
29var _ = Suite(&IdentifierSuite{})
30
31// TestGenerate checks that Generate() does not fail, and returns a
32// 128-byte string.
33func (s *IdentifierSuite) TestGenerate(c *C) {
34 id := New()
35
36 c.Check(id.Generate(), Equals, nil)
37 c.Check(id.String(), HasLen, 128)
38}
39
40// TestIdentifierInterface checks that Identifier implements Id.
41func (s *IdentifierSuite) TestIdentifierInterface(c *C) {
42 _ = []Id{New()}
43}
44
45// TestFailure checks that Identifier survives whoopsie shenanigans
46func (s *IdentifierSuite) TestIdentifierSurvivesShenanigans(c *C) {
47 count := 0
48 // using _Ctype* as a workaround for gocheck also having a C
49 gen := func(csp **_Ctype_char, errp **_Ctype_GError) {
50 count++
51 if count > 3 {
52 generator(csp, errp)
53 }
54 }
55 id := &Identifier{generator: gen}
56 id.Generate()
57 c.Check(id.String(), HasLen, 128)
58}
590
=== removed directory 'whoopsie/identifier/testing'
=== removed file 'whoopsie/identifier/testing/testing.go'
--- whoopsie/identifier/testing/testing.go 2014-02-21 16:17:28 +0000
+++ whoopsie/identifier/testing/testing.go 1970-01-01 00:00:00 +0000
@@ -1,70 +0,0 @@
1/*
2 Copyright 2013-2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17// Package testing implements a couple of Ids that are useful
18// for testing things that use whoopsie/identifier.
19package testing
20
21import "errors"
22
23// SettableIdentifier is an Id that lets you set the value of the identifier.
24//
25// By default the identifier's value is "<Settable>", so it's visible
26// if you're misusing it.
27type SettableIdentifier struct {
28 value string
29}
30
31// Settable is the constructor for SettableIdentifier.
32func Settable() *SettableIdentifier {
33 return &SettableIdentifier{"<Settable>"}
34}
35
36// Set is the method you use to set the identifier.
37func (sid *SettableIdentifier) Set(value string) {
38 sid.value = value
39}
40
41// Generate does nothing.
42func (sid *SettableIdentifier) Generate() error {
43 return nil
44}
45
46// String returns the string you set.
47func (sid *SettableIdentifier) String() string {
48 return sid.value
49}
50
51// FailingIdentifier is an Id that always fails to generate.
52type FailingIdentifier struct{}
53
54// Failing is the constructor for FailingIdentifier.
55func Failing() *FailingIdentifier {
56 return &FailingIdentifier{}
57}
58
59// Generate fails with an ubiquitous error.
60func (*FailingIdentifier) Generate() error {
61 return errors.New("lp0 on fire")
62}
63
64// String returns "<Failing>".
65//
66// The purpose of this is to make it easy to spot if you're using it
67// by accident.
68func (*FailingIdentifier) String() string {
69 return "<Failing>"
70}
710
=== removed file 'whoopsie/identifier/testing/testing_test.go'
--- whoopsie/identifier/testing/testing_test.go 2014-02-21 16:17:28 +0000
+++ whoopsie/identifier/testing/testing_test.go 1970-01-01 00:00:00 +0000
@@ -1,70 +0,0 @@
1/*
2 Copyright 2013-2014 Canonical Ltd.
3
4 This program is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License version 3, as published
6 by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranties of
10 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17package testing
18
19import (
20 identifier ".."
21 . "launchpad.net/gocheck"
22 "testing"
23)
24
25// hook up gocheck
26func Test(t *testing.T) { TestingT(t) }
27
28type IdentifierSuite struct{}
29
30var _ = Suite(&IdentifierSuite{})
31
32// TestSettableDefaultValueVisible tests that SettableIdentifier's default
33// value is notable.
34func (s *IdentifierSuite) TestSettableDefaultValueVisible(c *C) {
35 id := Settable()
36 c.Check(id.String(), Equals, "<Settable>")
37}
38
39// TestSettableSets tests that SettableIdentifier is settable.
40func (s *IdentifierSuite) TestSettableSets(c *C) {
41 id := Settable()
42 id.Set("hello")
43 c.Check(id.String(), Equals, "hello")
44}
45
46// TestSettableGenerateDoesNotFail tests that SettableIdentifier's Generate
47// does not fail.
48func (s *IdentifierSuite) TestSettableGenerateDoesNotFail(c *C) {
49 id := Settable()
50 c.Check(id.Generate(), Equals, nil)
51}
52
53// TestFailingFails tests that FailingIdentifier fails.
54func (s *IdentifierSuite) TestFailingFails(c *C) {
55 id := Failing()
56 c.Check(id.Generate(), Not(Equals), nil)
57}
58
59// TestFailingStringNotEmpty tests that FailingIdentifier still has a
60// non-empty string.
61func (s *IdentifierSuite) TestFailingStringNotEmpty(c *C) {
62 id := Failing()
63 c.Check(id.String(), Equals, "<Failing>")
64}
65
66// TestIdentifierInterface tests that FailingIdentifier and
67// SettableIdentifier implement Id.
68func (s *IdentifierSuite) TestIdentifierInterface(c *C) {
69 _ = []identifier.Id{Failing(), Settable()}
70}

Subscribers

People subscribed via source and target branches