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: 121
Proposed branch: lp:~ralsina/ubuntu-push/merge-automatic
Merge into: lp:ubuntu-push
Diff against target: 2235 lines (+1306/-207)
28 files modified
bus/notifications/raw.go (+1/-1)
bus/notifications/raw_test.go (+13/-0)
bus/polld/polld.go (+68/-0)
bus/polld/polld_test.go (+86/-0)
bus/powerd/powerd.go (+101/-0)
bus/powerd/powerd_test.go (+161/-0)
click/click.go (+13/-0)
click/click_test.go (+18/-0)
client/client.go (+39/-3)
client/client_test.go (+56/-13)
debian/changelog (+16/-1)
debian/config.json (+6/-1)
dependencies.tsv (+1/-1)
launch_helper/kindpool.go (+1/-1)
launch_helper/kindpool_test.go (+10/-3)
launch_helper/legacy/legacy.go (+36/-18)
launch_helper/legacy/legacy_test.go (+33/-4)
poller/poller.go (+220/-0)
poller/poller_test.go (+114/-0)
scripts/noisy-helper.sh (+7/-0)
server/acceptance/acceptanceclient.go (+15/-16)
server/acceptance/cmd/acceptanceclient.go (+54/-0)
server/acceptance/kit/api.go (+82/-0)
server/acceptance/kit/cliloop.go (+75/-52)
server/acceptance/kit/helpers.go (+46/-0)
server/acceptance/suites/broadcast.go (+14/-27)
server/acceptance/suites/suite.go (+5/-40)
server/acceptance/suites/unicast.go (+15/-26)
To merge this branch: bzr merge lp:~ralsina/ubuntu-push/merge-automatic
Reviewer Review Type Date Requested Status
Ubuntu Push Hackers Pending
Review via email: mp+231942@code.launchpad.net

Description of the change

Merge changes from ubuntu-push automatic branch:

  * Avoid rare race in kindpool_test.go
  * Interface with account-polld's dbus api.
  * Powerd integration.
  * Use symbolic icon for secondary icon in notification.
  * Log legacy helper failures.

To post a comment you must log in.
121. By PS Jenkins bot

* Avoid rare race in kindpool_test.go
* Interface with account-polld's dbus api.
* Powerd integration.
* Use symbolic icon for secondary icon in notification.
* Log legacy helper failures.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bus/notifications/raw.go'
--- bus/notifications/raw.go 2014-07-27 00:58:08 +0000
+++ bus/notifications/raw.go 2014-08-25 16:38:20 +0000
@@ -144,7 +144,7 @@
144 }144 }
145145
146 hints := make(map[string]*dbus.Variant)146 hints := make(map[string]*dbus.Variant)
147 hints["x-canonical-secondary-icon"] = &dbus.Variant{app.Icon()}147 hints["x-canonical-secondary-icon"] = &dbus.Variant{app.SymbolicIcon()}
148148
149 appId := app.Original()149 appId := app.Original()
150 actions := make([]string, 2*len(card.Actions))150 actions := make([]string, 2*len(card.Actions))
151151
=== modified file 'bus/notifications/raw_test.go'
--- bus/notifications/raw_test.go 2014-07-27 00:58:08 +0000
+++ bus/notifications/raw_test.go 2014-08-25 16:38:20 +0000
@@ -210,6 +210,19 @@
210 c.Check(hints["x-canonical-secondary-icon"], NotNil)210 c.Check(hints["x-canonical-secondary-icon"], NotNil)
211}211}
212212
213func (s *RawSuite) TestPresentUsesSymbolic(c *C) {
214 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
215 raw := Raw(endp, s.log)
216 worked := raw.Present(s.app, "notifId", &launch_helper.Notification{Card: &launch_helper.Card{Summary: "summary", Popup: true}})
217 c.Assert(worked, Equals, true)
218 callArgs := testibus.GetCallArgs(endp)
219 c.Assert(callArgs, HasLen, 1)
220 c.Assert(callArgs[0].Args, HasLen, 8)
221 hints, ok := callArgs[0].Args[6].(map[string]*dbus.Variant)
222 c.Assert(ok, Equals, true)
223 c.Check(hints["x-canonical-secondary-icon"].Value.(string), Equals, "-symbolic")
224}
225
213func (s *RawSuite) TestPresentNoNotificationPanics(c *C) {226func (s *RawSuite) TestPresentNoNotificationPanics(c *C) {
214 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))227 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
215 raw := Raw(endp, s.log)228 raw := Raw(endp, s.log)
216229
=== added directory 'bus/polld'
=== added file 'bus/polld/polld.go'
--- bus/polld/polld.go 1970-01-01 00:00:00 +0000
+++ bus/polld/polld.go 2014-08-25 16:38:20 +0000
@@ -0,0 +1,68 @@
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
17// Package polld wraps the account-polld dbus interface
18package polld
19
20import (
21 "errors"
22
23 "launchpad.net/ubuntu-push/bus"
24 "launchpad.net/ubuntu-push/logger"
25)
26
27var (
28 ErrUnconfigured = errors.New("unconfigured.")
29)
30
31// polld lives on a well-known bus.Address
32var BusAddress bus.Address = bus.Address{
33 Interface: "com.ubuntu.AccountPolld",
34 Path: "/com/ubuntu/AccountPolld",
35 Name: "com.ubuntu.AccountPolld",
36}
37
38type Polld interface {
39 Poll() error
40 WatchDones() (<-chan bool, error)
41}
42
43type polld struct {
44 endp bus.Endpoint
45 log logger.Logger
46}
47
48func New(endp bus.Endpoint, log logger.Logger) Polld {
49 return &polld{endp, log}
50}
51
52func (p *polld) Poll() error {
53 if p.endp == nil {
54 return ErrUnconfigured
55 }
56 return p.endp.Call("Poll", nil)
57}
58
59func (p *polld) WatchDones() (<-chan bool, error) {
60 if p.endp == nil {
61 return nil, ErrUnconfigured
62 }
63 ch := make(chan bool)
64 p.endp.WatchSignal("Done", func(...interface{}) {
65 ch <- true
66 }, func() { close(ch) })
67 return ch, nil
68}
069
=== added file 'bus/polld/polld_test.go'
--- bus/polld/polld_test.go 1970-01-01 00:00:00 +0000
+++ bus/polld/polld_test.go 2014-08-25 16:38:20 +0000
@@ -0,0 +1,86 @@
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 polld
18
19import (
20 "testing"
21 "time"
22
23 . "launchpad.net/gocheck"
24
25 testibus "launchpad.net/ubuntu-push/bus/testing"
26 helpers "launchpad.net/ubuntu-push/testing"
27 "launchpad.net/ubuntu-push/testing/condition"
28)
29
30// hook up gocheck
31func TestPolld(t *testing.T) { TestingT(t) }
32
33type PdSuite struct {
34 log *helpers.TestLogger
35}
36
37var _ = Suite(&PdSuite{})
38
39func (s *PdSuite) SetUpTest(c *C) {
40 s.log = helpers.NewTestLogger(c, "debug")
41}
42
43func (s *PdSuite) TestPollWorks(c *C) {
44 endp := testibus.NewTestingEndpoint(nil, condition.Work(true))
45 pd := New(endp, s.log)
46 err := pd.Poll()
47 c.Assert(err, IsNil)
48 args := testibus.GetCallArgs(endp)
49 c.Assert(args, HasLen, 1)
50 c.Check(args[0].Member, Equals, "Poll")
51 c.Check(args[0].Args, IsNil)
52}
53
54func (s *PdSuite) TestPollUnconfigured(c *C) {
55 c.Check(new(polld).Poll(), Equals, ErrUnconfigured)
56}
57
58func (s *PdSuite) TestPollFails(c *C) {
59 endp := testibus.NewTestingEndpoint(nil, condition.Work(false))
60 pd := New(endp, s.log)
61 c.Check(pd.Poll(), NotNil)
62}
63
64func (s *PdSuite) TestWatchDonesWorks(c *C) {
65 endp := testibus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{})
66 pd := New(endp, s.log)
67 ch, err := pd.WatchDones()
68 c.Assert(err, IsNil)
69 select {
70 case b := <-ch:
71 c.Check(b, Equals, true)
72 case <-time.After(100 * time.Millisecond):
73 c.Error("timeout waiting for bool")
74 }
75 select {
76 case b := <-ch:
77 c.Check(b, Equals, false)
78 case <-time.After(100 * time.Millisecond):
79 c.Error("timeout waiting for close")
80 }
81}
82
83func (s *PdSuite) TestWatchDonesUnconfigured(c *C) {
84 _, err := new(polld).WatchDones()
85 c.Check(err, Equals, ErrUnconfigured)
86}
087
=== added directory 'bus/powerd'
=== added file 'bus/powerd/powerd.go'
--- bus/powerd/powerd.go 1970-01-01 00:00:00 +0000
+++ bus/powerd/powerd.go 2014-08-25 16:38:20 +0000
@@ -0,0 +1,101 @@
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
17// Package powerd is an interface to powerd via dbus.
18package powerd
19
20import (
21 "errors"
22 "time"
23
24 "launchpad.net/ubuntu-push/bus"
25 "launchpad.net/ubuntu-push/logger"
26)
27
28// powerd lives on a well-known bus.Address
29var BusAddress bus.Address = bus.Address{
30 Interface: "com.canonical.powerd",
31 Path: "/com/canonical/powerd",
32 Name: "com.canonical.powerd",
33}
34
35// Powerd exposes a subset of powerd
36type Powerd interface {
37 RequestWakeup(name string, wakeupTime time.Time) (string, error)
38 ClearWakeup(cookie string) error
39 WatchWakeups() (<-chan bool, error)
40 RequestWakelock(name string) (string, error)
41 ClearWakelock(cookie string) error
42}
43
44type powerd struct {
45 endp bus.Endpoint
46 log logger.Logger
47}
48
49var (
50 ErrUnconfigured = errors.New("unconfigured.")
51)
52
53// New builds a new Powerd that uses the provided bus.Endpoint
54func New(endp bus.Endpoint, log logger.Logger) Powerd {
55 return &powerd{endp, log}
56}
57
58func (p *powerd) RequestWakeup(name string, wakeupTime time.Time) (string, error) {
59 if p.endp == nil {
60 return "", ErrUnconfigured
61 }
62 var res string
63 err := p.endp.Call("requestWakeup", bus.Args(name, uint64(wakeupTime.Unix())), &res)
64 return res, err
65}
66
67func (p *powerd) ClearWakeup(cookie string) error {
68 if p.endp == nil {
69 return ErrUnconfigured
70 }
71 return p.endp.Call("clearWakeup", bus.Args(cookie))
72}
73
74func (p *powerd) WatchWakeups() (<-chan bool, error) {
75 if p.endp == nil {
76 return nil, ErrUnconfigured
77 }
78 ch := make(chan bool)
79 p.endp.WatchSignal("Wakeup", func(...interface{}) {
80 ch <- true
81 }, func() { close(ch) })
82 return ch, nil
83}
84
85func (p *powerd) RequestWakelock(name string) (string, error) {
86 // wakelocks are documented on https://wiki.ubuntu.com/powerd#API
87 // (requestSysState with state=1)
88 if p.endp == nil {
89 return "", ErrUnconfigured
90 }
91 var res string
92 err := p.endp.Call("requestSysState", bus.Args(name, int32(1)), &res)
93 return res, err
94}
95
96func (p *powerd) ClearWakelock(cookie string) error {
97 if p.endp == nil {
98 return ErrUnconfigured
99 }
100 return p.endp.Call("clearSysState", bus.Args(cookie))
101}
0102
=== added file 'bus/powerd/powerd_test.go'
--- bus/powerd/powerd_test.go 1970-01-01 00:00:00 +0000
+++ bus/powerd/powerd_test.go 2014-08-25 16:38:20 +0000
@@ -0,0 +1,161 @@
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 powerd
18
19import (
20 "testing"
21 "time"
22
23 . "launchpad.net/gocheck"
24
25 testibus "launchpad.net/ubuntu-push/bus/testing"
26 helpers "launchpad.net/ubuntu-push/testing"
27 "launchpad.net/ubuntu-push/testing/condition"
28)
29
30// hook up gocheck
31func TestPowerd(t *testing.T) { TestingT(t) }
32
33type PdSuite struct {
34 log *helpers.TestLogger
35}
36
37var _ = Suite(&PdSuite{})
38
39func (s *PdSuite) SetUpTest(c *C) {
40 s.log = helpers.NewTestLogger(c, "debug")
41}
42
43func (s *PdSuite) TestRequestWakeupWorks(c *C) {
44 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), "cookie")
45 pd := New(endp, s.log)
46 t := time.Now().Add(5 * time.Minute)
47 ck, err := pd.RequestWakeup("name", t)
48 c.Assert(err, IsNil)
49 c.Check(ck, Equals, "cookie")
50 args := testibus.GetCallArgs(endp)
51 c.Assert(args, HasLen, 1)
52 c.Check(args[0].Member, Equals, "requestWakeup")
53 c.Check(args[0].Args, DeepEquals, []interface{}{"name", uint64(t.Unix())})
54}
55
56func (s *PdSuite) TestRequestWakeupUnconfigured(c *C) {
57 _, err := new(powerd).RequestWakeup("name", time.Now())
58 c.Assert(err, Equals, ErrUnconfigured)
59}
60
61func (s *PdSuite) TestRequestWakeupFails(c *C) {
62 endp := testibus.NewTestingEndpoint(nil, condition.Work(false))
63 pd := New(endp, s.log)
64 t := time.Now().Add(5 * time.Minute)
65 _, err := pd.RequestWakeup("name", t)
66 c.Assert(err, NotNil)
67}
68
69func (s *PdSuite) TestClearWakeupWorks(c *C) {
70 endp := testibus.NewTestingEndpoint(nil, condition.Work(true))
71 pd := New(endp, s.log)
72 err := pd.ClearWakeup("cookie")
73 c.Assert(err, IsNil)
74 args := testibus.GetCallArgs(endp)
75 c.Assert(args, HasLen, 1)
76 c.Check(args[0].Member, Equals, "clearWakeup")
77 c.Check(args[0].Args, DeepEquals, []interface{}{"cookie"})
78}
79
80func (s *PdSuite) TestClearWakeupUnconfigured(c *C) {
81 err := new(powerd).ClearWakeup("cookie")
82 c.Assert(err, Equals, ErrUnconfigured)
83}
84
85func (s *PdSuite) TestClearWakeupFails(c *C) {
86 endp := testibus.NewTestingEndpoint(nil, condition.Work(false))
87 pd := New(endp, s.log)
88 err := pd.ClearWakeup("cookie")
89 c.Assert(err, NotNil)
90}
91
92func (s *PdSuite) TestRequestWakelockWorks(c *C) {
93 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), "cookie")
94 pd := New(endp, s.log)
95 ck, err := pd.RequestWakelock("name")
96 c.Assert(err, IsNil)
97 c.Check(ck, Equals, "cookie")
98 args := testibus.GetCallArgs(endp)
99 c.Assert(args, HasLen, 1)
100 // wakelocks are documented on https://wiki.ubuntu.com/powerd#API
101 c.Check(args[0].Member, Equals, "requestSysState")
102 c.Check(args[0].Args, DeepEquals, []interface{}{"name", int32(1)})
103}
104
105func (s *PdSuite) TestRequestWakelockUnconfigured(c *C) {
106 _, err := new(powerd).RequestWakelock("name")
107 c.Assert(err, Equals, ErrUnconfigured)
108}
109
110func (s *PdSuite) TestRequestWakelockFails(c *C) {
111 endp := testibus.NewTestingEndpoint(nil, condition.Work(false))
112 pd := New(endp, s.log)
113 _, err := pd.RequestWakelock("name")
114 c.Assert(err, NotNil)
115}
116
117func (s *PdSuite) TestClearWakelockWorks(c *C) {
118 endp := testibus.NewTestingEndpoint(nil, condition.Work(true))
119 pd := New(endp, s.log)
120 err := pd.ClearWakelock("cookie")
121 c.Assert(err, IsNil)
122 args := testibus.GetCallArgs(endp)
123 c.Assert(args, HasLen, 1)
124 c.Check(args[0].Member, Equals, "clearSysState")
125 c.Check(args[0].Args, DeepEquals, []interface{}{"cookie"})
126}
127
128func (s *PdSuite) TestClearWakelockUnconfigured(c *C) {
129 c.Check(new(powerd).ClearWakelock("cookie"), NotNil)
130}
131
132func (s *PdSuite) TestClearWakelockFails(c *C) {
133 endp := testibus.NewTestingEndpoint(nil, condition.Work(false))
134 pd := New(endp, s.log)
135 err := pd.ClearWakelock("cookie")
136 c.Assert(err, NotNil)
137}
138
139func (s *PdSuite) TestWatchWakeupsWorks(c *C) {
140 endp := testibus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{})
141 pd := New(endp, s.log)
142 ch, err := pd.WatchWakeups()
143 c.Assert(err, IsNil)
144 select {
145 case b := <-ch:
146 c.Check(b, Equals, true)
147 case <-time.After(100 * time.Millisecond):
148 c.Error("timeout waiting for bool")
149 }
150 select {
151 case b := <-ch:
152 c.Check(b, Equals, false)
153 case <-time.After(100 * time.Millisecond):
154 c.Error("timeout waiting for close")
155 }
156}
157
158func (s *PdSuite) TestWatchWakeupsUnconfigured(c *C) {
159 _, err := new(powerd).WatchWakeups()
160 c.Check(err, Equals, ErrUnconfigured)
161}
0162
=== modified file 'click/click.go'
--- click/click.go 2014-08-06 02:44:06 +0000
+++ click/click.go 2014-08-25 16:38:20 +0000
@@ -136,6 +136,19 @@
136 return cappinfo.AppIconFromDesktopId(app.DesktopId())136 return cappinfo.AppIconFromDesktopId(app.DesktopId())
137}137}
138138
139func _symbolic(icon string) string {
140 if strings.ContainsRune(icon, '/') {
141 return icon
142 }
143 return icon + "-symbolic"
144}
145
146var symbolic = _symbolic
147
148func (app *AppId) SymbolicIcon() string {
149 return symbolic(app.Icon())
150}
151
139func (app *AppId) MarshalJSON() ([]byte, error) {152func (app *AppId) MarshalJSON() ([]byte, error) {
140 return json.Marshal(app.Original())153 return json.Marshal(app.Original())
141}154}
142155
=== modified file 'click/click_test.go'
--- click/click_test.go 2014-08-06 03:50:14 +0000
+++ click/click_test.go 2014-08-25 16:38:20 +0000
@@ -182,3 +182,21 @@
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}
185
186func (s *clickSuite) TestSymbolicAppendsSymbolicIfIconIsName(c *C) {
187 symb := symbolic("foo")
188 c.Check(symb, Equals, "foo-symbolic")
189}
190
191func (s *clickSuite) TestSymbolicLeavesAloneIfIconIsPath(c *C) {
192 symb := symbolic("foo/bar")
193 c.Check(symb, Equals, "foo/bar")
194}
195
196func (s *clickSuite) TestSymbolicIconCallsSymbolic(c *C) {
197 symbolic = func(string) string { return "xyzzy" }
198 defer func() { symbolic = _symbolic }()
199 app, err := ParseAppId("_python3.4")
200 c.Assert(err, IsNil)
201 c.Check(app.SymbolicIcon(), Equals, "xyzzy")
202}
185203
=== modified file 'client/client.go'
--- client/client.go 2014-08-12 00:32:32 +0000
+++ client/client.go 2014-08-25 16:38:20 +0000
@@ -44,6 +44,7 @@
44 "launchpad.net/ubuntu-push/identifier"44 "launchpad.net/ubuntu-push/identifier"
45 "launchpad.net/ubuntu-push/launch_helper"45 "launchpad.net/ubuntu-push/launch_helper"
46 "launchpad.net/ubuntu-push/logger"46 "launchpad.net/ubuntu-push/logger"
47 "launchpad.net/ubuntu-push/poller"
47 "launchpad.net/ubuntu-push/protocol"48 "launchpad.net/ubuntu-push/protocol"
48 "launchpad.net/ubuntu-push/util"49 "launchpad.net/ubuntu-push/util"
49)50)
@@ -71,6 +72,12 @@
71 // fallback values for simplified notification usage72 // fallback values for simplified notification usage
72 FallbackVibration *launch_helper.Vibration `json:"fallback_vibration"`73 FallbackVibration *launch_helper.Vibration `json:"fallback_vibration"`
73 FallbackSound string `json:"fallback_sound"`74 FallbackSound string `json:"fallback_sound"`
75 // times for the poller
76 PollInterval config.ConfigTimeDuration `json:"poll_interval"`
77 PollSettle config.ConfigTimeDuration `json:"poll_settle"`
78 PollNetworkWait config.ConfigTimeDuration `json:"poll_net_wait"`
79 PollPolldWait config.ConfigTimeDuration `json:"poll_polld_wait"`
80 PollDoneWait config.ConfigTimeDuration `json:"poll_done_wait"`
74}81}
7582
76// PushService is the interface we use of service.PushService.83// PushService is the interface we use of service.PushService.
@@ -115,6 +122,7 @@
115 unregisterCh chan *click.AppId122 unregisterCh chan *click.AppId
116 trackAddressees map[string]*click.AppId123 trackAddressees map[string]*click.AppId
117 installedChecker click.InstalledChecker124 installedChecker click.InstalledChecker
125 poller poller.Poller
118}126}
119127
120// Creates a new Ubuntu Push Notifications client-side daemon that will use128// Creates a new Ubuntu Push Notifications client-side daemon that will use
@@ -221,6 +229,21 @@
221 }229 }
222}230}
223231
232// derivePollerSetup derives the Poller setup from the client configuration bits.
233func (client *PushClient) derivePollerSetup() *poller.PollerSetup {
234 return &poller.PollerSetup{
235 Times: poller.Times{
236 AlarmInterval: client.config.PollInterval.TimeDuration(),
237 SessionStateSettle: client.config.PollSettle.TimeDuration(),
238 NetworkWait: client.config.PollNetworkWait.TimeDuration(),
239 PolldWait: client.config.PollPolldWait.TimeDuration(),
240 DoneWait: client.config.PollDoneWait.TimeDuration(),
241 },
242 Log: client.log,
243 SessionStateGetter: client.session,
244 }
245}
246
224// getAuthorization gets the authorization blob to send to the server247// getAuthorization gets the authorization blob to send to the server
225func (client *PushClient) getAuthorization(url string) string {248func (client *PushClient) getAuthorization(url string) string {
226 client.log.Debugf("getting authorization for %s", url)249 client.log.Debugf("getting authorization for %s", url)
@@ -266,8 +289,8 @@
266 return err289 return err
267}290}
268291
269// initSession creates the session object292// initSessionAndPoller creates the session and the poller objects
270func (client *PushClient) initSession() error {293func (client *PushClient) initSessionAndPoller() error {
271 info := map[string]interface{}{294 info := map[string]interface{}{
272 "device": client.systemImageInfo.Device,295 "device": client.systemImageInfo.Device,
273 "channel": client.systemImageInfo.Channel,296 "channel": client.systemImageInfo.Channel,
@@ -280,6 +303,18 @@
280 return err303 return err
281 }304 }
282 client.session = sess305 client.session = sess
306 client.poller = poller.New(client.derivePollerSetup())
307 return nil
308}
309
310// runPoller starts and runs the poller
311func (client *PushClient) runPoller() error {
312 if err := client.poller.Start(); err != nil {
313 return err
314 }
315 if err := client.poller.Run(); err != nil {
316 return err
317 }
283 return nil318 return nil
284}319}
285320
@@ -509,6 +544,7 @@
509 client.startPushService,544 client.startPushService,
510 client.startPostalService,545 client.startPostalService,
511 client.takeTheBus,546 client.takeTheBus,
512 client.initSession,547 client.initSessionAndPoller,
548 client.runPoller,
513 )549 )
514}550}
515551
=== modified file 'client/client_test.go'
--- client/client_test.go 2014-08-12 00:32:32 +0000
+++ client/client_test.go 2014-08-25 16:38:20 +0000
@@ -45,6 +45,7 @@
45 "launchpad.net/ubuntu-push/identifier"45 "launchpad.net/ubuntu-push/identifier"
46 idtesting "launchpad.net/ubuntu-push/identifier/testing"46 idtesting "launchpad.net/ubuntu-push/identifier/testing"
47 "launchpad.net/ubuntu-push/launch_helper"47 "launchpad.net/ubuntu-push/launch_helper"
48 "launchpad.net/ubuntu-push/poller"
48 "launchpad.net/ubuntu-push/protocol"49 "launchpad.net/ubuntu-push/protocol"
49 helpers "launchpad.net/ubuntu-push/testing"50 helpers "launchpad.net/ubuntu-push/testing"
50 "launchpad.net/ubuntu-push/testing/condition"51 "launchpad.net/ubuntu-push/testing/condition"
@@ -143,7 +144,11 @@
143144
144func (cs *clientSuite) SetUpSuite(c *C) {145func (cs *clientSuite) SetUpSuite(c *C) {
145 config.IgnoreParsedFlags = true // because configure() uses <flags>146 config.IgnoreParsedFlags = true // because configure() uses <flags>
146 newIdentifier = func() (identifier.Id, error) { return idtesting.Settable(), nil }147 newIdentifier = func() (identifier.Id, error) {
148 id := idtesting.Settable()
149 id.Set("42") // must be hex of len 32
150 return id, nil
151 }
147 cs.timeouts = util.SwapTimeouts([]time.Duration{0})152 cs.timeouts = util.SwapTimeouts([]time.Duration{0})
148 cs.leveldbPath = ""153 cs.leveldbPath = ""
149}154}
@@ -173,6 +178,11 @@
173 "session_url": "xyzzy://",178 "session_url": "xyzzy://",
174 "registration_url": "reg://",179 "registration_url": "reg://",
175 "log_level": "debug",180 "log_level": "debug",
181 "poll_interval": "5m",
182 "poll_settle": "20ms",
183 "poll_net_wait": "1m",
184 "poll_polld_wait": "3m",
185 "poll_done_wait": "5s",
176 }186 }
177 for k, v := range overrides {187 for k, v := range overrides {
178 cfgMap[k] = v188 cfgMap[k] = v
@@ -502,6 +512,39 @@
502}512}
503513
504/*****************************************************************514/*****************************************************************
515 derivePollerSetup tests
516******************************************************************/
517func (cs *clientSuite) TestDerivePollerSetup(c *C) {
518 cs.writeTestConfig(map[string]interface{}{})
519 cli := NewPushClient(cs.configPath, cs.leveldbPath)
520 cli.session = new(session.ClientSession)
521 err := cli.configure()
522 c.Assert(err, IsNil)
523 expected := &poller.PollerSetup{
524 Times: poller.Times{
525 AlarmInterval: 5 * time.Minute,
526 SessionStateSettle: 20 * time.Millisecond,
527 NetworkWait: time.Minute,
528 PolldWait: 3 * time.Minute,
529 DoneWait: 5 * time.Second,
530 },
531 Log: cli.log,
532 SessionStateGetter: cli.session,
533 }
534 // sanity check that we are looking at all fields
535 vExpected := reflect.ValueOf(expected).Elem()
536 nf := vExpected.NumField()
537 for i := 0; i < nf; i++ {
538 fv := vExpected.Field(i)
539 // field isn't empty/zero
540 c.Assert(fv.Interface(), Not(DeepEquals), reflect.Zero(fv.Type()).Interface(), Commentf("forgot about: %s", vExpected.Type().Field(i).Name))
541 }
542 // finally compare
543 setup := cli.derivePollerSetup()
544 c.Check(setup, DeepEquals, expected)
545}
546
547/*****************************************************************
505 startService tests548 startService tests
506******************************************************************/549******************************************************************/
507550
@@ -653,7 +696,7 @@
653 cli := NewPushClient(cs.configPath, cs.leveldbPath)696 cli := NewPushClient(cs.configPath, cs.leveldbPath)
654 cli.log = cs.log697 cli.log = cs.log
655 cli.systemImageInfo = siInfoRes698 cli.systemImageInfo = siInfoRes
656 c.Assert(cli.initSession(), IsNil)699 c.Assert(cli.initSessionAndPoller(), IsNil)
657 cs.log.ResetCapture()700 cs.log.ResetCapture()
658 cli.hasConnectivity = true701 cli.hasConnectivity = true
659 cli.handleErr(errors.New("bananas"))702 cli.handleErr(errors.New("bananas"))
@@ -686,7 +729,7 @@
686 cli := NewPushClient(cs.configPath, cs.leveldbPath)729 cli := NewPushClient(cs.configPath, cs.leveldbPath)
687 cli.log = cs.log730 cli.log = cs.log
688 cli.systemImageInfo = siInfoRes731 cli.systemImageInfo = siInfoRes
689 c.Assert(cli.initSession(), IsNil)732 c.Assert(cli.initSessionAndPoller(), IsNil)
690733
691 c.Assert(cli.hasConnectivity, Equals, false)734 c.Assert(cli.hasConnectivity, Equals, false)
692 cli.handleConnState(true)735 cli.handleConnState(true)
@@ -958,7 +1001,7 @@
958 cli.systemImageInfo = siInfoRes1001 cli.systemImageInfo = siInfoRes
959 cli.connCh = make(chan bool, 1)1002 cli.connCh = make(chan bool, 1)
960 cli.connCh <- true1003 cli.connCh <- true
961 c.Assert(cli.initSession(), IsNil)1004 c.Assert(cli.initSessionAndPoller(), IsNil)
9621005
963 ch := make(chan bool, 1)1006 ch := make(chan bool, 1)
964 go cli.doLoop(func(bool) { ch <- true }, nopBcast, nopUcast, nopError, nopUnregister)1007 go cli.doLoop(func(bool) { ch <- true }, nopBcast, nopUcast, nopError, nopUnregister)
@@ -969,7 +1012,7 @@
969 cli := NewPushClient(cs.configPath, cs.leveldbPath)1012 cli := NewPushClient(cs.configPath, cs.leveldbPath)
970 cli.log = cs.log1013 cli.log = cs.log
971 cli.systemImageInfo = siInfoRes1014 cli.systemImageInfo = siInfoRes
972 c.Assert(cli.initSession(), IsNil)1015 c.Assert(cli.initSessionAndPoller(), IsNil)
973 cli.session.BroadcastCh = make(chan *session.BroadcastNotification, 1)1016 cli.session.BroadcastCh = make(chan *session.BroadcastNotification, 1)
974 cli.session.BroadcastCh <- &session.BroadcastNotification{}1017 cli.session.BroadcastCh <- &session.BroadcastNotification{}
9751018
@@ -982,7 +1025,7 @@
982 cli := NewPushClient(cs.configPath, cs.leveldbPath)1025 cli := NewPushClient(cs.configPath, cs.leveldbPath)
983 cli.log = cs.log1026 cli.log = cs.log
984 cli.systemImageInfo = siInfoRes1027 cli.systemImageInfo = siInfoRes
985 c.Assert(cli.initSession(), IsNil)1028 c.Assert(cli.initSessionAndPoller(), IsNil)
986 cli.session.NotificationsCh = make(chan session.AddressedNotification, 1)1029 cli.session.NotificationsCh = make(chan session.AddressedNotification, 1)
987 cli.session.NotificationsCh <- session.AddressedNotification{}1030 cli.session.NotificationsCh <- session.AddressedNotification{}
9881031
@@ -995,7 +1038,7 @@
995 cli := NewPushClient(cs.configPath, cs.leveldbPath)1038 cli := NewPushClient(cs.configPath, cs.leveldbPath)
996 cli.log = cs.log1039 cli.log = cs.log
997 cli.systemImageInfo = siInfoRes1040 cli.systemImageInfo = siInfoRes
998 c.Assert(cli.initSession(), IsNil)1041 c.Assert(cli.initSessionAndPoller(), IsNil)
999 cli.session.ErrCh = make(chan error, 1)1042 cli.session.ErrCh = make(chan error, 1)
1000 cli.session.ErrCh <- nil1043 cli.session.ErrCh <- nil
10011044
@@ -1008,7 +1051,7 @@
1008 cli := NewPushClient(cs.configPath, cs.leveldbPath)1051 cli := NewPushClient(cs.configPath, cs.leveldbPath)
1009 cli.log = cs.log1052 cli.log = cs.log
1010 cli.systemImageInfo = siInfoRes1053 cli.systemImageInfo = siInfoRes
1011 c.Assert(cli.initSession(), IsNil)1054 c.Assert(cli.initSessionAndPoller(), IsNil)
1012 cli.unregisterCh = make(chan *click.AppId, 1)1055 cli.unregisterCh = make(chan *click.AppId, 1)
1013 cli.unregisterCh <- app11056 cli.unregisterCh <- app1
10141057
@@ -1060,7 +1103,7 @@
1060 cli.postalService = d1103 cli.postalService = d
1061 c.Assert(cli.startPostalService(), IsNil)1104 c.Assert(cli.startPostalService(), IsNil)
10621105
1063 c.Assert(cli.initSession(), IsNil)1106 c.Assert(cli.initSessionAndPoller(), IsNil)
10641107
1065 cli.session.BroadcastCh = make(chan *session.BroadcastNotification)1108 cli.session.BroadcastCh = make(chan *session.BroadcastNotification)
1066 cli.session.ErrCh = make(chan error)1109 cli.session.ErrCh = make(chan error)
@@ -1141,7 +1184,7 @@
1141 // and now everthing is better! We have a config,1184 // and now everthing is better! We have a config,
1142 c.Check(string(cli.config.Addr), Equals, ":0")1185 c.Check(string(cli.config.Addr), Equals, ":0")
1143 // and a device id,1186 // and a device id,
1144 c.Check(cli.deviceId, HasLen, 32)1187 c.Check(cli.deviceId, HasLen, 40)
1145 // and a session,1188 // and a session,
1146 c.Check(cli.session, NotNil)1189 c.Check(cli.session, NotNil)
1147 // and a bus,1190 // and a bus,
@@ -1161,13 +1204,13 @@
1161 c.Check(err, NotNil)1204 c.Check(err, NotNil)
1162}1205}
11631206
1164func (cs *clientSuite) TestInitSessionErr(c *C) {1207func (cs *clientSuite) TestinitSessionAndPollerErr(c *C) {
1165 cli := NewPushClient(cs.configPath, cs.leveldbPath)1208 cli := NewPushClient(cs.configPath, cs.leveldbPath)
1166 cli.log = cs.log1209 cli.log = cs.log
1167 cli.systemImageInfo = siInfoRes1210 cli.systemImageInfo = siInfoRes
1168 // change the cli.pem value so initSession fails1211 // change the cli.pem value so initSessionAndPoller fails
1169 cli.pem = []byte("foo")1212 cli.pem = []byte("foo")
1170 c.Assert(cli.initSession(), NotNil)1213 c.Assert(cli.initSessionAndPoller(), NotNil)
1171}1214}
11721215
1173/*****************************************************************1216/*****************************************************************
11741217
=== modified file 'debian/changelog'
--- debian/changelog 2014-08-12 02:33:16 +0000
+++ debian/changelog 2014-08-25 16:38:20 +0000
@@ -1,3 +1,18 @@
1ubuntu-push (0.62) UNRELEASED; urgency=medium
2
3 [ Samuele Pedroni ]
4 * Avoid rare race in kindpool_test.go
5
6 [ John R. Lenton]
7 * Interface with account-polld's dbus api.
8 * Powerd integration.
9 * Use symbolic icon for secondary icon in notification.
10
11 [Roberto Alsina]
12 * Log legacy helper failures.
13
14 -- Roberto Alsina <ralsina@yoga> Fri, 22 Aug 2014 16:00:31 -0300
15
1ubuntu-push (0.61+14.10.20140812.4-0ubuntu1) utopic; urgency=medium16ubuntu-push (0.61+14.10.20140812.4-0ubuntu1) utopic; urgency=medium
217
3 [ Guillermo Gonzalez ]18 [ Guillermo Gonzalez ]
@@ -13,7 +28,7 @@
13 * Make messaging menu entries show current time instead of epoch for timestamp of 0.28 * Make messaging menu entries show current time instead of epoch for timestamp of 0.
14 * Tweak the upstart script, start after unity.29 * 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.30 * Correctly report invalid app ids, missing apps, and package/app id mismatches as separate errors over dbus.
16 31
17 [Roberto Alsina]32 [Roberto Alsina]
18 * Check that sound paths don't go up into the tree.33 * Check that sound paths don't go up into the tree.
19 * Initial draft of QML-based doc34 * Initial draft of QML-based doc
2035
=== modified file 'debian/config.json'
--- debian/config.json 2014-08-08 09:03:42 +0000
+++ debian/config.json 2014-08-25 16:38:20 +0000
@@ -14,5 +14,10 @@
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},16 "fallback_vibration": {"pattern": [100, 100], "repeat": 2},
17 "fallback_sound": "sounds/ubuntu/notifications/Slick.ogg"17 "fallback_sound": "sounds/ubuntu/notifications/Slick.ogg",
18 "poll_interval": "5m",
19 "poll_settle": "20ms",
20 "poll_net_wait": "1m",
21 "poll_polld_wait": "3m",
22 "poll_done_wait": "5s"
18}23}
1924
=== modified file 'dependencies.tsv'
--- dependencies.tsv 2014-07-26 07:28:59 +0000
+++ dependencies.tsv 2014-08-25 16:38:20 +0000
@@ -1,5 +1,5 @@
1code.google.com/p/go-uuid hg 7dda39b2e7d5e265014674c5af696ba4186679e9 111code.google.com/p/go-uuid hg 7dda39b2e7d5e265014674c5af696ba4186679e9 11
2code.google.com/p/gosqlite hg 74691fb6f83716190870cde1b658538dd4b18eb0 152code.google.com/p/gosqlite hg 74691fb6f83716190870cde1b658538dd4b18eb0 15
3launchpad.net/go-dbus/v1 bzr james@jamesh.id.au-20140530132806-hpqbkxczif6o1dpz 1273launchpad.net/go-dbus/v1 bzr james@jamesh.id.au-20140801110939-lzfql7fk76dt6ckd 128
4launchpad.net/go-xdg/v0 bzr john.lenton@canonical.com-20140208094800-gubd5md7cro3mtxa 104launchpad.net/go-xdg/v0 bzr john.lenton@canonical.com-20140208094800-gubd5md7cro3mtxa 10
5launchpad.net/gocheck bzr gustavo@niemeyer.net-20140225173054-xu9zlkf9kxhvow02 875launchpad.net/gocheck bzr gustavo@niemeyer.net-20140225173054-xu9zlkf9kxhvow02 87
66
=== modified file 'launch_helper/kindpool.go'
--- launch_helper/kindpool.go 2014-07-29 15:24:01 +0000
+++ launch_helper/kindpool.go 2014-08-25 16:38:20 +0000
@@ -72,7 +72,7 @@
72func DefaultLaunchers(log logger.Logger) map[string]HelperLauncher {72func DefaultLaunchers(log logger.Logger) map[string]HelperLauncher {
73 return map[string]HelperLauncher{73 return map[string]HelperLauncher{
74 "click": cual.New(log),74 "click": cual.New(log),
75 "legacy": legacy.New(),75 "legacy": legacy.New(log),
76 }76 }
77}77}
7878
7979
=== modified file 'launch_helper/kindpool_test.go'
--- launch_helper/kindpool_test.go 2014-08-08 09:03:42 +0000
+++ launch_helper/kindpool_test.go 2014-08-25 16:38:20 +0000
@@ -102,16 +102,22 @@
102 return args102 return args
103}103}
104104
105func (s *poolSuite) SetUpSuite(c *C) {
106 xdgCacheHome = c.MkDir
107}
108
109func (s *poolSuite) TearDownSuite(c *C) {
110 xdgCacheHome = xdg.Cache.Home
111}
112
105func (s *poolSuite) SetUpTest(c *C) {113func (s *poolSuite) SetUpTest(c *C) {
106 s.log = helpers.NewTestLogger(c, "debug")114 s.log = helpers.NewTestLogger(c, "debug")
107 s.fakeLauncher = &fakeHelperLauncher{argCh: make(chan [5]string, 10)}115 s.fakeLauncher = &fakeHelperLauncher{argCh: make(chan [5]string, 10)}
108 s.pool = NewHelperPool(map[string]HelperLauncher{"fake": s.fakeLauncher}, s.log)116 s.pool = NewHelperPool(map[string]HelperLauncher{"fake": s.fakeLauncher}, s.log)
109 xdgCacheHome = c.MkDir
110}117}
111118
112func (s *poolSuite) TearDownTest(c *C) {119func (s *poolSuite) TearDownTest(c *C) {
113 s.pool = nil120 s.pool = nil
114 xdgCacheHome = xdg.Cache.Home
115}121}
116122
117func (s *poolSuite) TestDefaultLaunchers(c *C) {123func (s *poolSuite) TestDefaultLaunchers(c *C) {
@@ -379,8 +385,9 @@
379}385}
380386
381func (s *poolSuite) TestGetTempFilename(c *C) {387func (s *poolSuite) TestGetTempFilename(c *C) {
388 tmpDir := c.MkDir()
382 GetTempDir = func(pkgName string) (string, error) {389 GetTempDir = func(pkgName string) (string, error) {
383 return c.MkDir(), nil390 return tmpDir, nil
384 }391 }
385 // restore it when we are done392 // restore it when we are done
386 defer func() {393 defer func() {
387394
=== modified file 'launch_helper/legacy/legacy.go'
--- launch_helper/legacy/legacy.go 2014-07-18 20:45:21 +0000
+++ launch_helper/legacy/legacy.go 2014-08-25 16:38:20 +0000
@@ -18,20 +18,23 @@
18package legacy18package legacy
1919
20import (20import (
21 "bytes"
21 "os"22 "os"
22 "os/exec"23 "os/exec"
23 "path/filepath"24 "path/filepath"
24 "strconv"25 "strconv"
2526
26 "launchpad.net/ubuntu-push/click"27 "launchpad.net/ubuntu-push/click"
28 "launchpad.net/ubuntu-push/logger"
27)29)
2830
29type legacyHelperLauncher struct {31type legacyHelperLauncher struct {
32 log logger.Logger
30 done func(string)33 done func(string)
31}34}
3235
33func New() *legacyHelperLauncher {36func New(log logger.Logger) *legacyHelperLauncher {
34 return new(legacyHelperLauncher)37 return &legacyHelperLauncher{log: log}
35}38}
3639
37func (lhl *legacyHelperLauncher) InstallObserver(done func(string)) error {40func (lhl *legacyHelperLauncher) InstallObserver(done func(string)) error {
@@ -47,27 +50,42 @@
4750
48func (*legacyHelperLauncher) RemoveObserver() error { return nil }51func (*legacyHelperLauncher) RemoveObserver() error { return nil }
4952
53type msg struct {
54 id string
55 err error
56}
57
50func (lhl *legacyHelperLauncher) Launch(_, progname, f1, f2 string) (string, error) {58func (lhl *legacyHelperLauncher) Launch(_, progname, f1, f2 string) (string, error) {
51 cmd := exec.Command(progname, f1, f2)59 comm := make(chan msg)
52 cmd.Stdin = nil
53 cmd.Stdout = nil
54 cmd.Stderr = nil
5560
56 err := cmd.Start()
57 if err != nil {
58 return "", err
59 }
60 proc := cmd.Process
61 if proc == nil {
62 panic("cmd.Process is nil after successful cmd.Start()??")
63 }
64 id := strconv.FormatInt((int64)(proc.Pid), 36)
65 go func() {61 go func() {
66 proc.Wait()62 cmd := exec.Command(progname, f1, f2)
63 var stdout bytes.Buffer
64 cmd.Stdout = &stdout
65 var stderr bytes.Buffer
66 cmd.Stderr = &stderr
67 err := cmd.Start()
68 if err != nil {
69 comm <- msg{"", err}
70 return
71 }
72 proc := cmd.Process
73 if proc == nil {
74 panic("cmd.Process is nil after successful cmd.Start()??")
75 }
76 id := strconv.FormatInt((int64)(proc.Pid), 36)
77 comm <- msg{id, nil}
78 p_err := cmd.Wait()
79 if p_err != nil {
80 // Helper failed or got killed, log output/errors
81 lhl.log.Errorf("Legacy helper failed: %v", p_err)
82 lhl.log.Errorf("Legacy helper failed. Stdout: %s", stdout)
83 lhl.log.Errorf("Legacy helper failed. Stderr: %s", stderr)
84 }
67 lhl.done(id)85 lhl.done(id)
68 }()86 }()
6987 msg := <-comm
70 return id, nil88 return msg.id, msg.err
71}89}
7290
73func (lhl *legacyHelperLauncher) Stop(_, id string) error {91func (lhl *legacyHelperLauncher) Stop(_, id string) error {
7492
=== modified file 'launch_helper/legacy/legacy_test.go'
--- launch_helper/legacy/legacy_test.go 2014-07-18 20:45:21 +0000
+++ launch_helper/legacy/legacy_test.go 2014-08-25 16:38:20 +0000
@@ -32,7 +32,7 @@
32 select {32 select {
33 case s := <-ch:33 case s := <-ch:
34 return s34 return s
35 case <-time.After(time.Second):35 case <-time.After(5 * time.Second):
36 c.Fatal("timed out waiting for value")36 c.Fatal("timed out waiting for value")
37 return ""37 return ""
38 }38 }
@@ -42,12 +42,14 @@
4242
43type legacySuite struct {43type legacySuite struct {
44 lhl *legacyHelperLauncher44 lhl *legacyHelperLauncher
45 log *helpers.TestLogger
45}46}
4647
47var _ = Suite(&legacySuite{})48var _ = Suite(&legacySuite{})
4849
49func (ls *legacySuite) SetUpTest(c *C) {50func (ls *legacySuite) SetUpTest(c *C) {
50 ls.lhl = New()51 ls.log = helpers.NewTestLogger(c, "info")
52 ls.lhl = New(ls.log)
51}53}
5254
53func (ls *legacySuite) TestInstallObserver(c *C) {55func (ls *legacySuite) TestInstallObserver(c *C) {
@@ -94,12 +96,39 @@
94 c.Assert(err, NotNil)96 c.Assert(err, NotNil)
95}97}
9698
99func (ls *legacySuite) TestHelperFails(c *C) {
100 ch := make(chan string, 1)
101 c.Assert(ls.lhl.InstallObserver(func(id string) { ch <- id }), IsNil)
102
103 _, err := ls.lhl.Launch("", "/bin/false", "", "")
104 c.Assert(err, IsNil)
105
106 takeNext(ch, c)
107 c.Check(ls.log.Captured(), Matches, "(?s).*Legacy helper failed.*")
108}
109
110func (ls *legacySuite) TestHelperFailsLog(c *C) {
111 ch := make(chan string, 1)
112 c.Assert(ls.lhl.InstallObserver(func(id string) { ch <- id }), IsNil)
113
114 exe := helpers.ScriptAbsPath("noisy-helper.sh")
115 _, err := ls.lhl.Launch("", exe, "", "")
116 c.Assert(err, IsNil)
117
118 takeNext(ch, c)
119 c.Check(ls.log.Captured(), Matches, "(?s).*BOOM-1.*")
120 c.Check(ls.log.Captured(), Matches, "(?s).*BANG-1.*")
121 c.Check(ls.log.Captured(), Matches, "(?s).*BOOM-20.*")
122 c.Check(ls.log.Captured(), Matches, "(?s).*BANG-20.*")
123}
124
97func (ls *legacySuite) TestStop(c *C) {125func (ls *legacySuite) TestStop(c *C) {
98 ch := make(chan string, 1)126 ch := make(chan string, 1)
99 c.Assert(ls.lhl.InstallObserver(func(id string) { ch <- id }), IsNil)127 c.Assert(ls.lhl.InstallObserver(func(id string) { ch <- id }), IsNil)
100128
101 exe := helpers.ScriptAbsPath("slow-helper.sh")129 // exe := helpers.ScriptAbsPath("slow-helper.sh")
102 id, err := ls.lhl.Launch("", exe, "", "")130 id, err := ls.lhl.Launch("", "/bin/sleep", "9", "1")
131 c.Assert(err, IsNil)
103132
104 err = ls.lhl.Stop("", "===")133 err = ls.lhl.Stop("", "===")
105 c.Check(err, NotNil) // not a valid id134 c.Check(err, NotNil) // not a valid id
106135
=== added directory 'poller'
=== added file 'poller/poller.go'
--- poller/poller.go 1970-01-01 00:00:00 +0000
+++ poller/poller.go 2014-08-25 16:38:20 +0000
@@ -0,0 +1,220 @@
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
17// Package poller implements Poller, a thing that uses (hw) alarms to
18// wake the device up from deep sleep periodically, check for
19// notifications, and poke polld.
20package poller
21
22import (
23 "errors"
24 "sync"
25 "time"
26
27 "launchpad.net/ubuntu-push/bus"
28 "launchpad.net/ubuntu-push/bus/polld"
29 "launchpad.net/ubuntu-push/bus/powerd"
30 "launchpad.net/ubuntu-push/client/session"
31 "launchpad.net/ubuntu-push/logger"
32 "launchpad.net/ubuntu-push/util"
33)
34
35var (
36 ErrUnconfigured = errors.New("not configured")
37 ErrAlreadyStarted = errors.New("already started")
38 ErrNotStarted = errors.New("not started")
39)
40
41type stater interface {
42 State() session.ClientSessionState
43}
44
45type Times struct {
46 AlarmInterval time.Duration
47 SessionStateSettle time.Duration
48 NetworkWait time.Duration
49 PolldWait time.Duration
50 DoneWait time.Duration
51}
52
53type Poller interface {
54 IsConnected() bool
55 Start() error
56 Run() error
57}
58
59type PollerSetup struct {
60 Times Times
61 Log logger.Logger
62 SessionStateGetter stater
63}
64
65type poller struct {
66 times Times
67 log logger.Logger
68 powerd powerd.Powerd
69 polld polld.Polld
70 cookie string
71 sessionState stater
72}
73
74func New(setup *PollerSetup) Poller {
75 return &poller{
76 times: setup.Times,
77 log: setup.Log,
78 powerd: nil,
79 polld: nil,
80 sessionState: setup.SessionStateGetter,
81 }
82}
83
84func (p *poller) IsConnected() bool {
85 return p.sessionState.State() == session.Running
86}
87
88func (p *poller) Start() error {
89 if p.log == nil {
90 return ErrUnconfigured
91 }
92 if p.powerd != nil || p.polld != nil {
93 return ErrAlreadyStarted
94 }
95 powerdEndp := bus.SystemBus.Endpoint(powerd.BusAddress, p.log)
96 polldEndp := bus.SessionBus.Endpoint(polld.BusAddress, p.log)
97 var wg sync.WaitGroup
98 wg.Add(2)
99 go func() {
100 n := util.NewAutoRedialer(powerdEndp).Redial()
101 p.log.Debugf("powerd dialed on try %d", n)
102 wg.Done()
103 }()
104 go func() {
105 n := util.NewAutoRedialer(polldEndp).Redial()
106 p.log.Debugf("polld dialed in on try %d", n)
107 wg.Done()
108 }()
109 wg.Wait()
110
111 p.powerd = powerd.New(powerdEndp, p.log)
112 p.polld = polld.New(polldEndp, p.log)
113
114 return nil
115}
116
117func (p *poller) Run() error {
118 if p.log == nil {
119 return ErrUnconfigured
120 }
121 if p.powerd == nil || p.polld == nil {
122 return ErrNotStarted
123 }
124 wakeupCh, err := p.powerd.WatchWakeups()
125 if err != nil {
126 return err
127 }
128 doneCh, err := p.polld.WatchDones()
129 if err != nil {
130 return err
131 }
132 go p.run(wakeupCh, doneCh)
133 return nil
134}
135
136func (p *poller) run(wakeupCh <-chan bool, doneCh <-chan bool) {
137 var lockCookie string
138
139 for {
140 lockCookie = p.step(wakeupCh, doneCh, lockCookie)
141 }
142}
143
144func (p *poller) step(wakeupCh <-chan bool, doneCh <-chan bool, lockCookie string) string {
145
146 t := time.Now().Add(p.times.AlarmInterval).Truncate(time.Second)
147 _, err := p.powerd.RequestWakeup("ubuntu push client", t)
148 if err != nil {
149 p.log.Errorf("RequestWakeup got %v", err)
150 return lockCookie
151 }
152 p.log.Debugf("requested wakeup at %s", t)
153 if lockCookie != "" {
154 if err := p.powerd.ClearWakelock(lockCookie); err != nil {
155 p.log.Errorf("ClearWakelock(%#v) got %v", lockCookie, err)
156 } else {
157 p.log.Debugf("cleared wakelock cookie %s.", lockCookie)
158 }
159 lockCookie = ""
160 }
161 for b := range wakeupCh {
162 if !b {
163 panic("WatchWakeups channel produced a false value (??)")
164 }
165 // the channel will produce a true for every
166 // wakeup, not only the one we asked for
167 now := time.Now()
168 p.log.Debugf("got woken up; time is %s", now)
169 if !now.Before(t) {
170 break
171 }
172 }
173 lockCookie, err = p.powerd.RequestWakelock("ubuntu push client")
174 if err != nil {
175 p.log.Errorf("RequestWakelock got %v", err)
176 return lockCookie
177 }
178 p.log.Debugf("got wakelock cookie of %s", lockCookie)
179 time.Sleep(p.times.SessionStateSettle)
180 for i := 0; i < 20; i++ {
181 if p.IsConnected() {
182 break
183 }
184 time.Sleep(p.times.NetworkWait / 20)
185 }
186 if !p.IsConnected() {
187 p.log.Errorf("not connected after %s; giving up", p.times.NetworkWait)
188 } else {
189 p.log.Debugf("poking polld.")
190 // drain the doneCH
191 drain:
192 for {
193 select {
194 case <-doneCh:
195 default:
196 break drain
197 }
198 }
199
200 if err := p.polld.Poll(); err != nil {
201 p.log.Errorf("Poll got %v", err)
202 } else {
203 p.log.Debugf("waiting for polld to signal Done.")
204 select {
205 case b := <-doneCh:
206 if !b {
207 panic("WatchDones channel produced a false value (??)")
208 }
209 p.log.Debugf("polld Done.")
210 case <-time.After(p.times.PolldWait):
211 p.log.Errorf("polld still not done after %s; giving up", p.times.PolldWait)
212 }
213 }
214
215 // XXX check whether something was actually done before waiting
216 time.Sleep(p.times.DoneWait)
217 }
218
219 return lockCookie
220}
0221
=== added file 'poller/poller_test.go'
--- poller/poller_test.go 1970-01-01 00:00:00 +0000
+++ poller/poller_test.go 2014-08-25 16:38:20 +0000
@@ -0,0 +1,114 @@
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*/
16package poller
17
18import (
19 "testing"
20 "time"
21
22 . "launchpad.net/gocheck"
23
24 "launchpad.net/ubuntu-push/client/session"
25 helpers "launchpad.net/ubuntu-push/testing"
26)
27
28// hook up gocheck
29func TestPoller(t *testing.T) { TestingT(t) }
30
31type PrSuite struct {
32 log *helpers.TestLogger
33 myd *myD
34}
35
36var _ = Suite(&PrSuite{})
37
38type myD struct {
39 // in/out for RequestWakeup
40 reqWakeName string
41 reqWakeTime time.Time
42 reqWakeCookie string
43 reqWakeErr error
44 // WatchWakeups
45 watchWakeCh <-chan bool
46 watchWakeErr error
47 // RequestWakelock
48 reqLockName string
49 reqLockCookie string
50 reqLockErr error
51 // ClearWakelock
52 clearLockCookie string
53 clearLockErr error
54 // Poll
55 pollErr error
56 // WatchDones
57 watchDonesCh <-chan bool
58 watchDonesErr error
59 // State
60 stateState session.ClientSessionState
61}
62
63func (m *myD) RequestWakeup(name string, wakeupTime time.Time) (string, error) {
64 m.reqWakeName = name
65 m.reqWakeTime = wakeupTime
66 return m.reqWakeCookie, m.reqWakeErr
67}
68func (m *myD) RequestWakelock(name string) (string, error) {
69 m.reqLockName = name
70 return m.reqLockCookie, m.reqLockErr
71}
72func (m *myD) ClearWakelock(cookie string) error {
73 m.clearLockCookie = cookie
74 return m.clearLockErr
75}
76func (m *myD) ClearWakeup(cookie string) error { panic("clearwakeup called??") }
77func (m *myD) WatchWakeups() (<-chan bool, error) { return m.watchWakeCh, m.watchWakeErr }
78func (m *myD) Poll() error { return m.pollErr }
79func (m *myD) WatchDones() (<-chan bool, error) { return m.watchDonesCh, m.watchDonesErr }
80func (m *myD) State() session.ClientSessionState { return m.stateState }
81
82func (s *PrSuite) SetUpTest(c *C) {
83 s.log = helpers.NewTestLogger(c, "debug")
84 s.myd = &myD{}
85}
86
87func (s *PrSuite) TestStep(c *C) {
88 p := &poller{
89 times: Times{},
90 log: s.log,
91 powerd: s.myd,
92 polld: s.myd,
93 sessionState: s.myd,
94 }
95 s.myd.reqLockCookie = "wakelock cookie"
96 s.myd.stateState = session.Running
97 // we'll get the wakeup right away
98 wakeupCh := make(chan bool, 1)
99 wakeupCh <- true
100 // we won't get the "done" signal in time ;)
101 doneCh := make(chan bool)
102 // and a channel to get the return value from a goroutine
103 ch := make(chan string)
104 // now, run
105 go func() { ch <- p.step(wakeupCh, doneCh, "old cookie") }()
106 select {
107 case s := <-ch:
108 c.Check(s, Equals, "wakelock cookie")
109 case <-time.After(time.Second):
110 c.Fatal("timeout waiting for step")
111 }
112 // check we cleared the old cookie
113 c.Check(s.myd.clearLockCookie, Equals, "old cookie")
114}
0115
=== added file 'scripts/noisy-helper.sh'
--- scripts/noisy-helper.sh 1970-01-01 00:00:00 +0000
+++ scripts/noisy-helper.sh 2014-08-25 16:38:20 +0000
@@ -0,0 +1,7 @@
1#!/bin/sh
2for a in `seq 1 100`
3do
4echo BOOM-$a
5>&2 echo BANG-$a
6done
7exit 1
08
=== modified file 'server/acceptance/acceptanceclient.go'
--- server/acceptance/acceptanceclient.go 2014-06-05 09:32:43 +0000
+++ server/acceptance/acceptanceclient.go 2014-08-25 16:38:20 +0000
@@ -19,9 +19,7 @@
1919
20import (20import (
21 "crypto/tls"21 "crypto/tls"
22 "crypto/x509"
23 "encoding/json"22 "encoding/json"
24 "errors"
25 "fmt"23 "fmt"
26 "net"24 "net"
27 "strings"25 "strings"
@@ -40,35 +38,36 @@
40 ImageChannel string38 ImageChannel string
41 ServerAddr string39 ServerAddr string
42 ExchangeTimeout time.Duration40 ExchangeTimeout time.Duration
43 CertPEMBlock []byte
44 ReportPings bool41 ReportPings bool
45 Levels map[string]int6442 Levels map[string]int64
46 Insecure bool // don't verify certs43 TLSConfig *tls.Config
47 Prefix string // prefix for events44 Prefix string // prefix for events
48 Auth string45 Auth string
49 // connection46 // connection
50 Connection net.Conn47 Connection net.Conn
51}48}
5249
53// Dial connects to a server using the configuration in the ClientSession50// Dial connects to a server using the configuration in the
54// and sets up the connection.51// ClientSession and sets up the connection.
55func (sess *ClientSession) Dial() error {52func (sess *ClientSession) Dial() error {
56 conn, err := net.DialTimeout("tcp", sess.ServerAddr, sess.ExchangeTimeout)53 conn, err := net.DialTimeout("tcp", sess.ServerAddr, sess.ExchangeTimeout)
57 if err != nil {54 if err != nil {
58 return err55 return err
59 }56 }
60 tlsConfig := &tls.Config{}57 sess.TLSWrapAndSet(conn)
61 if sess.CertPEMBlock != nil {58 return nil
62 cp := x509.NewCertPool()59}
63 ok := cp.AppendCertsFromPEM(sess.CertPEMBlock)60
64 if !ok {61// TLSWrapAndSet wraps a socket connection in tls and sets it as
65 return errors.New("dial: could not parse certificate")62// session.Connection. For use instead of Dial().
66 }63func (sess *ClientSession) TLSWrapAndSet(conn net.Conn) {
67 tlsConfig.RootCAs = cp64 var tlsConfig *tls.Config
65 if sess.TLSConfig != nil {
66 tlsConfig = sess.TLSConfig
67 } else {
68 tlsConfig = &tls.Config{}
68 }69 }
69 tlsConfig.InsecureSkipVerify = sess.Insecure
70 sess.Connection = tls.Client(conn, tlsConfig)70 sess.Connection = tls.Client(conn, tlsConfig)
71 return nil
72}71}
7372
74type serverMsg struct {73type serverMsg struct {
7574
=== added file 'server/acceptance/cmd/acceptanceclient.go'
--- server/acceptance/cmd/acceptanceclient.go 1970-01-01 00:00:00 +0000
+++ server/acceptance/cmd/acceptanceclient.go 2014-08-25 16:38:20 +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
17// acceptanceclient command for playing.
18package main
19
20import (
21 "log"
22 "os/exec"
23 "strings"
24
25 "launchpad.net/ubuntu-push/server/acceptance"
26 "launchpad.net/ubuntu-push/server/acceptance/kit"
27)
28
29type configuration struct {
30 kit.Configuration
31 AuthHelper string `json:"auth_helper"`
32 WaitFor string `json:"wait_for"`
33}
34
35func main() {
36 kit.Defaults["auth_helper"] = ""
37 kit.Defaults["wait_for"] = ""
38 cfg := &configuration{}
39 kit.CliLoop(cfg, &cfg.Configuration, func(session *acceptance.ClientSession, cfgDir string) {
40 log.Printf("with: %#v", session)
41 }, func(url string) string {
42 if cfg.AuthHelper == "" {
43 return ""
44 }
45 auth, err := exec.Command(cfg.AuthHelper, url).Output()
46 if err != nil {
47 log.Fatalf("auth helper: %v", err)
48 }
49 return strings.TrimSpace(string(auth))
50 }, func() string {
51 return cfg.WaitFor
52 }, func() {
53 })
54}
055
=== added directory 'server/acceptance/kit'
=== added file 'server/acceptance/kit/api.go'
--- server/acceptance/kit/api.go 1970-01-01 00:00:00 +0000
+++ server/acceptance/kit/api.go 2014-08-25 16:38:20 +0000
@@ -0,0 +1,82 @@
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 kit contains reusable building blocks for acceptance.
18package kit
19
20import (
21 "bytes"
22 "crypto/tls"
23 "encoding/json"
24 "errors"
25 "io/ioutil"
26 "net/http"
27)
28
29// APIClient helps making api requests.
30type APIClient struct {
31 ServerAPIURL string
32 // hook to adjust requests
33 MassageRequest func(req *http.Request, message interface{}) *http.Request
34 // other state
35 httpClient *http.Client
36}
37
38// SetupClient sets up the http client to make requests.
39func (api *APIClient) SetupClient(tlsConfig *tls.Config) {
40 api.httpClient = &http.Client{
41 Transport: &http.Transport{TLSClientConfig: tlsConfig},
42 }
43}
44
45var ErrNOk = errors.New("not ok")
46
47// Post a API request.
48func (api *APIClient) PostRequest(path string, message interface{}) (map[string]interface{}, error) {
49 packedMessage, err := json.Marshal(message)
50 if err != nil {
51 panic(err)
52 }
53 reader := bytes.NewReader(packedMessage)
54
55 url := api.ServerAPIURL + path
56 request, _ := http.NewRequest("POST", url, reader)
57 request.ContentLength = int64(reader.Len())
58 request.Header.Set("Content-Type", "application/json")
59
60 if api.MassageRequest != nil {
61 request = api.MassageRequest(request, message)
62 }
63
64 resp, err := api.httpClient.Do(request)
65 if err != nil {
66 return nil, err
67 }
68 defer resp.Body.Close()
69 body, err := ioutil.ReadAll(resp.Body)
70 if err != nil {
71 return nil, err
72 }
73 var res map[string]interface{}
74 err = json.Unmarshal(body, &res)
75 if err != nil {
76 return nil, err
77 }
78 if ok, _ := res["ok"].(bool); !ok {
79 return res, ErrNOk
80 }
81 return res, nil
82}
083
=== renamed file 'server/acceptance/cmd/acceptanceclient.go' => 'server/acceptance/kit/cliloop.go'
--- server/acceptance/cmd/acceptanceclient.go 2014-07-11 21:24:21 +0000
+++ server/acceptance/kit/cliloop.go 2014-08-25 16:38:20 +0000
@@ -14,15 +14,13 @@
14 with this program. If not, see <http://www.gnu.org/licenses/>.14 with this program. If not, see <http://www.gnu.org/licenses/>.
15*/15*/
1616
17// acceptanceclient command for playing.17package kit
18package main
1918
20import (19import (
21 "flag"20 "flag"
22 "fmt"21 "fmt"
23 "log"22 "log"
24 "os"23 "os"
25 "os/exec"
26 "path/filepath"24 "path/filepath"
27 "regexp"25 "regexp"
28 "strings"26 "strings"
@@ -32,27 +30,58 @@
32 "launchpad.net/ubuntu-push/server/acceptance"30 "launchpad.net/ubuntu-push/server/acceptance"
33)31)
3432
35var (33type Configuration struct {
36 insecureFlag = flag.Bool("insecure", false, "disable checking of server certificate and hostname")
37 reportPingsFlag = flag.Bool("reportPings", true, "report each Ping from the server")
38 deviceModel = flag.String("model", "?", "device image model")
39 imageChannel = flag.String("imageChannel", "?", "image channel")
40)
41
42type configuration struct {
43 // session configuration34 // session configuration
44 ExchangeTimeout config.ConfigTimeDuration `json:"exchange_timeout"`35 ExchangeTimeout config.ConfigTimeDuration `json:"exchange_timeout"`
45 // server connection config36 // server connection config
46 Addr config.ConfigHostPort37 Target string `json:"target"`
47 CertPEMFile string `json:"cert_pem_file"`38 Addr config.ConfigHostPort `json:"addr"`
48 AuthHelper string `json:"auth_helper"`39 CertPEMFile string `json:"cert_pem_file"`
49 RunTimeout config.ConfigTimeDuration `json:"run_timeout"`40 Insecure bool `json:"insecure" help:"disable checking of server certificate and hostname"`
50 WaitFor string `json:"wait_for"`41 Domain string `json:"domain" help:"domain for tls connect"`
51}42 // run timeout
5243 RunTimeout config.ConfigTimeDuration `json:"run_timeout"`
53func main() {44 // flags
45 ReportPings bool `json:"reportPings" help:"report each Ping from the server"`
46 DeviceModel string `json:"model" help:"device image model"`
47 ImageChannel string `json:"imageChannel" help:"image channel"`
48}
49
50func (cfg *Configuration) PickByTarget(what, productionValue, stagingValue string) (value string) {
51 switch cfg.Target {
52 case "production":
53 value = productionValue
54 case "staging":
55 value = stagingValue
56 case "":
57 log.Fatalf("either %s or target must be given", what)
58 default:
59 log.Fatalf("if specified target should be production|staging")
60 }
61 return
62}
63
64// Control.
65var (
66 Name = "acceptanceclient"
67 Defaults = map[string]interface{}{
68 "target": "",
69 "addr": ":0",
70 "exchange_timeout": "5s",
71 "cert_pem_file": "",
72 "insecure": false,
73 "domain": "",
74 "run_timeout": "0s",
75 "reportPings": true,
76 "model": "?",
77 "imageChannel": "?",
78 }
79)
80
81// CliLoop parses command line arguments and runs a client loop.
82func CliLoop(totalCfg interface{}, cfg *Configuration, onSetup func(sess *acceptance.ClientSession, cfgDir string), auth func(string) string, waitFor func() string, onConnect func()) {
54 flag.Usage = func() {83 flag.Usage = func() {
55 fmt.Fprintf(os.Stderr, "Usage: acceptancclient [options] <device id>\n")84 fmt.Fprintf(os.Stderr, "Usage: %s [options] <device id>\n", Name)
56 flag.PrintDefaults()85 flag.PrintDefaults()
57 }86 }
58 missingArg := func(what string) {87 missingArg := func(what string) {
@@ -60,14 +89,7 @@
60 flag.Usage()89 flag.Usage()
61 os.Exit(2)90 os.Exit(2)
62 }91 }
63 cfg := &configuration{}92 err := config.ReadFilesDefaults(totalCfg, Defaults, "<flags>")
64 err := config.ReadFilesDefaults(cfg, map[string]interface{}{
65 "exchange_timeout": "5s",
66 "cert_pem_file": "",
67 "auth_helper": "",
68 "run_timeout": "0s",
69 "wait_for": "",
70 }, "<flags>")
71 if err != nil {93 if err != nil {
72 log.Fatalf("reading config: %v", err)94 log.Fatalf("reading config: %v", err)
73 }95 }
@@ -76,36 +98,34 @@
76 case narg < 1:98 case narg < 1:
77 missingArg("device-id")99 missingArg("device-id")
78 }100 }
101 addr := ""
102 if cfg.Addr == ":0" {
103 addr = cfg.PickByTarget("addr", "push-delivery.ubuntu.com:443",
104 "push-delivery.staging.ubuntu.com:443")
105 } else {
106 addr = cfg.Addr.HostPort()
107 }
79 session := &acceptance.ClientSession{108 session := &acceptance.ClientSession{
80 ExchangeTimeout: cfg.ExchangeTimeout.TimeDuration(),109 ExchangeTimeout: cfg.ExchangeTimeout.TimeDuration(),
81 ServerAddr: cfg.Addr.HostPort(),110 ServerAddr: addr,
82 DeviceId: flag.Arg(0),111 DeviceId: flag.Arg(0),
83 // flags112 // flags
84 Model: *deviceModel,113 Model: cfg.DeviceModel,
85 ImageChannel: *imageChannel,114 ImageChannel: cfg.ImageChannel,
86 ReportPings: *reportPingsFlag,115 ReportPings: cfg.ReportPings,
87 Insecure: *insecureFlag,116 }
88 }117 cfgDir := filepath.Dir(flag.Lookup("cfg@").Value.String())
89 log.Printf("with: %#v", session)118 onSetup(session, cfgDir)
90 if !*insecureFlag && cfg.CertPEMFile != "" {119 session.TLSConfig, err = MakeTLSConfig(cfg.Domain, cfg.Insecure, cfg.CertPEMFile, cfgDir)
91 cfgDir := filepath.Dir(flag.Lookup("cfg@").Value.String())120 if err != nil {
92 log.Printf("cert: %v relToDir: %v", cfg.CertPEMFile, cfgDir)121 log.Fatalf("tls config: %v", err)
93 session.CertPEMBlock, err = config.LoadFile(cfg.CertPEMFile, cfgDir)122 }
94 if err != nil {123 session.Auth = auth("https://push.ubuntu.com/")
95 log.Fatalf("reading CertPEMFile: %v", err)
96 }
97 }
98 if len(cfg.AuthHelper) != 0 {
99 auth, err := exec.Command(cfg.AuthHelper, "https://push.ubuntu.com/").Output()
100 if err != nil {
101 log.Fatalf("auth helper: %v", err)
102 }
103 session.Auth = strings.TrimSpace(string(auth))
104 }
105 var waitForRegexp *regexp.Regexp124 var waitForRegexp *regexp.Regexp
106 if cfg.WaitFor != "" {125 waitForStr := waitFor()
126 if waitForStr != "" {
107 var err error127 var err error
108 waitForRegexp, err = regexp.Compile(cfg.WaitFor)128 waitForRegexp, err = regexp.Compile(waitForStr)
109 if err != nil {129 if err != nil {
110 log.Fatalf("wait_for regexp: %v", err)130 log.Fatalf("wait_for regexp: %v", err)
111 }131 }
@@ -118,6 +138,9 @@
118 go func() {138 go func() {
119 for {139 for {
120 ev := <-events140 ev := <-events
141 if strings.HasPrefix(ev, "connected") {
142 onConnect()
143 }
121 if waitForRegexp != nil && waitForRegexp.MatchString(ev) {144 if waitForRegexp != nil && waitForRegexp.MatchString(ev) {
122 log.Println("<matching-event>:", ev)145 log.Println("<matching-event>:", ev)
123 os.Exit(0)146 os.Exit(0)
124147
=== added file 'server/acceptance/kit/helpers.go'
--- server/acceptance/kit/helpers.go 1970-01-01 00:00:00 +0000
+++ server/acceptance/kit/helpers.go 2014-08-25 16:38:20 +0000
@@ -0,0 +1,46 @@
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 kit
18
19import (
20 "crypto/tls"
21 "crypto/x509"
22 "fmt"
23
24 "launchpad.net/ubuntu-push/config"
25)
26
27// MakeTLSConfig makes a tls.Config, optionally reading a cert from
28// disk, possibly relative to relDir.
29func MakeTLSConfig(domain string, insecure bool, certPEMFile string, relDir string) (*tls.Config, error) {
30 tlsConfig := &tls.Config{}
31 tlsConfig.ServerName = domain
32 tlsConfig.InsecureSkipVerify = insecure
33 if !insecure && certPEMFile != "" {
34 certPEMBlock, err := config.LoadFile(certPEMFile, relDir)
35 if err != nil {
36 return nil, fmt.Errorf("reading cert: %v", err)
37 }
38 cp := x509.NewCertPool()
39 ok := cp.AppendCertsFromPEM(certPEMBlock)
40 if !ok {
41 return nil, fmt.Errorf("could not parse certificate")
42 }
43 tlsConfig.RootCAs = cp
44 }
45 return tlsConfig, nil
46}
047
=== modified file 'server/acceptance/suites/broadcast.go'
--- server/acceptance/suites/broadcast.go 2014-06-25 11:00:15 +0000
+++ server/acceptance/suites/broadcast.go 2014-08-25 16:38:20 +0000
@@ -41,8 +41,7 @@
41 ExpireOn: future,41 ExpireOn: future,
42 Data: json.RawMessage(`{"img1/m1": 42}`),42 Data: json.RawMessage(`{"img1/m1": 42}`),
43 })43 })
44 c.Assert(err, IsNil)44 c.Assert(err, IsNil, Commentf("%v", got))
45 c.Assert(got, Matches, OK)
46 c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":42}]`)45 c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":42}]`)
47 stop()46 stop()
48 c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`)47 c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`)
@@ -56,14 +55,13 @@
56 ExpireOn: future,55 ExpireOn: future,
57 Data: json.RawMessage(`{"img1/m2": 10}`),56 Data: json.RawMessage(`{"img1/m2": 10}`),
58 })57 })
59 c.Assert(err, IsNil)58 c.Assert(err, IsNil, Commentf("%v", got))
60 got, err = s.PostRequest("/broadcast", &api.Broadcast{59 got, err = s.PostRequest("/broadcast", &api.Broadcast{
61 Channel: "system",60 Channel: "system",
62 ExpireOn: future,61 ExpireOn: future,
63 Data: json.RawMessage(`{"img1/m1": 20}`),62 Data: json.RawMessage(`{"img1/m1": 20}`),
64 })63 })
65 c.Assert(err, IsNil)64 c.Assert(err, IsNil, Commentf("%v", got))
66 c.Assert(got, Matches, OK)
67 c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:2 payloads:[{"img1/m1":20}]`)65 c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:2 payloads:[{"img1/m1":20}]`)
68 stop()66 stop()
69 c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`)67 c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`)
@@ -77,8 +75,7 @@
77 ExpireOn: future,75 ExpireOn: future,
78 Data: json.RawMessage(`{"img1/m1": 1}`),76 Data: json.RawMessage(`{"img1/m1": 1}`),
79 })77 })
80 c.Assert(err, IsNil)78 c.Assert(err, IsNil, Commentf("%v", got))
81 c.Assert(got, Matches, OK)
8279
83 events, errCh, stop := s.StartClient(c, "DEVB", nil)80 events, errCh, stop := s.StartClient(c, "DEVB", nil)
84 // gettting pending on connect81 // gettting pending on connect
@@ -97,8 +94,7 @@
97 ExpireOn: future,94 ExpireOn: future,
98 Data: json.RawMessage(fmt.Sprintf(payloadFmt, i)),95 Data: json.RawMessage(fmt.Sprintf(payloadFmt, i)),
99 })96 })
100 c.Assert(err, IsNil)97 c.Assert(err, IsNil, Commentf("%v", got))
101 c.Assert(got, Matches, OK)
102 }98 }
10399
104 events, errCh, stop := s.StartClient(c, "DEVC", nil)100 events, errCh, stop := s.StartClient(c, "DEVC", nil)
@@ -130,8 +126,7 @@
130 ExpireOn: future,126 ExpireOn: future,
131 Data: json.RawMessage(`{"img1/m1": 42}`),127 Data: json.RawMessage(`{"img1/m1": 42}`),
132 })128 })
133 c.Assert(err, IsNil)129 c.Assert(err, IsNil, Commentf("%v", got))
134 c.Assert(got, Matches, OK)
135 c.Check(NextEvent(events1, errCh1), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":42}]`)130 c.Check(NextEvent(events1, errCh1), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":42}]`)
136 c.Check(NextEvent(events2, errCh2), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":42}]`)131 c.Check(NextEvent(events2, errCh2), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":42}]`)
137 stop1()132 stop1()
@@ -149,8 +144,7 @@
149 ExpireOn: future,144 ExpireOn: future,
150 Data: json.RawMessage(`{"img1/m1": 1}`),145 Data: json.RawMessage(`{"img1/m1": 1}`),
151 })146 })
152 c.Assert(err, IsNil)147 c.Assert(err, IsNil, Commentf("%v", got))
153 c.Assert(got, Matches, OK)
154 c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":1}]`)148 c.Check(NextEvent(events, errCh), Equals, `broadcast chan:0 app: topLevel:1 payloads:[{"img1/m1":1}]`)
155 stop()149 stop()
156 c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`)150 c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`)
@@ -161,8 +155,7 @@
161 ExpireOn: future,155 ExpireOn: future,
162 Data: json.RawMessage(`{"img1/m1": 2}`),156 Data: json.RawMessage(`{"img1/m1": 2}`),
163 })157 })
164 c.Assert(err, IsNil)158 c.Assert(err, IsNil, Commentf("%v", got))
165 c.Assert(got, Matches, OK)
166 // reconnect, provide levels, get only later notification159 // reconnect, provide levels, get only later notification
167 events, errCh, stop = s.StartClient(c, "DEVD", map[string]int64{160 events, errCh, stop = s.StartClient(c, "DEVD", map[string]int64{
168 protocol.SystemChannelId: 1,161 protocol.SystemChannelId: 1,
@@ -180,15 +173,13 @@
180 ExpireOn: future,173 ExpireOn: future,
181 Data: json.RawMessage(`{"img1/m1": 1}`),174 Data: json.RawMessage(`{"img1/m1": 1}`),
182 })175 })
183 c.Assert(err, IsNil)176 c.Assert(err, IsNil, Commentf("%v", got))
184 c.Assert(got, Matches, OK)
185 got, err = s.PostRequest("/broadcast", &api.Broadcast{177 got, err = s.PostRequest("/broadcast", &api.Broadcast{
186 Channel: "system",178 Channel: "system",
187 ExpireOn: future,179 ExpireOn: future,
188 Data: json.RawMessage(`{"img1/m1": 2}`),180 Data: json.RawMessage(`{"img1/m1": 2}`),
189 })181 })
190 c.Assert(err, IsNil)182 c.Assert(err, IsNil, Commentf("%v", got))
191 c.Assert(got, Matches, OK)
192183
193 events, errCh, stop := s.StartClient(c, "DEVB", map[string]int64{184 events, errCh, stop := s.StartClient(c, "DEVB", map[string]int64{
194 protocol.SystemChannelId: 10,185 protocol.SystemChannelId: 10,
@@ -219,15 +210,13 @@
219 ExpireOn: future,210 ExpireOn: future,
220 Data: json.RawMessage(`{"img1/m1": 1}`),211 Data: json.RawMessage(`{"img1/m1": 1}`),
221 })212 })
222 c.Assert(err, IsNil)213 c.Assert(err, IsNil, Commentf("%v", got))
223 c.Assert(got, Matches, OK)
224 got, err = s.PostRequest("/broadcast", &api.Broadcast{214 got, err = s.PostRequest("/broadcast", &api.Broadcast{
225 Channel: "system",215 Channel: "system",
226 ExpireOn: future,216 ExpireOn: future,
227 Data: json.RawMessage(`{"img1/m1": 2}`),217 Data: json.RawMessage(`{"img1/m1": 2}`),
228 })218 })
229 c.Assert(err, IsNil)219 c.Assert(err, IsNil, Commentf("%v", got))
230 c.Assert(got, Matches, OK)
231220
232 events, errCh, stop := s.StartClient(c, "DEVB", map[string]int64{221 events, errCh, stop := s.StartClient(c, "DEVB", map[string]int64{
233 protocol.SystemChannelId: -10,222 protocol.SystemChannelId: -10,
@@ -246,15 +235,13 @@
246 ExpireOn: future,235 ExpireOn: future,
247 Data: json.RawMessage(`{"img1/m1": 1}`),236 Data: json.RawMessage(`{"img1/m1": 1}`),
248 })237 })
249 c.Assert(err, IsNil)238 c.Assert(err, IsNil, Commentf("%v", got))
250 c.Assert(got, Matches, OK)
251 got, err = s.PostRequest("/broadcast", &api.Broadcast{239 got, err = s.PostRequest("/broadcast", &api.Broadcast{
252 Channel: "system",240 Channel: "system",
253 ExpireOn: time.Now().Add(1 * time.Second).Format(time.RFC3339),241 ExpireOn: time.Now().Add(1 * time.Second).Format(time.RFC3339),
254 Data: json.RawMessage(`{"img1/m1": 2}`),242 Data: json.RawMessage(`{"img1/m1": 2}`),
255 })243 })
256 c.Assert(err, IsNil)244 c.Assert(err, IsNil, Commentf("%v", got))
257 c.Assert(got, Matches, OK)
258245
259 time.Sleep(2 * time.Second)246 time.Sleep(2 * time.Second)
260 // second broadcast is expired247 // second broadcast is expired
261248
=== modified file 'server/acceptance/suites/suite.go'
--- server/acceptance/suites/suite.go 2014-06-25 11:00:15 +0000
+++ server/acceptance/suites/suite.go 2014-08-25 16:38:20 +0000
@@ -18,13 +18,9 @@
18package suites18package suites
1919
20import (20import (
21 "bytes"
22 "encoding/json"
23 "flag"21 "flag"
24 "fmt"22 "fmt"
25 "io/ioutil"
26 "net"23 "net"
27 "net/http"
28 "os"24 "os"
29 "runtime"25 "runtime"
30 "time"26 "time"
@@ -32,6 +28,7 @@
32 . "launchpad.net/gocheck"28 . "launchpad.net/gocheck"
3329
34 "launchpad.net/ubuntu-push/server/acceptance"30 "launchpad.net/ubuntu-push/server/acceptance"
31 "launchpad.net/ubuntu-push/server/acceptance/kit"
35 helpers "launchpad.net/ubuntu-push/testing"32 helpers "launchpad.net/ubuntu-push/testing"
36)33)
3734
@@ -84,14 +81,10 @@
84 StartServer func(c *C, s *AcceptanceSuite, handle *ServerHandle)81 StartServer func(c *C, s *AcceptanceSuite, handle *ServerHandle)
85 // populated by StartServer82 // populated by StartServer
86 ServerHandle83 ServerHandle
87 ServerAPIURL string84 kit.APIClient // has ServerAPIURL
88 // KillGroup should be populated by StartServer with functions85 // KillGroup should be populated by StartServer with functions
89 // to kill the server process86 // to kill the server process
90 KillGroup map[string]func(os.Signal)87 KillGroup map[string]func(os.Signal)
91 // hook to adjust requests
92 MassageRequest func(req *http.Request, message interface{}) *http.Request
93 // other state
94 httpClient *http.Client
95}88}
9689
97// Start a new server for each test.90// Start a new server for each test.
@@ -101,7 +94,7 @@
101 c.Assert(s.ServerHandle.ServerEvents, NotNil)94 c.Assert(s.ServerHandle.ServerEvents, NotNil)
102 c.Assert(s.ServerHandle.ServerAddr, Not(Equals), "")95 c.Assert(s.ServerHandle.ServerAddr, Not(Equals), "")
103 c.Assert(s.ServerAPIURL, Not(Equals), "")96 c.Assert(s.ServerAPIURL, Not(Equals), "")
104 s.httpClient = &http.Client{}97 s.SetupClient(nil)
105}98}
10699
107func (s *AcceptanceSuite) TearDownTest(c *C) {100func (s *AcceptanceSuite) TearDownTest(c *C) {
@@ -110,45 +103,19 @@
110 }103 }
111}104}
112105
113// Post a API request.
114func (s *AcceptanceSuite) PostRequest(path string, message interface{}) (string, error) {
115 packedMessage, err := json.Marshal(message)
116 if err != nil {
117 panic(err)
118 }
119 reader := bytes.NewReader(packedMessage)
120
121 url := s.ServerAPIURL + path
122 request, _ := http.NewRequest("POST", url, reader)
123 request.ContentLength = int64(reader.Len())
124 request.Header.Set("Content-Type", "application/json")
125
126 if s.MassageRequest != nil {
127 request = s.MassageRequest(request, message)
128 }
129
130 resp, err := s.httpClient.Do(request)
131 if err != nil {
132 panic(err)
133 }
134 defer resp.Body.Close()
135 body, err := ioutil.ReadAll(resp.Body)
136 return string(body), err
137}
138
139func testClientSession(addr string, deviceId, model, imageChannel string, reportPings bool) *acceptance.ClientSession {106func testClientSession(addr string, deviceId, model, imageChannel string, reportPings bool) *acceptance.ClientSession {
140 certPEMBlock, err := ioutil.ReadFile(helpers.SourceRelative("../ssl/testing.cert"))107 tlsConfig, err := kit.MakeTLSConfig("", false, helpers.SourceRelative("../ssl/testing.cert"), "")
141 if err != nil {108 if err != nil {
142 panic(fmt.Sprintf("could not read ssl/testing.cert: %v", err))109 panic(fmt.Sprintf("could not read ssl/testing.cert: %v", err))
143 }110 }
144 return &acceptance.ClientSession{111 return &acceptance.ClientSession{
145 ExchangeTimeout: 100 * time.Millisecond,112 ExchangeTimeout: 100 * time.Millisecond,
146 ServerAddr: addr,113 ServerAddr: addr,
147 CertPEMBlock: certPEMBlock,
148 DeviceId: deviceId,114 DeviceId: deviceId,
149 Model: model,115 Model: model,
150 ImageChannel: imageChannel,116 ImageChannel: imageChannel,
151 ReportPings: reportPings,117 ReportPings: reportPings,
118 TLSConfig: tlsConfig,
152 }119 }
153}120}
154121
@@ -198,5 +165,3 @@
198165
199// Long after the end of the tests.166// Long after the end of the tests.
200var future = time.Now().Add(9 * time.Hour).Format(time.RFC3339)167var future = time.Now().Add(9 * time.Hour).Format(time.RFC3339)
201
202const OK = `.*"ok":true.*`
203168
=== modified file 'server/acceptance/suites/unicast.go'
--- server/acceptance/suites/unicast.go 2014-07-14 15:23:17 +0000
+++ server/acceptance/suites/unicast.go 2014-08-25 16:38:20 +0000
@@ -23,6 +23,7 @@
2323
24 . "launchpad.net/gocheck"24 . "launchpad.net/gocheck"
2525
26 "launchpad.net/ubuntu-push/server/acceptance/kit"
26 "launchpad.net/ubuntu-push/server/api"27 "launchpad.net/ubuntu-push/server/api"
27)28)
2829
@@ -45,20 +46,15 @@
45 DeviceId: "DEV1",46 DeviceId: "DEV1",
46 AppId: "app1",47 AppId: "app1",
47 })48 })
48 c.Assert(err, IsNil)49 c.Assert(err, IsNil, Commentf("%v", res))
49 c.Assert(res, Matches, OK)
50 var reg map[string]interface{}
51 err = json.Unmarshal([]byte(res), &reg)
52 c.Assert(err, IsNil)
53 events, errCh, stop := s.StartClientAuth(c, "DEV1", nil, auth)50 events, errCh, stop := s.StartClientAuth(c, "DEV1", nil, auth)
54 got, err := s.PostRequest("/notify", &api.Unicast{51 got, err := s.PostRequest("/notify", &api.Unicast{
55 Token: reg["token"].(string),52 Token: res["token"].(string),
56 AppId: "app1",53 AppId: "app1",
57 ExpireOn: future,54 ExpireOn: future,
58 Data: json.RawMessage(`{"a": 42}`),55 Data: json.RawMessage(`{"a": 42}`),
59 })56 })
60 c.Assert(err, IsNil)57 c.Assert(err, IsNil, Commentf("%v", got))
61 c.Assert(got, Matches, OK)
62 c.Check(NextEvent(events, errCh), Equals, `unicast app:app1 payload:{"a":42};`)58 c.Check(NextEvent(events, errCh), Equals, `unicast app:app1 payload:{"a":42};`)
63 stop()59 stop()
64 c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`)60 c.Assert(NextEvent(s.ServerEvents, nil), Matches, `.* ended with:.*EOF`)
@@ -80,8 +76,7 @@
80 ExpireOn: future,76 ExpireOn: future,
81 Data: json.RawMessage(`{"to": 1}`),77 Data: json.RawMessage(`{"to": 1}`),
82 })78 })
83 c.Assert(err, IsNil)79 c.Assert(err, IsNil, Commentf("%v", got))
84 c.Assert(got, Matches, OK)
85 got, err = s.PostRequest("/notify", &api.Unicast{80 got, err = s.PostRequest("/notify", &api.Unicast{
86 UserId: userId2,81 UserId: userId2,
87 DeviceId: "DEV2",82 DeviceId: "DEV2",
@@ -89,8 +84,7 @@
89 ExpireOn: future,84 ExpireOn: future,
90 Data: json.RawMessage(`{"to": 2}`),85 Data: json.RawMessage(`{"to": 2}`),
91 })86 })
92 c.Assert(err, IsNil)87 c.Assert(err, IsNil, Commentf("%v", got))
93 c.Assert(got, Matches, OK)
94 c.Check(NextEvent(events1, errCh1), Equals, `unicast app:app1 payload:{"to":1};`)88 c.Check(NextEvent(events1, errCh1), Equals, `unicast app:app1 payload:{"to":1};`)
95 c.Check(NextEvent(events2, errCh2), Equals, `unicast app:app1 payload:{"to":2};`)89 c.Check(NextEvent(events2, errCh2), Equals, `unicast app:app1 payload:{"to":2};`)
96 stop1()90 stop1()
@@ -111,8 +105,7 @@
111 ExpireOn: future,105 ExpireOn: future,
112 Data: json.RawMessage(`{"a": 42}`),106 Data: json.RawMessage(`{"a": 42}`),
113 })107 })
114 c.Assert(err, IsNil)108 c.Assert(err, IsNil, Commentf("%v", got))
115 c.Assert(got, Matches, OK)
116109
117 // get pending on connect110 // get pending on connect
118 events, errCh, stop := s.StartClientAuth(c, "DEV1", nil, auth)111 events, errCh, stop := s.StartClientAuth(c, "DEV1", nil, auth)
@@ -134,8 +127,7 @@
134 ExpireOn: future,127 ExpireOn: future,
135 Data: json.RawMessage(fmt.Sprintf(payloadFmt, i)),128 Data: json.RawMessage(fmt.Sprintf(payloadFmt, i)),
136 })129 })
137 c.Assert(err, IsNil)130 c.Assert(err, IsNil, Commentf("%v", got))
138 c.Assert(got, Matches, OK)
139 }131 }
140132
141 events, errCh, stop := s.StartClientAuth(c, "DEV2", nil, auth)133 events, errCh, stop := s.StartClientAuth(c, "DEV2", nil, auth)
@@ -168,8 +160,7 @@
168 ExpireOn: future,160 ExpireOn: future,
169 Data: json.RawMessage(fmt.Sprintf(payloadFmt, i)),161 Data: json.RawMessage(fmt.Sprintf(payloadFmt, i)),
170 })162 })
171 c.Assert(err, IsNil)163 c.Assert(err, IsNil, Commentf("%v", got))
172 c.Assert(got, Matches, OK)
173 }164 }
174165
175 got, err := s.PostRequest("/notify", &api.Unicast{166 got, err := s.PostRequest("/notify", &api.Unicast{
@@ -179,8 +170,9 @@
179 ExpireOn: future,170 ExpireOn: future,
180 Data: json.RawMessage(fmt.Sprintf(payloadFmt, MaxNotificationsPerApplication)),171 Data: json.RawMessage(fmt.Sprintf(payloadFmt, MaxNotificationsPerApplication)),
181 })172 })
182 c.Assert(err, IsNil)173 c.Assert(err, Equals, kit.ErrNOk, Commentf("%v", got))
183 c.Assert(got, Matches, `.*"error":"too-many-pending".*`)174 errorStr, _ := got["error"].(string)
175 c.Assert(errorStr, Equals, "too-many-pending")
184176
185 // clear all pending177 // clear all pending
186 got, err = s.PostRequest("/notify", &api.Unicast{178 got, err = s.PostRequest("/notify", &api.Unicast{
@@ -191,8 +183,7 @@
191 Data: json.RawMessage(fmt.Sprintf(payloadFmt, 1000)),183 Data: json.RawMessage(fmt.Sprintf(payloadFmt, 1000)),
192 ClearPending: true,184 ClearPending: true,
193 })185 })
194 c.Assert(err, IsNil)186 c.Assert(err, IsNil, Commentf("%v", got))
195 c.Assert(got, Matches, OK)
196187
197 events, errCh, stop := s.StartClientAuth(c, "DEV2", nil, auth)188 events, errCh, stop := s.StartClientAuth(c, "DEV2", nil, auth)
198 // getting the 1 pending on connect189 // getting the 1 pending on connect
@@ -214,8 +205,7 @@
214 Data: json.RawMessage(`{"m": 1}`),205 Data: json.RawMessage(`{"m": 1}`),
215 ReplaceTag: "tagFoo",206 ReplaceTag: "tagFoo",
216 })207 })
217 c.Assert(err, IsNil)208 c.Assert(err, IsNil, Commentf("%v", got))
218 c.Assert(got, Matches, OK)
219209
220 // replace210 // replace
221 got, err = s.PostRequest("/notify", &api.Unicast{211 got, err = s.PostRequest("/notify", &api.Unicast{
@@ -226,8 +216,7 @@
226 Data: json.RawMessage(`{"m": 2}`),216 Data: json.RawMessage(`{"m": 2}`),
227 ReplaceTag: "tagFoo",217 ReplaceTag: "tagFoo",
228 })218 })
229 c.Assert(err, IsNil)219 c.Assert(err, IsNil, Commentf("%v", got))
230 c.Assert(got, Matches, OK)
231220
232 events, errCh, stop := s.StartClientAuth(c, "DEV2", nil, auth)221 events, errCh, stop := s.StartClientAuth(c, "DEV2", nil, auth)
233 // getting the 1 pending on connect222 // getting the 1 pending on connect

Subscribers

People subscribed via source and target branches